PWN-stdout学习
stdout
stdin、stdout、stderr对应程序的输入流文件、输出流文件和错误流文件,默认情况下文件标识符为0、1、2。
很多pwn题在init中都有如下代码,这是初始化程序的io
结构体,只有初始化之后,io
函数才能在程序过程中打印数据,如果不初始化,就只能在exit
结束的时候,才能一起把数据打印出来。。
1 | setvbuf(stdin, 0LL, 2, 0LL); |
函数原型如下:
1 | int setvbuf(FILE *stream, char *buffer, int mode, size_t size) |
- stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了一个打开的流。
- buffer – 这是分配给用户的缓冲。如果设置为 NULL,该函数会自动分配一个指定大小的缓冲。
- mode – 这指定了文件缓冲的模式。
而其中mode参数决定了文件缓存的模式:
对于stdout而言,mode为0时表示全缓冲,即程序在exit结束时或缓冲区填满时才打印内容。
mode为1时表示行缓冲,即程序在遇到换行符或者在缓冲区填满时才打印内容。
mode为2时表示无缓冲,即程序立刻打印当前内容。
很多pwn题中需要泄露libc,但题目中没有给出puts相关的函数或者将stdout的mode设为0(即开启全缓冲),使得没有办法泄露libc。
这里总结几种思路:
1、对于堆题,若程序中没有puts相关函数但能任意地址写,可利用_IO_2_1_stdout泄露libc,具体实现方法为修改该结构体的flag字段,使得程序误认为缓冲区有内容,即可调用io_puts函数输出stdout_addr进而泄露libc。_
参考: 好好说话之IO_FILE利用(1):利用_IO_2_1_stdout泄露libc_libc泄露方式-CSDN博客
2、对于栈题,若设置了setvbuf(stdout, 0LL, 0, 0LL) 且存在栈溢出,可:
若能控制rdx寄存器,则可通过调用setvbuf(stdout, 0LL, 2, 0LL) 将输出缓冲区开启,之后泄露libc。
若不能控制rdx,可尝试填满缓冲区将内容输出。
1
2
3for i range(150):
payload = b'a' * offset + p64(pop_rdi) + p64(puts_got) + p64(puts_plt)
io.sendlineafter(payload)有时候填满缓冲区对于本地可以,对于远程却不行(可能是远程对缓冲区有一些设置),此时可以通过fllush(stdout)刷新缓冲区,即可输入内容。
具体方法:若got表可写,由于fllush和setvbuf的libc地址相近,仅相差2字节,且低12位固定,因此可以考虑爆破4位,即1/16概率修改setvbuf为fllush,然后跳转回main(因为不知道stdout的地址),再次执行setvbuf(stdin, 0LL, 0, 0LL) 时会执行fllush(stdout)刷新缓冲区进而泄露出libc。
例题:2024春秋杯网络安全联赛夏季赛 stdout
程序开始设置了setvbuf(stdout, 0LL, 0, 0LL) 且存在栈溢出。
exp:
1 | from pwn import * |