一切都源于某个中午的突发奇想~
想法
众所周知,在较低的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
之类的利用手法,以及搭配起来是否比原解法更方便。