一切都源于某个中午的突发奇想~
想法
众所周知,在较低的libc版本(2.34之前)下进行orw利用一般是把__free_hook劫持为setcontext进行栈迁移(现在还需要跳板gadget完成rdx到rdi的转变),堆的各项布局对于我这个想寄就寄的菜鸟来说真是心力憔悴。
转念一想free(buf)中的buf是人为可控的,free也可以由__free_hook被劫持为某个函数。刚好当时国赛出题出的就是有关格式化字符串printf和scanf的漏洞利用,就有了这样的想法——free(buf)===>scanf(buf),其中buf堆块中存放着%n$s来对准rbp链在函数返回地址处直接构造orw。
scanf格式化字符串漏洞
利用scanf(%n$s)可以对偏移为n的栈上指针指向处进行任意长度的输入。常见利用手法有:
- 栈上若残留
IO指针可进行IO_ATTACK泄露libc - 利用
rbp链构造rop,直接避开了rbp链前的canary
试验
于是兴致勃勃地跑去用上次刚复盘过的题目进行试验:
下图为已劫持__free_hook为scanf,正在尝试执行scanf('%8$s')操作(8为rbp链的偏移):
送些垃圾数据过去,看一下栈的变化:
可以看到我们成功利用rbp链将main函数栈的返回地址给覆盖为了anzaanza,加上scanf也不会被\x00截断,所以直接构造orw并非难事。
分析
优点
省去了对堆块的布局操作以及寻找
rdx和rdi转变的gadget,简化为只需要爆破出固定偏移即可。上次复盘时感觉
setcontext和orw链需要在一个较大的堆块中进行布局,如果所给堆块较小的话,大概会提高利用的复杂程度(第一次利用setcontext解题,经验不足,可能是拙见)。而scanf可输入不限长,对参数堆块buf的大小也不做限制。如果题目允许,则可不断申请、释放?相当于无限次格式化字符串漏洞(不论劫持为
scanf还是printf),可以在栈上构造想要的地址,然后对其指向处进行修改等操作?
缺点
- 试验过程中我们知道,在
delete功能函数中利用scanf格式化漏洞利用只能修改到其父函数即main函数的返回地址,而许多题目都是直接exit退出了,不给main函数返回的机会。 - 会被
\x0a截断,对rop链有要求。 - 大概更适用于
orw,毕竟不开沙箱的话直接构造system('/bin/sh')就好。
其他
scanf(buf)和system(buf)都是控制函数和其第一个参数,而后者被sandbox之后,前者还有用武之地(除非orw白名单)。不知能否配合io_file之类的利用手法,以及搭配起来是否比原解法更方便。