介绍
找大佬要来的堆练习题3。
是pwnable.tw中一道堆的题目。
有好多种姿势getshell,实在是长见识了。
分析程序
分析题目流程,比较清晰。raise养花,visit查看,remove移除,clean清除全部。
结构体:
1 | struct node{ |
raise函数,分配一个堆放结构体,flag置1,name为大小由用户指定的堆,color直接读入。最后将结构体插入bss段中的flowers中。
visit函数,打印所有flag位不为0的flower的name和color。
remove函数,选择一个flower的index,将flag置0,free掉name,但指针未清空。并且在检查index的之后,只检查了index的合法性和bss上index对应的堆是否存在,并没有检查flag位,所以可以double free。
1 | if ( index <= 99 && (v0 = flowers[index]) != 0LL ) |
clean函数,基本无关,且无明显漏洞。
程序保护全开:
1 | Arch: amd64-64-little |
利用方式 __malloc_hook
首先,利用Unsotred bin
泄露出libc
的地址,比较简单不再赘述。
然后,因为有Full RELRO
,写got
表肯定不行了。这里的做法是通过hook函数
劫持程序控制流。但不巧的是__free_hook
旁边没有合适的size,所以这里改写__malloc_hook
为one_gadget
。
然后,又来个不巧的,找到的所有one_gadget
,直接调用malloc
都不可用,所以我们选择double free
来调用malloc_printerr
以此来触发__malloc_hook
,经测试,这样是可以的。
onegadget选取以下第三个。
1 | 0x45216 execve("/bin/sh", rsp+0x30, environ) |
脚本
1 | #!/usr/bin/env python2 |
利用方式 _IO_2_1_stdout_
这种方式是,攻击file struct
,在我看来,这题用这个方法还是比较好的。
首先,泄露libc
地址和heap
地址,比较简单,不再赘述。(注:我脚本写的比较麻烦,主要是为了让堆上面好看一点,各各阶段清晰一点)。
然后,利用double free
分配一个在_IO_2_1_stdout_
附近的chunk
,这里选在_IO_2_1_stdout_ + 0x9d
正好大小合适。
1 | pwndbg> x/gx ((void *)&_IO_2_1_stdout_ + 0x9d + 8) |
然后修改_IO_2_1_stdout_
中的vtable,使vtable中的xsputn
指向one_gadget
,我们可以在修改vtable
的时候顺便把one_gadget
放上去,控制好地址就好了。这里是将vtable
指向_IO_2_1_stdout_ + 0x98
此时,xsputn
正好是&vtable - 16
1 | 0x7f89b7d076f0 <_IO_2_1_stdout_+208>: 0x00007f89b7a33147 0x00007f89b7d076b8 |
最后在,执行到printf
的时候就会调用vtable
的xsputn
,从而getshell.
脚本
1 | #!/usr/bin/env python2 |
更多方法
还有好几种方法可以做出这道题,简直是五花八门。可以参考以下链接: