前言
2.34以上的版本取消了hook的存在 导致我们getshell的操作只能通过其他办法了 于是house of的链学习就成为了必要 house of cat这条链 只需要一次largebinattack的机会 随后就是伪造io结构 就可以做到劫持程序执行流 诸如setcontext等的利用
链路分析
触发io的方式和house of kiwi相似 都是通过assert输出报错 不过还是有所不同 接下来跟着我来分析一下
截止到__main_assert函数的时候 链路都和house of kiwi一致 这里就不复述了
不同的是 kiwi是利用fflush触发fake file 本次我们是通过fxprintf中的函数 来实现任意函数调用
原理是因为vtable虽然在2.24以后加入了检测 但是由于检测不严格 对于一些轻微的偏移可以被无视 所以我们可以修改vtable为其他的表并且加上偏移 就可以实现表内的函数任意调用
而在这些函数中 会存在一些以寄存器寻址的call指令 利用这一点我们就可以劫持程序执行流
本次利用的关键就是_IO_wfile_seekoff函数 其内部会调用IO_switch_to_wget_mode函数 该函数内部存在一个可以被我们控制的call指令
我们首先利用largebinattack将stderr的地址覆盖为堆地址 随后在堆中伪造结构体 将vtable结构体修改为_IO_wfile_jumps+0x10 这样就可以顺利调用到_IO_wfile_seekoff函数
接着回到gdb 我们跟进一下fxprintf函数 一起完整的过一遍链
我们还需要进行两次繁琐的函数跳转 随后才能到达利用的关键函数点
这里 就会进入被我们所伪造的vtable表固定的偏移中 在我们上面那样设置的情况下 最后是会调用到_IO_wfile_seekoff函数 我们跟进一下
可以看到 其内部存在IO_switch_to_wget_mode函数 我们跟进一下
这里存在一个可控的call指令 rax的值可以被我们所操控 是写入到stderr上的堆地址固定偏移的一块地址
如果我们在此处地方写入打setcontext用的万能gadget 再搭配上setcontext 就可以做到控制程序执行流
链路其实是比较清晰的 我们主要是来看一下如何伪造 很多网上的资料其实伪造部分都没有讲清楚
伪造分析
首先我们提供一个完整的fake file构成 我们根据上面所圈出来的框 依次来讲解为什么要如此设定 需要绕过哪些地方 因为授人以鱼不如授人以渔 教会如何利用好动调来伪造结构体才是最重要的 虽然这篇文章也不能让你马上掌握这个能力
首先是灰框部分 之所以要这样布局 我们先来看前面的这个libc地址 要明白其用意何在 我们首先要定义到需要这个参数的位置 我的办法是直接n 通常会直接卡在某个语句或者触发某个报错 通过这个办法来判断执行到了哪个汇编语句
可以看到 是触发了进程的终止 那么这个一般是由于vtable表的检测失败引起的
为了佐证我们的猜想 我们找到触发检测的函数 随后直接n 看能不能通过这个函数 结果发现不行 印证了猜想 那么我们要想绕过这一点 显然就是靠最近的一次判断 只要不跳转到执行vtable表检测的地方就行了
关键的两个寄存器 就是rcx和rax了 rcx最后一次的复制和rax有关系 而rax当时的值是一个相对固定的
rax最初的值是一个相对固定的 索引到的是一个libc地址
而执行到sub的时候 对应的参数是一个栈上的地址
显然sub后的rax值我们并没有办法改变 那么重点就是放在通过r12来影响rax的值
注意到最开始r12的赋值语句 和rbp有关系 而rbp的值正是我们的fake file的起始地址 而对应的偏移正是我们所说的灰色部分 但是由于赋值完以后还要减去rdi寄存器的值 所以最后赋值给rax的值最好比较大一点
接下来 我们根据程序执行流跟进到_IO_wfile_seekoff函数 可以看到要想成功执行到Io_switch_to_wget_mode函数 就需要我们通过上面的cmp判断 rcx和rdx这两个寄存器 分别溯源一下最后一次赋值
和rax有关 而rax为fake file的起始地址 对应的偏移处是我们最开始图片的红框 我们要使得二者的值不一样 才能进入_IO_wfile_seekoff函数
进入_IO_wfile_seekoff 可以看到此时我们就可以进行任意地址call了 对应的偏移是0xe0 对应着我们最开始图片的
黄框 并且另外一个参数还有作用 这个后面遇到了再讲
接着就是万能gadget的调用了
此时rdi的值就是fake file的起始地址 我们需要给其设定对应的值 以此来决定rdx的值 从而控制最后的call
对应着我们图片中的红框蓝框 我们将其布置好 使得接下来执行setcontext
然后后面的就是setcontext的利用问题了 这里就不讲了
没有提到的是紫色框中的参数 这一部分 我们直接将其赋为0 随后n看会卡在哪个地方
可以看到会卡在这里 这里是因为rdi+8处不是一个地址 但是这里的cmp又要调用对应地址的内容 所以就无法进行 我们溯源一下 看看rdi的值是怎么得到的
可以看到和rbx有关系 而动调发现rbx的值就是fake file的起始地址 所以在对应地址布置一下就好了
模板
首先泄露libc地址和hepa地址 随后利用largebinattaack在stderr写入可控堆块地址用来充当fake file
随后布置好结构体 利用top chunk或者largebin chunk来触发io链 最后orw
add(0x420)#0 |