使用fgetc冲破全缓冲
Oxh3y3 二进制安全 449浏览 · 2025-04-05 07:04

XYCTF2025中的Ret2libc's Revenge 这道题真的做我难受,使用了三种方法

1 main函数的返回地址libc_start_call_mainogg但是要爆破,可能性是4096分之一。本机aslr开启以后可以爆进去,但是不能执行cat /flag

2 使用elf的相关gadget控制rdi再执行call puts从而将libc地址写入输出缓冲区。再写上0x3e个执行call puts的语句把缓冲区塞满,最后输出libc。可是远程不行!开始以为是缓冲区大小不一样,但是我写了一个循环去试,没试出来。

3 使用elf的相关gadget控制rdistdout,再调用fgetc@plt就可以刷新缓冲区。因为我控制不了rcx为2从而执行setvbuf,所以想试试fgetc是否有奇效。

setvbuf

setvbuf函数,它有三种mode:

也就是说,要想使缓冲区的地址打印出来,有以下思路:

1重新设置setvbuf,但这不知道管不管用,没法尝试因为这需要4个参数,没法控制rdx和rcx

2调用fflush(stdout),但是需要泄露libc地址,死循环,这里做不到

3挤爆缓冲区,自然就会把内容打印出来了 这三种思路我都没走通,干脆走其他路子--那就是使用fgetc刷新缓冲区。

原理分析

fgetc定义:

啥也看不出来,把libc.so.6丢到ida里。
image.png
判断了fp->_IO_read_pt >= fp->_IO_read_end
image.png
如果将fp设置为stdout,也就是控制rdi。那么我们可以看到这两个值是相等的。所以程序会进入__uflow,跟进去看看。

定义如下:

可以看到会执行到_IO_switch_to_get_mode这个位置。

定义如下:

学过IO的师傅们就轻车熟路了。fp->_IO_write_ptr > fp->_IO_write_base这条判断显然是成立的。那么就会进入_IO_OVERFLOW,#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
image.png
就是说会调用stdoutvtable中的__overflow。实际上就是调用_IO_new_file_overflow
image.png
image.png
源码如下:

一些重要的魔数定义如下:

此时f->_flags=0xfbad2884,其中_IO_NO_WRITES = 0x8 ,所以f->_flags & _IO_NO_WRITES=0。会进去下一个分支,(f->_flags & _IO_CURRENTLY_PUTTING) = 0x800f->_IO_write_base != NULL那么就会走到if (ch == EOF)这里,因为rsi=0xffffffff所以条件成立,那么就会调用`_IO_do_write (f, f->_IO_write_base,f->_IO_write_ptr - f->_IO_write_base);

_IO_do_write定义如下:

new_do_write定义如下:

接着会调用_IO_SYSWRITE也就是_IO_file_write
image.png


定义如下:

最后会调用__write (f->_fileno, data, to_do),其实就是调用write输出f->_IO_write_ptr - f->_IO_write_base中的内容
image.png
至此刷新了stdout的缓冲区。

总结

条件允许的情况下其实也不用去塞满stdout的缓冲区,这种使用fgetc的简洁,也不需要知道缓冲区的大小,更不用payload里写很多gadget让执行流执行输出去挤爆缓冲区。

3 条评论
某人
表情
可输入 255
目录