
Neutralización inadecuada de elementos especiales utilizados en un comando SQL (“Inyección SQL”) en los dispositivos de la serie SonicWall SMA1000 permite que un atacante remoto autenticado con privilegios de administrador de solo lectura escale privilegios hasta administrador principal.
Una vulnerabilidad de inyección SQL ciega posterior a la autenticación en la consola de gestión de SonicWall SMA 8200v (puerto 8443) permite que cualquier administrador autenticado — incluidos los administradores de solo lectura con privilegios bajos — extraiga el hash SHA-512 de la contraseña del administrador principal del archivo de configuración del dispositivo. Debido a que SonicWall utiliza la misma credencial tanto para el administrador de la consola de gestión como para la cuenta root del sistema operativo, descifrar este hash otorga acceso completo a nivel root al dispositivo.
Clasificación: Escalada de Privilegios (Administrador de bajo privilegio a Root) CVSS 3.1: 7.2 (Alta) — AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H Complejidad del Ataque: Baja (el PoC automatizado completa la extracción en ~60-90 minutos) Requisitos previos: Cualquier credencial válida de la consola de gestión (incluidas las cuentas de solo lectura)
| Componente | Detalles |
|---|---|
| Plataforma | SonicWall SMA 8200v (dispositivo virtual) |
| Firmware | 12.5.0-02283 (confirmado); probablemente todos los 12.x |
| Servicio | Consola de Gestión — Jetty + Struts 2 (puerto 8443) |
| Endpoint | POST /activeUsers.action |
| Parámetros Vulnerables | realmFilter, communityFilter (entre parámetros) |
| Clase de Causa Raíz | com.aventail.mgmt.sql.Sql.safeParam() |
| Base de Datos | MariaDB 10.11.14, usuario DbAdmin (TODOS LOS PRIVILEGIOS + FILE) |
| Archivo Objetivo | /usr/local/app/mgmt-server/datastore/active/sysconf/avconfig.xml |
La consola de gestión construye consultas SQL para el panel de Usuarios Activos mediante un método auxiliar safeParam() en la clase com.aventail.mgmt.sql.Sql. Este método escapa las comillas simples (') y las comillas dobles (") duplicándolas, pero no escapa los caracteres de barra invertida (\).
En MySQL/MariaDB, la barra invertida es el carácter de escape predeterminado dentro de los literales de cadena. La secuencia \' hace que la base de datos interprete la comilla simple como un carácter literal en lugar de un terminador de cadena. Esto significa que una barra invertida al final de un valor de parámetro escapará la comilla de cierre que la aplicación añade, provocando que el literal de cadena SQL se extienda hacia la sintaxis adyacente.
Brecha de saneamiento:```
Input: test
safeParam output: test\ (backslash NOT escaped)
In SQL: ... rt.name='test') AND (ct.name='...'
^^ backslash escapes the closing quote
El `'` de cierre después de `test\` se consume como un carácter de comilla literal dentro de la cadena, por lo que el literal de cadena SQL se extiende a través del límite `) AND (ct.name='` y hacia el valor del siguiente parámetro, donde se puede inyectar SQL controlado por el atacante.
### 3.2 Mecánica de Inyección: Técnica de Parámetros Cruzados
El endpoint `activeUsers.action` acepta múltiples parámetros de filtro que se interpolan en una única cláusula WHERE de SQL. Los parámetros relevantes son `realmFilter` y `communityFilter`, que aparecen en una consulta estructurada aproximadamente como:```sql
SELECT ... FROM ...
WHERE ...
AND (rt.name='<realmFilter>')
AND (ct.name='<communityFilter>')
...
Configuración de parámetros de ataque:
| Parámetro | Valor | Propósito |
|---|---|---|
realmFilter | test\ | La barra invertida final escapa la comilla de cierre, extendiendo el literal de cadena a través del límite AND |
communityFilter | )) OR (SELECT IF(<condition>,SLEEP(N),0))-- x | Cierra los paréntesis abiertos, inyecta SLEEP condicional, comenta el resto |
SQL resultante después de la interpolación:```sql WHERE ... AND (rt.name='test') AND (ct.name=' )) OR (SELECT IF(,SLEEP(N),0))-- x')
Desglosando esto:
1. `rt.name='test\')` — la `\` escapa la `'`, por lo que la cadena no se cierra aquí
2. `AND (ct.name='` — se convierte en parte del valor de la cadena (texto literal)
3. La cadena finalmente se cierra en la siguiente `'` (del inicio de `communityFilter`)
4. `))` — cierra los dos paréntesis abiertos de la estructura de la cláusula WHERE
5. `OR (SELECT IF(...))` — inyecta la condición de inyección SQL ciega
6. `-- x` — comenta el SQL restante (`')` y otras cláusulas)
### 3.3 Extracción ciega basada en tiempo
Dado que el manejo de errores de Struts 2 de la aplicación captura las excepciones SQL de forma elegante (siempre devolviendo HTTP 200 con el mismo contenido de página independientemente del éxito o fracaso de la consulta), los métodos de extracción basados en errores y en UNION no son viables. La inyección se explota mediante la técnica ciega basada en tiempo:
- **Condición VERDADERA:** `IF(<condición>, SLEEP(N), 0)` — la respuesta se retrasa por la duración de SLEEP multiplicada por el número de filas del conjunto de resultados
- **Condición FALSA:** Sin SLEEP — la respuesta regresa en ~200-500ms
La función SLEEP se ejecuta por fila en la evaluación de la cláusula WHERE. Con una tabla de monitoreo típica que contiene 30-300+ filas, incluso un valor SLEEP pequeño (p. ej., 0.3s) produce un retraso claramente distinguible (10-100s para VERDADERO vs. <1s para FALSO).
Cada carácter de los datos objetivo se extrae mediante búsqueda binaria sobre el rango ASCII:```sql
ORD(SUBSTRING((<extraction_expr>), <position>, 1)) > <midpoint>
Esto requiere un máximo de 7 solicitudes por carácter (log2(128) = 7), lo que da un total de ~686 solicitudes para un hash SHA-512 de 98 caracteres.
PRIVILEGE ESCALATION CHAIN
============================================================================
[1] Authenticate Low-privilege admin (e.g., "readonly") | authenticates to management console on port 8443 | using "Local Authentication" realm v [2] SQL Injection Cross-parameter blind SQLi via activeUsers.action | realmFilter backslash + communityFilter payload | Condition: IF(, SLEEP(N), 0) v [3] LOAD_FILE() MariaDB DbAdmin user has FILE privilege | secure_file_priv=NULL does NOT block reads | avconfig.xml is group-readable (mode 664) v [4] Locate Hash LOCATE('consoleMode', file) anchors to admin section | LOCATE('', file, anchor) finds hash element | SUBSTRING + SUBSTRING_INDEX extracts hash value v [5] Extract Hash Binary search extracts hash char-by-char | ~98 chars * ~7 requests = ~686 requests | Output: $6$$ (SHA-512 crypt) v [6] Crack Hash hashcat -m 1800 / john --format=sha512crypt | Admin password = Root SSH password (by design) v [7] Full Compromise SSH as root, management console as admin Complete appliance takeover
### 4.2 Paso 1: Autenticación
La consola de gestión SMA admite dos dominios de autenticación: