后面打算多更一些IO链利用相关的,发现国内是真的喜欢考IO
libc 2.23及其之前的版本IO vtable没有保护,可以直接覆写IO_FILE结构体0xd8偏移的vtable指针,伪造vtable表,劫持程序流。
这里用一道经典老题——2018HCTF the end来展示基础IO getshell的利用方式。
程序libc版本为2.23.
首先看一下保护
打IO的基本上都是保护全开,算是比堆更复杂的利用方式了。
分析一下程序逻辑。
值得注意的是,程序关掉了标准输出stdout和标准错误stderr两个IO_FILE。然后给了五次任意地址写1字节的机会,最后调用exit函数结束掉程序。
这里打IO给出两种方式:
一、劫持stdout 的vtable表。
exit函数被调用结束掉程序时,有如下IO调用链子:
这里调用了stdout的vtable中的_set_buf函数。程序vtable表本身位于数据段,无法写,但是vtable指针可以被劫持。由于libc版本未对vtable指针做出检查,导致可以劫持vtable指针为fake vtable,再伪造_ste_buf函数表项为one_gadget直接getshell。
攻击思路如下:
1、首先劫持stdout的vtable为fake vtable,fake vtable的地址选取要求,该地址偏移0x58,即_set_buf在vtable中的偏移处可写,且为一个libc地址,这样我们才能用较少次数的任意地址写修改其为ogg地址,即选取的fake vtable应该在stdout结构体附近寻找。也可以去stdout结构体的vtable附近寻找。
2、在fake vtable的0x58处写入ogg地址,然后等着程序调用exit直接getshell。
exp如下:
执行结果:
成功执行one gadget。
二、写exit_hook
不熟悉exithook可参考如下链接:
https://www.cnblogs.com/bhxdn/p/14222558.html
exithook类似堆中的malloc hook和free hook为调用exit函数时必然会调用的函数,因此称之为hook,实质上是调用了 __rtld_lock_lock_recursive 和 __rtld_lock_unlock_recursive两个函数,这两个函数位于_rtld_global结构体中,而该结构体位于ld中,与libc与固定偏移,且可写。
可以gdb手动找一下:
可以直接看到该结构体和libc的偏移,而两个exithook的函数都跟结构体有固定偏移,具体由libc版本而定:
在libc-2.23中
hook1 offset:3848
hook2 offset :3856
在libc-2.27中
hook1 offset :3840
hook2 offset :3848
则直接利用任意地址写,将one_gadget地址写入任意一个exit hook即可。
exp如下:
执行结果:
在exit hook中写入one_gadget。
成功执行one_gadget。
值得注意的是,由于程序关闭了标准输出,笔者自行本地测试的时候无法得到标准输出。
不过通过执行touch 命令发现确实可以创建文件,说明还是成功执行了shell的吧。,而且看gdb的调试窗口确实也是执行到shell了,如果在远程环境打的话应该可以正常通过
exec 1>&0通过重定向标准输出得到服务器输出内容。
劫持vtable算是最简单的IO利用方式,后面会继续更一些house of家族的复杂IO利用,尤其是house of apple 123,国内考的好多都可以用apple2直接秒的说。
By Del0n1x
Keyboard