该笔记目的是为了方便自己做题,所以每个打法写的非常简便,且部分内容为从各位师傅的文章中直接复制,没有记录更加深入的原理
部分结构源码
_IO_FILE
1 | struct _IO_FILE |
_IO_jump_t
1 | struct _IO_jump_t |
struct _IO_wide_data
1 | struct _IO_wide_data |
_IO_wfile_jumps
1 | const struct _IO_jump_t _IO_wfile_jumps libio_vtable = |
_IO_printf_buffer_as_file_jumps
1 | static const struct _IO_jump_t _IO_printf_buffer_as_file_jumps libio_vtable = |
_IO_cookie_jumps
1 | static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = { |
Tcache Stashing Unlink Attack
- 利用流程
1、假设目前tcache bin中已经有五个堆块,并且相应大小的small bin中已经有两个堆块,由bk指针连接为:chunk_A<-chunk_B
2、利用漏洞修改chunk_A的bk为fake chunk,并且修改fake chunk的bk为target_addr - 0x10
3、通过calloc()越过tcache bin,直接从small bin中取出chunk_B返回给用户,并且会将chunk_A以及其所指向的fake chunk放入tcache bin(这里只会检测chunk_A的fd指针是否指向了chunk_B)
4、在fake chunk放入tcache bin之前,执行了bck->fd = bin;的操作(这里的bck就是fake chunk的bk,也就是target_addr - 0x10),故target_addr - 0x10的fd,也就target_addr地址会被写入一个与libc相关大数值(可利用)
5、再申请一次,就可以从tcache中获得fake chunk的控制权
poc(how2heap):
1 | // glibc 2.36 |
fastbin reverse into tcache
- 利用流程:
1、申请14个fastbin范围内的chunk,然后释放7个用来填满tcache,然后将剩下的七个释放进fastbin中
2、修改第一个放进fastbin的chunk的fd为&target
3、通过申请7个chunk将tcache清空
4、申请一个chunk,此时由于tcache中已经没有chunk了,所以会去fastbin里拿,然后将fastbin中剩余的chunk转到tcache中,fastbin中有7个,分配一个,剩下六个,依次放进tcache中,而最先放入fastbin中的chunk的fd被我们改成了&target,所以tcache会将target也当做一个chunk放进tcache中
5、此时tcache中有7个chunk,最后一个chunk即为target
poc(how2heap):
1 | // glibc 2.36 |
largebin attack
- 利用流程:
1、在largebin list中放入一个堆块A,并利用UAF等漏洞在bk_nextsize写入target_addr - 0x20
2、释放一个大小略小于堆块A的堆块B进入到同一个largebin list,此时就会在target_addr中写入堆块B的地址
利用_IO_2_1_stdout_泄露地址
通常将_flags设置为0xfbad1800 设置_IO_write_base指向想要泄露的地方 _IO_write_ptr指向泄露结束的地址
之后遇到puts或printf 就会将_IO_write_base指向的内容打印出来
1 | payload = p64(0xfbad1800) + p64(0) * 3 + '\x00' |
通过off_by_null进行unlink
注意:通常第一个被释放的chunk不会被进行合并
某次做题时进行的操作
1 | ptr = heap_base+0x2c0 # ptr指向第一个chunk的fd指针的位置(chunk 0) |
chunk1的中间部分+chunk2+chunk3进行合并
house of botcake
通常在存在UAF漏洞,但只能对其进行delete操作时使用
利用步骤
1、先将tcache bin填满(大小要大于0x80)
2、再连续free两个连着的堆块(A在B的上方,A不能进入tcache bin 且 B的大小要与第一步tcache bin中的相等),使其合并后进入unsorted bin
3、从tcache bin中取出一个堆块,空出一个位置
4、将Chunk B利用UAF漏洞,再次释放到tcache bin中,并申请回unsorted bin中的Chunk A & B合并的大堆块(部分),修改Chunk B的next指针指向任意地址,并申请到任意地址的控制权
house of kiwi(<= 2.36)
- 将
_IO_file_jumps中的_IO_new_file_sync修改为setcontext + 61注意:该_IO_file_jumps位于stderr中 - 其中
rdx指向_IO_helper_jumps_addr,rdi指向_IO_2_1_stderr_addr - 可以通过在
_IO_helper_jumps_addr + 0xA0的位置写入rop链也可以通过在libc中找gadget将rdi转移到rbx中 - 通过
__malloc_assert触发该攻击
house of cat
- 依旧为将
stderr所指向的区域伪造一个fake_io_file - 利用模板
1 | fake_io_addr = heapbase+0xb00 # 伪造的fake_IO结构体的地址 |
- size为
0x118
模板二:
1 | fake_file = flat({ |
- 最终通过
_malloc_assert来触发攻击
从2.36版本开始,删除了_malloc_assert对stderr的刷新,因此通过该方法触发攻击的手段生效,但可以使用fsop的方法来触发攻击(FSOP需将vtable改为IO_wfile_jumps+0x30,并且要将_IO_list_all指向可控的fake_file地址)
house of 秦月汉关
修改libc中的部分got.plt节来执行我们想要的函数
例子
puts函数调用时会隐式的调用strlen函数,而strlen函数的got.plt节在libc中,如果我们将strlen的got.plt中的内容改为system函数的地址,那么执行puts("/bin/sh")->strlen("/bin/sh")就会变成puts("/bin/sh")->system("/bin/sh")进而getshell
poc:
1 |
|
house of apple2
主要是因为系统没有对wfile_vtable中函数指针的合法性进行检测,导致可以让我们进行利用
利用链:
1 | _IO_wfile_underflow_mmap |
在_IO_2_1_stderr_的位置伪造一个fake_file
Csome版fake_file:
1 | fake_file = flat({ |
通过exit或者主函数返回触发该攻击
改方法也可以用于打rop,将0x28处设置为magic gadget的地址,0x0处布置好内容用于将指定的地址设置进rbx中并使rip能够指向setcontext+61
1 | # stderr-0x40+0x18 == 0 -> FILE->_wide_data->_IO_wirite_base == 0 |
house of husk
改方法主要是利用printf函数在进行格式化字符串时会调用__printf_arginfo_table和__printf_function_table中的函数指针所指向的函数,此时我我们可以劫持这2个指针所以指向的地址,然后在该地址中写入ogg的地址,在下次调用printf函数时即可getshell
这里要注意,在进行格式化字符串时操作系统会对这两个指针判断是否空,如果为空则会调用calloc来分配内存来给这两个表,其中每一个表的大小都为0x100.需要注意的是,在我们伪造的表中,格式化字符所对应的函数指针要么是0,要么是一个合法的地址
假设现在__printf_function_table和__printf_arginfo_table分别被填上了chunk 4与chunk 8的堆块地址(chunk header)
方法一:
1 | one_gadget = libc.address + 0xe6c7e |
由于有堆块头,所以格式化字符的索引要减2,这样写就满足了__printf_function_table不为空,进入了printf_positional函数,并调用了__printf_arginfo_table中的函数指针
方法二:
1 | one_gadget = libc.address + 0xe6ed8 |
__printf_arginfo_table和__printf_function_table的地址可以直接通过p &来查找
可以利用下面这个函数来查找相关地址:
1 | /* Register FUNC to be called to format SPEC specifiers. */ |
函数 vfprintf 中的部分源码:
1 | if (__glibc_unlikely (__printf_function_table != NULL |
house of pig
主要是利用_IO_str_overflow这个io函数,该函数中可以完成malloc、memcpy、free一条龙服务
且该函数汇编中存在下面这段代码
1 | 0x00007ffff7e2f0dd <+61>: mov rdx,QWORD PTR [rdi+0x28] |
rdi指向IO_FILE结构体首地址,可以方便我们来通过setcontext+61来打srop
高版本没有hook函数,可是_IO_str_overflow中存在memset函数的调用,而且该函数的got表在glibc,glibc中的got表可写,所以可以修改got表来达到和hook相同的效果
fake_file
1 | # 在0xa0的tcache链表头伪造一个memset_got_addr的地址 |
需要注意的是,在memset之前仍然有free(IO->buf_base),因此需要伪造一下memset_got_addr的fake chunk的堆块头,以及其next chunk的堆块头
最终通过exit()函数或者程序正常退出触发攻击
house of emma
house of emma主要利用了_IO_cookie_jumps这个vtable
利用house of KiWi配合house of emma的调用链为__malloc_assert -> __fxprintf -> __vfxprintf -> locked_vfxprintf -> __vfprintf_internal -> _IO_new_file_xsputn ( => _IO_cookie_write),这里用的是_IO_cookie_write函数,用其他的当然也同理
fs寄存器的值和地址可以通过x/16gx pthread_self()指令来查看
fake_file:
1 | # __pointer_chk_guard为tls[0x30]处的值,用于对数据进行加密 |
最终通过_malloc_hook来触发(<=2.36)
当然也可以通过_IO_flush_all来触发,不过由于在exit函数的调用过程中会出现很多会出现调用别的给加密过的函数指针,由于__pointer_chk_guard已经给我们修改,所以这些函数指针在给调用的时候会给报错,即该方法的利用比较麻烦,不建议使用
劫持tls_dtor_list,利用__call_tls_dtors拿到权限
主要功能为getshell,当然也可以利用setcontext+61打srop
dtor_list:
1 | struct dtor_list |
__call_tls_dtors:
1 | void __call_tls_dtors (void) |
可以看到func其实是一个函数指针,而obj则是指向该函数的参数的位置
我们可以将tls_dtor_list指向我们伪造的堆地址,将func改为system的地址(ogg也可以),obj指向/bin/sh\x00即可getshell
注意,这里对func这个函数指针进行了加密,所以要泄露出__pointer_chk_guard的值或将其修改为已知的值
fs寄存器的值和地址可以通过x/16gx pthread_self()指令来查看
模板:
1 | ROL = lambda val, r_bits, max_bits: \ |
若是想orw,那么可以让func成员为magic_gadget的相关数据,将rdi与rdx转换后,再调用setcontext + 61走SROP即可
最终通过exit()函数退出程序触发攻击
house of apple3 + house of 一骑当千
这个模板为两种打法相结合,house of 一骑当千的好处是打orw时不需要用到magic gadget。house of apple3的关注点主要是对成员_codecvt的利用,当wfile的vtable给上保护后依然可以使用
模板:
1 | import numpy as np |
house of some
Csome学长提出的利用链,主要是通过伪造fake_file来造成任意地址写和任意地址读
其中fake_file分为write_file和read_file,令_IO_list_all指向一个read_file,并令write_file的_chain指向将要执行的read函数所写入的地址。在进行fsop时会遍历到read_file执行read函数,此时写入write_file,并令其_chain指向另外一个我们已经写好的read_file。此时由于我们第一个read_flie的_chain已经指向我们read函数所写入的地址,而该地址已经被我们写入了write_file,我们可以通过该write_file来泄露任意地址。执行完wirte函数后rip则会继续遍历到我们下一个read_file。我们便可以不断的进行read-write-read这样一个过程,可以无数次泄露程序的所有地址和修改任何地址中的内容
write_file:
1 | fake_file_write = flat({ |
read_file:
1 | fake_file_read = flat({ |
Csome的自动化脚本:
1 | from pwn import * |
在编写exp时,只需通过from House_of_some import HouseOfSome将该包导入即可
house of banana
🍌🍌🍌🍌🍌🍌🍌🍌🍌🍌🍌🍌🍌
通过puts函数来触发IO链
通过源码我们可以发现,puts函数在启用时会调用虚表中的_IO_wfile_xsputn函数,因此这里给了我们机会来修改虚表偏移来触发io链
我们可以将fake_file直接写在_IO_2_1_stdout_的位置上面或者修改stdout指针指向我们的fake_file
首先是用于getshell的house of apple2
1 | fake_file = flat({ |
然后是用于打orw的house of apple2
1 | fake_file = flat({ |
house of some改进版
这里就贴某次比赛的exp,优点是可以不泄露堆地址
1 | fake_io_read = flat({ |
house of some2
原文链接:https://blog.csome.cc/p/house-of-some-2/ 只能说非常的暴力
1 | from bisect import * |
参考文章
https://www.cnblogs.com/Sta8r9/p/17586219.html(house of cat)
https://bbs.kanxue.com/thread-273895.htm#msg_header_h3_6(House of cat新型glibc中IO利用手法解析 && 第六届强网杯House of cat详解)
https://www.anquanke.com/post/id/235598(House OF Kiwi)
https://bbs.kanxue.com/thread-273832.htm([原创]House of apple 一种新的glibc中IO攻击方法 (2))
https://bbs.kanxue.com/thread-272098.htm#msg_header_h3_13([原创] CTF 中 glibc堆利用 及 IO_FILE 总结)
https://bbs.kanxue.com/thread-276031.htm([原创]无路远征——GLIBC2.37后时代的IO攻击之道(二)house_of_秦月汉关)
https://bbs.kanxue.com/thread-273863.htm#msg_header_h3_0([原创]House of apple 一种新的glibc中IO攻击方法 (3))
https://bbs.kanxue.com/thread-276056.htm([原创]无路远征——GLIBC2.37后时代的IO攻击之道(五)house_of_一骑当千)