nofile
题目简述
题目开了canary保护,有一个栈溢出的漏洞,有一个printf的格式化字符串是%s,可以泄露地址。另外发现有一个函数vulfunc,可以读文件,题目描述也说了有一个叫flag的文件,但是题目在开始时限制了进程可打开的最大文件描述符的数量为0:
1 | unsigned __int64 init() |
因此在跳转到该文件之前需要将文件描述符的限制去掉。可以再调用一次setrlimit函数修改文件描述符数量。rlimit结构体如下:
1 | struct rlimit { |
因为题目开了canary,还需要泄露canary,跳转到某个函数还需要泄露程序加载基址。
另外因为发现程序没有”pop rdi;retn”的rop,read函数需要三个参数,但是程序本身就有一个输入的函数readstr,只需要两个参数。
总结利用思路如下:
1 | 利用格式化字符串漏洞泄露canary和程序基址。 |
exp
1 | from pwn import * |
babytcache
题目简述
这道题类似于pwnable.tw上的Tcache Tear,不同的是去掉了show函数无法通过正常的输出泄露libc地址,另外开了PIE,在不泄露程序基址的条件下无法在bss段上伪造堆。但是在IDA里可以看到程序在一开始在0xABCDA000处mmap了一段大小为0x2000的空间,并接受0x200的输入。
1 | ssize_t sub_96A() |
另外程序最大申请0x110的chunk,申请的次数没有限制,但是每次释放的chunk均为最后一次申请的chunk,释放的次数最多为8次。libc版本为2.27,有了tcache,所以可以用double free。
利用思路
(1) 伪造chunk:因为有了tcache,所以释放的chunk会先进入tcache中,按照原来Tcache Tear的思路,可以在0xABCDA000地址处伪造chunk,因为泄露libc要将其释放到unsorted bin中,所以要大于tcache中chunk的大小,因此伪造的chunk要至少为0x420,首先尝试了这种方法,但是发现泄露后delete的次数全部用完了,后面就没法再用tcache double free来将free_hook的地址修改为one_gadget。
但是在调试的时候发现在某一次申请chunk时某条tcache链的数量被覆写为255,其实是因为tcache在分配时没有检查数量,只根据操作来加减数字,当tcache的数量为0时,再从tcache里分配一个chunk,tcache的数量就变成了0xff(255)。此时再释放一个相同大小的chunk就不会进入tcache了。那就可以在第一次输入时就在0xABCDA000处伪造chunk并布置好对应位置的size越过检查,这样就省了两次delete。
1 | gdb-peda$ heapinfo |
(2)泄露libc:
没有show函数,可以利用_IO_2_1_stdout_来泄露,之前在hitcon2018的baby_tcache和国赛的bms出现过,具体可参考以下链接:
1 | http://myhackerworld.top/2018/11/20/tcache%E6%9C%BA%E5%88%B6%E7%9A%84%E5%87%A0%E9%81%93pwn%E9%A2%98/ |
总结一下利用思路:
1 | 在0xABCDA000处伪造大小为0x110的chunk,利用double free移植堆空间至该处。 |
exp
1 | #coding=utf-8 |
silent
题目简述
一个栈溢出的题目,没有libc,想到之前做过32位的利用return to dl resolve将某个函数的got表改为system,然后来get shell,但这次是64位,函数参数需要用rdi、rsi、rdx等寄存器来传参,另外重定位表.rel.plt、符号表.dynsym和.synstr字符串表的大小由0x10变成了0x18。除了正常在bss段上构造这些表外,如果伪造的r_info的值较大时,会导致无法读取内存引发错误,而绕过的方法是link_map+0x1c8处的值为0,而在函数传参时link_map就是GOT[1]的地址,程序一开始有一个赋值的操作,已经为我们构造好了这个条件,因此只要构造好rop就可以了。
1 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) |
另外在构造时开始是在bss+0x800处构造的,但调试时会报一个错误,没有找到解决方法,后来又太高了栈顶,在bss+0x1000处构造的,然后就会成功。
总结一下利用思路:
1 | 劫持程序控制流至read函数,同时将system的参数“/bin/sh”pop给edx,最后返回到bss+0x1000处。 |
exp
1 | from pwn import * |