# [HGAME2024] Week4-EldenRingFinal
# 题目描述
年轻人的第一道 IO_FILE 题目
结构体比较复杂的 heap,看似 free 函数之后没有清空指针会有 uaf 漏洞,但是由于 chunk 之间采用链表链接,取出 chunk 也就没法遍历到了,所以就相当于清空指针的作用。而且创建指向内容的 chunk 的 chunk 在 malloc 之后也写入了新东西,所以没法直接 UAF。
add_note 函数在往 chunk 写入内容的时候有 Off-By-One 漏洞,因此我们能使用 chunk extend 来覆盖之后的 chunk 实现 UAF 的效果。
但是没有提供 show 函数,也就没法直接 leak libc 了。
于是直接打 IO_FILE 结构体来 leak 出 libc
# IO_FILE struct
1 | struct _IO_jump_t |
可以看到结构体的 vtable 中存储着一堆的函数指针,据说可以伪造 vtable 来 get shell。
1 | struct _IO_FILE_plus; |
1 | struct _IO_FILE { |
可以看到_IO_FILE 结构体保存的是读写的指针以及 flags,可知 stdin,stdout,stderr 都各有一个这样的结构体。
read write 各有 ptr end base 三个项目,分别表示当前指针,结束位置指针,开始位置指针。
_chain 指针就是把这几个结构体和_IO_FILE_all 串联起来了。
# IO_FILE - 调试
从理论到实践
打开 IDA,直接看.bss 上面的三个结构体,分别就是 stdout,stdin,stderr 的结构体
用 gdb 调试
我们进入 stdout 结构体的 (_IO_FILE) file 看一下,就是_IO_FILE 结构体了
# IO_FILE leak - 利用
我们把_flags 修改为 0xfbadxxxx,清空 read 的三个指针,再把 write 的 base 指针最低一个字节修改就可以打印从 base 到 ptr 的内容
泄露_IO_file_jumps:
1 | payload = p64(0xfbad1800)+p64(0)*3+b"\x58" |
泄露_IO_2_1_stdin_:
1 | payload = p64(0xfbad3887)+p64(0)*3+p8(0) |
# 堆风水 + Off-By-One - 利用
首先我们要 leak libc,就要把一个 chunk 申请到 stdout 结构体那里把 stdout 给改成对应内容。由于没有 libc 基址,我们需要采用修改 free 的 unsortedbin 的 fd 的低位的方法实现得到 stdout 结构体的 fake chunk 的效果
由于 unsortedbin 以及 small,large bin 都是双向链表,我们很难采用这种他们来申请到 fake chunk。于是我们就需要用 Off-By-One 来把这个 unsortedbin 的 size 改为 0x71 并且把它串到 0x70size 的 fastbin 链表上面。
这题的 chunk 的结构相对比较复杂,但是我们如果采用某种方法把存储内容的 chunk 都放在连续的内存上面,那么 Off-By-One 就非常容易利用了。
这个过程很简单,不再详述。
之后就是修改下一个 chunk 的 size 位,让它覆盖到下一个 chunk,为了过 prev_inuse (nextchunk) 的检测,这里我直接覆盖了两个 chunk.
把这个大的 chunk free 掉,再 malloc 就可以实现写入下面的堆块的效果了,我们修改下一个 chunk (0x71) 的 fd 的低位,使它指向那个 unsortedbin,之后再用一次 Off-By-One 把这个 unsortedbin 的 size 改为 0x71,这样就能通过 fastbin 取出 chunk 的时候关于 chunksize 和 fastbin index 的检查了。
这样我们就可以申请到 fakechunk 并且修改 stdout 结构体了。
之后我们有了 libc 基址,就可以接着改 fd,申请到 hook 那里的 chunk,从而修改 malloc_hook 或者 free_hook 来 get shell 了
# EXP
为了能够在收到 EOF 的时候继续爆破而不用手动重启脚本,我们把整个 exploit 过程封装成函数 pwn (),之后添加:
1 | while 1: |
不知道为什么远程改 stdout 结构体的时候最后一位 \x58 的 payload 无法泄露地址,但是本地两种 payload 都可以泄露地址。
还有打 free_hook 的时候 fastbin 的 fake chunk 过不去 size 和 idx 的检查,但是调试了发现 size 是 0x7f 过不去,idx 对应的也是 0x70,很玄学。被迫打 malloc_hook.
1 | #patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn |
自动爆破:
1 | #patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn |
# 也许,我们并不需要爆破???
我们爆破是为了让 fd 指向我们固定的 chunk,如果让这个 fd 原本指向的地址和那个 chunk 的 offset 很小,那么我们只需要修改最后一个字节就 OK 了。
但是这样做增加了堆风水的难度,但是可行的。(把 0x30 的 chunk 扔出去,把那几个 chunk 的 size 缩小一下就可以了)