这个仅含源码的 PoC 针对一个固定版本的 WordPress Docker 官方镜像,复现了未认证的命令执行。
一个 pingback.ping 请求会让 WordPress 在同一次 PHP 请求中两次获取攻击者提供的 URL。第一次响应通过 Brotli 破坏了一个 PHP 处理程序。重试请求头启动 Bash 并返回实验环境中的 flag。
该 profile 针对特定目标。它不对当前 WordPress 镜像、其他摘要或 WordPress Core 中的内存错误作出任何声明。
警告:仅在随附的实验环境中、在你自己拥有的系统上运行此 PoC。它包含可用的 RCE 载荷。
/flag.txt,flag 在 XML-RPC 响应中返回。sh 到 bash 到 sleep 的进程链。| 项目 | 值 |
|---|---|
| 目标 | docker.io/library/wordpress@sha256:806680ba62232c4c300fb9dcc9f89d08e9e8c2bfc59837a1f58716f8648306d5 |
| 镜像 ID | c64dd89c00079939f18d70489e7c3e30cb071e8581824ab2de688f9f4d70d076 |
| 平台 | Linux amd64,Debian 13.7 |
| 应用 | WordPress 7.1.3,Apache 2.4.69 prefork |
| 运行时 | PHP 8.3.36 NTS |
| 解码器 | libbrotli1:amd64 1.1.0-2+b7 |
该 Linux/amd64 manifest 在发布时由 wordpress:latest 选中。当前的 latest 标签不再选中它。
实验环境使用无根 Podman 运行这个未经修改的 Docker 官方镜像。它不注入 Brotli 库。
CVE-2026-111497 提供了解码器破坏。WordPress 通过 pingback.ping 提供了未认证的获取路径。
畸形流从活动的 system 处理程序指针复制六个字节到 strtolower 处理程序。两个指针使用相同的、由 ASLR 加载的 libphp.so 基址。因此,该 profile 不需要绝对地址泄漏。
该 profile 仍然依赖于确切的 Brotli、PHP、glibc、cURL、Requests、Apache、分配器和请求历史。第一次响应使用 161 字节的头部和 23,069,816 字节的主体。源端发送 23,069,752 字节,等待一秒,然后发送最后的 64 字节。
解码器在破坏后返回错误。Requests 在 CURLE_BAD_CONTENT_ENCODING 之后不进行内容解码而重试。其第一个头部名称到达被修改的处理程序并启动固定的 Bash 命令。
应用路径需要以下条件:
/xmlrpc.php 可访问,且 pingback.ping 已启用。实验环境在没有插件或受害者端插桩的情况下创建这些条件。
generate_payloads.py 是权威的载荷来源。仓库不跟踪任何预构建的载荷二进制文件。
生成器仅使用 Python 标准库。它离线工作,不发送流量或执行命令。它根据 RFC 7932 定义 profile 常量和序列化阶段。
verify.sh 重新生成所有片段。它验证其大小、哈希、中间位偏移和单比特选择器关系。
测试主机在 amd64 上使用 WSL2 下的 Ubuntu 24.04.4。它使用无根 Podman 4.9.3、Bash 5.2.21 和 Python 3.12.3。
最低要求是 Linux amd64、无根 Podman、Bash、Python 3.8 或更高版本、ASLR 模式 2 以及标准 GNU 用户工具。保持端口 18170 和子网 93.184.218.0/24 空闲,或选择替代方案。启用强制模式的 SELinux 主机可能会拒绝绑定挂载。
在运行实验环境之前,拉取不可变镜像:
podman pull docker.io/library/wordpress@sha256:806680ba62232c4c300fb9dcc9f89d08e9e8c2bfc59837a1f58716f8648306d5
podman pull docker.io/library/wordpress@sha256:65d5f185a6f539803a177ea9cdd5ad632ae24427bea114447e67a6fcd5d8bfe8
podman pull docker.io/library/mariadb@sha256:33a6653ab666774f48818120045f9367a81cd4c54c027412fe294a7d1274e2dc
使脚本可执行,然后验证源包:
chmod +x generate_payloads.py verify.sh run.sh
./verify.sh
每种模式都使用一个新标签运行:
| 模式 | 命令 | 预期结果 |
|---|---|---|
| 正向 | ./run.sh standard | 返回 flag 并打印 PoC positive: |
| 可观测 | ./run.sh observable | 同时记录可见的 bash 和 sleep 进程 |
| 选择器 | ./run.sh selector | 重试但将 flag 排除在响应之外 |
选择器在字节偏移 1066 处改变一个比特。它是同一镜像内的因果对照,而非固定解码器对照。
可选参数依次为模式、标签、主机端口和第三个网络八位组:
./run.sh standard trial2 18171 219
运行器将证据写入 runs/<tag>/。主要文件包括:
VERDICT.txt:结果和请求计数client-request.raw 和 client-response.raw:入站线路数据origin-evidence/origin-events.json:两个出站请求和重试头oracle-match.txt:响应中的 flag 匹配payloads/identity.json:生成的配置文件元数据MANIFEST.sha256:所有证据文件的哈希值运行器使用内部 Podman 网络,并仅在 127.0.0.1 上发布 WordPress。客户端不接受目标地址。源站不接受命令输入。固定命令仅读取合成 flag。
运行器在 Apache 启动前从 Apache 环境中移除 flag 值。它还会创建新的凭据,并且仅移除当前运行的资源。
响应拆分和进程历史是漏洞利用输入。如果运行失败,请使用新标签重复未更改的配置文件。不要在运行失败后调整地址或 payload 字节。
此包不保证跨镜像摘要、PHP 构建、架构、分配器或响应调度的可移植性。不同的目标可能会停止执行或仅导致崩溃。
上游在 1.0.10、1.1.1 和 1.2.1 中修复了维护中的 Brotli 分支。发行版供应商可以向后移植该修复。请使用供应商公告和包变更日志,而不仅仅是显示的版本。