魅力程序猿

  • 首页
  • Java
  • Android
  • APP
    • 扑克计分器
    • Video Wallpaper
  • 联系我
  • 关于我
  • 资助
道子
向阳而生
  1. 首页
  2. AI技术
  3. 正文

House of botcake与IOFILE任意读写

2026年5月21日 133点热度 0人点赞 0条评论

House of botcake

利用条件与效果及使用范围

应该是在libc2.31这个手法要的条件就是两个:UAF与申请足够数量的堆块。而效果就是:任意地址写,配合对IOFILE结构体的利用可以实现任意读,结合起来就可以进行任意读写。主要思路就是绕过tcache的double free检测即tcache key字段来完成攻击。虽然自有tcache机制以来到现在都可以使用,不过因为之前tcache检测很弱直接double free也没有影响,所以主要还是glibc2.29-至今用的比较多。

主要流程就是先申请7个同一大小的堆块,再申请两个相同大小的堆块,前申请的我们不妨叫堆块a,后申请的叫堆块b,再申请一个堆块防止合并。
释放申请的9个堆块,其中7个堆块会填满tcache对应大小的链表,此时因为tcache已满,我们再释放相同大小的堆块就会进入unsortedbin。

同时,因为unsortedbin的堆块如果物理相邻就会合并,我们需要让堆块a和b合并。合并后我们再申请一个相同大小的堆块,由于系统会首先遍历tcache链表,如果能找到就会在tcache里取堆块。所以会从tcache里取一个堆块出来,此时我们利用UAF漏洞再free一次堆块b,这样堆块b就又在tcache又在unsortedbin了。

为什么能free?实际上free一个指针应该只会检测这个指针+*(这个存放这个指针的地址-8)的地址的p标志位是不是1
以及这个指针+*(这个存放这个指针的地址-8)的地址的下一个地址的p标志位是不是1。
文字描述太抽象了,比如有UAF,我们直接在链表里找出来那个地址。
这个地址一般就是我们写入数据的地址,我们用这个地址减0x10,再用heap 这个地址 
这个命令看一下堆块的完整性即可。

此时如果我们申请比堆块a大小大一点的堆块,由于在tcache里找不到,系统就会在unsortedbin里切割,此时就会切割我们a,b合并后的堆块了。此时我们就完成了overlapping,可以改在tcache里的堆块b的fd指针实现任意地址写了。如果我们不这样申请,而是多申请几次,申请的堆块的总大小等于堆块a的大小,就可以在堆块b的fd指针里留下main_arena相关的地址,而这个地址又和libc相关。只要我们再继续切割unsortedbin不就可以改堆块b的fd指针吗,只要爆破两位就可以构造出指向IOFILE结构体的指针,进而完成任意读写。

目前我感觉我还是不太有能力结合源码分析,各位如果想看结合源码分析可以看raycp师傅的文章IO FILE 之任意读写,写的非常好。本文大部分也借鉴了其中的内容,不过我是以比较宏观的视角来分析的,可能比较好理解一点。不过源码肯定要看,之后我也会结合源码讲讲。

先回顾一下IOFILE结构体

0x0   _flags
0x8   _IO_read_ptr
0x10  _IO_read_end
0x18  _IO_read_base
0x20  _IO_write_base
0x28  _IO_write_ptr
0x30  _IO_write_end
0x38  _IO_buf_base
0x40  _IO_buf_end
0x48  _IO_save_base
0x50  _IO_backup_base
0x58  _IO_save_end
0x60  _markers
0x68  _chain
0x70  _fileno
0x74  _flags2
0x78  _old_offset
0x80  _cur_column
0x82  _vtable_offset
0x83  _shortbuf
0x88  _lock
0x90  _offset
0x98  _codecvt
0xa0  _wide_data
0xa8  _freeres_list
0xb0  _freeres_buf
0xb8  __pad5
0xc0  _mode
0xc4  _unused2
0xd8  vtable

我们知道,除了进行系统调用的输入/输出read,write这些,其他的输入/输出都会用到IOFILE结构体,即三个输入/输出流中,即stdin(标准输入),stdout(标准输出),stderr(标准错误)。而我们输入/输出一般都是缓冲输入/输出。我们可以这些流中的结构体成员起到任意读写的效果。

比如stdin,我们就是利用了改变输入缓冲区的地址,来把我们本应该输入到输入缓冲区的数据,先输入到了我们要输入的目标地址,即控制stdin的_IO_buf_base与 _IO_buf_end。

当然我们利用肯定是有条件的:

  • 条件第一个就是输入缓冲区要没有数据即_IO_read_end —— _ IO_read_ptr >0
  • 条件第二个就是flags字段表明这地方可写,即flag里没有不可写的位标志,其中c语言定义了#define _IO_NO_READS 4,也就是我们的flags字段&0x4要为0,即设置flags&~0x4
  • 条件第三个我们输入的文件描述符,如果想用键盘输入就要把_fileno设为0
  • 条件第四个就是 _IO_buf_base设置成我们写入的地址, _IO_buf_end设置成我们写入结束的地址。且输入缓冲区的大小即 _IO_buf_end- _IO_buf_base要大于我们输入的数据的大小。

这样我们就可以利用stdin进行任意写了。当然其实实际上一般我们很多条件本来就会满足,我们改的时候注意一下这些条件即可一般就改 _IO_buf_base与 _IO_buf_end就可以了。

由于stdout即有把输出缓冲区的数据输出出来,又有把数据写入输出缓冲区,所以利用stdout可以进行任意读写。但是这个任意写需要我们能控制输出的数据,并不能直接从键盘上读取数据。实际利用的话把_IO_write_ptr(输出缓冲区的开始)改成我们想写的地址的开始,把 _IO_write_end(输出缓冲区的结束)改成我们想写地址的结束即可。

跟stdin类似,我们攻击的是输出缓冲区,利用输出时如果系统检测到输出缓冲区有数据就会刷新输出缓冲区的机制,把 _IO_write_base改成我们想泄露数据的开始,把 _IO_write_end改成我们想泄露数据的结束即可。

  • flags标志不能有_IO_NO_WRITES标志,即flags&0x8必须为0。可以设置flags&~0x8
  • flags标志最好有_IO_CURRENTLY_PUTTING标志,因为不然会多一些操作,让流程不可控,可以设置flag | 0x800
  • 设置_fileno为1
  • 设置_IO_read_end等于 _IO_write_base 或设置 _flag & _IO_IS_APPENDING即 _flag | 0x1000。
  • 把 _IO_write_base改成我们想泄露数据的开始,把 _IO_write_end改成我们想泄露数据的结束即可。

2026ISCC线上挑战赛决赛——note

这个比赛如何暂且不骂了,这个远程靶机也先不骂了。单单看这个题还是出的可以的。题开了pie,开了沙箱,只允许ORW,glibc给的版本是2.31,我们当2.31打吧。远程好像不是这个版本(我***)

逻辑就是普通的堆菜单题有edit功能,但是没有show功能,不能输出数据。有UAF,索引可以申请0-0x15,但索引可以复用。所以我们可以打House of botcake,然后攻击stdout泄露出libc此时就有两种思路了。

第一种是:我们之前在打stdout的时候已经有了攻击stdout的伪造的堆块,所以我们可以用edit功能一直打stdout。这样我们就可以利用stdout读环境变量泄露栈地址打栈上rop。当然没有edit也影响不大,因为索引可以复用,再打一遍House of botcake也一样。

第二种是:因为版本是2.31,我们可以打__free_hook,把freehook改成magic gadget,然后利用setcontext调用实现栈迁移,把栈迁移到freehook来,并且利用setcontext调用read往freehook上写rop链实现ORW。

这两种都可以,这里要泄露堆地址个人感觉不是很方便,所以就没想过写shellcode的。用mprotect把freehook附近的地址改成可执行然后在上面填shellcode感

标签: AI 人工智能 技术博客
最后更新:2026年5月23日

daozi

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论
搜索
联系方式

QQ群:179730949
QQ群:114559024
欢迎您加入Android大家庭
本人QQ:136049925

赐我一丝安慰
给我一点鼓励

COPYRIGHT © 2023 魅力程序猿. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

豫ICP备15000477号