0x0 简介
熟悉一下IO_file,参考下列资料
https://qianfei11.github.io/2019/08/17/Pwnable-tw-seethefile/
http://blog.leanote.com/post/mut3p1g/FSP-pwnable.tw%5B9%5D
https://ray-cp.github.io/archivers/IO_FILE_fclose_analysis
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/io_file/introduction-zh/
0x1 File 结构描述
1 | /* The tag name of this struct is _IO_FILE to preserve historic |
进程中的 FILE 结构会通过_chain 域彼此连接形成一个链表,链表头部用全局变量_IO_list_all 表示,通过这个值我们可以遍历所有的 FILE 结构。
在标准 I/O 库中,每个程序启动时有三个文件流是自动打开的:stdin、stdout、stderr。因此在初始状态下,_IO_list_all 指向了一个有这些文件流构成的链表,但是需要注意的是这三个文件流位于 libc.so 的数据段。而我们使用fopen
创建的文件流是分配在堆内存上的。
但是事实上_IO_FILE 结构外包裹着另一种结构_IO_FILE_plus,其中包含了一个重要的指针 vtable 指向了一系列函数指针。
在 libc2.23 版本下,32 位的 vtable 偏移为 0x94,64 位偏移为 0xd8
IO_FILE_plus
1 | struct _IO_FILE_plus |
vtable
1 | const struct _IO_jump_t _IO_file_jumps libio_vtable = |
偏移
1 | //struct _IO_FILE |
0x2 seethefile
分析程序
openfile:
读取 长度为63 filename,打开文件,这里有溢出,因为 filename 大小为 40,但是不能覆盖到fpwritefile:
输出文件 magicbuf,通过这个应该能泄露地址,首先要把内容写道magicbufreadfile:
从文件中读取内容到 0x18 长度closefile:
这里会调用 fclose 函数,可以用来触发xexit :
这里 name 有溢出。可以覆盖到fp,使之指向我们伪造的fp,在调用fcolse时调用system(“bin/sh”)
漏洞利用
泄露libc
在linux系统中,文件/proc/[pid]/maps中记录了pid对应程序的内存区域以及权限信息等,程序自身可以通过访问/proc/self/maps文件获取这些信息,因此我们可以利用本题文件读取的功能,获取maps文件中记录的地址信息,从而获得libc的地址。
构造file
想要拿到shell,以下是关键:
fp
指向伪造的IO_file
存在IO_file
的vtable->_fclose
覆盖为system
地址- 因为
vtable
中的函数调用时会把对应的_IO_FILE_plus
指针作为第一个参数传递,因此这里我们把"sh"
写入_IO_FILE_plus
头部。
但是构造的时候需要注意:
- 偏移
0x48
处的lock
字段指向的是一个IO_stdfile_2_lock
结构,本地调试时这个结构中的数据均为\x00
;因此,我们可以用\x00填充name,然后用name的地址覆盖lock
字段。 - 同时要注意
_vtable_offset
要为0,其偏移为0x46
且只占一个字节
从看到一位师傅的思路不错,它一开始总是报错,于是就找了一个合法的FILE
结构,将里面不为空的全部复制为name
对应的地址,其他的都为0,将这个结构重新尝试了一遍就能在本地成功了。
其它
__flags
记录 FILE
结构体的一些状态;_markers
为指向 markers
结构体的指针变量,存放流的位置的单向链表;_chain
变量为一个单向链表的指针,记录进程中创建的 FILE 结构体。
exp
1 | from pwn import * |