---
title: Hacking de Aplicaciones Web vulnerables a SQL Injection
description: Descubre qué es la inyección SQL, cómo se explota y las medidas para prevenir esta vulnerabilidad en aplicaciones web. Protege tus datos y sistemas.
---

[Blog de Internet Security Auditors](https://blog.isecauditors.com)

# [Hacking de Aplicaciones Web vulnerables a SQL Injection](https://blog.isecauditors.com/2019/12/hacking-de-aplicaciones-web-con-sql-injection.html)

 Escrito por [Internet Security Auditors](https://blog.isecauditors.com/author/internet-security-auditors) | 17 dic 2019, 5:00:00

### **¿Qué es SQL Injection?**

 Es una vulnerabilidad que permite a un atacante realizar consultas a la base de datos de una aplicación web mediante la inyección de instrucciones SQL en los parámetros vulnerables de las peticiones.

Explotando la vulnerabilidad, un atacante podría obtener información sensible almacenada en la base de datos. Como, por ejemplo, usuarios, contraseñas, correos e información confidencial.

Además, dependiendo de los privilegios que tenga, podría borrar la base de datos, cambiar el nombre de tablas, anular transacciones o escalar privilegios y acceder al sistema. 

### **¿Por qué sucede esto?**

 El principal motivo es porqué el servidor no sanea correctamente el contenido de los parámetros introducidos por el usuario. En otras palabras, el servidor no comprueba que la información introducida por el usuario sea la correcta y no contenga código malicioso.

A continuación, se exponen algunos ejemplos. Al final se muestran más detalles de las máquinas usadas y se mencionaran otras máquinas virtuales con las que se pueden crear laboratorios para practicar.

**Ejemplo OWASP Mutillidae**   
Para ello, vamos a coger un ejemplo de una máquina vulnerable, llamada “ *OWASP Mutillidae II*”.

Se accede la web: https://192.168.0.21/mutillidae/ y, usando los menús, se accede a “ *User Info (SQL)*”:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/instrucciones-de-acceso-a-la-pagina-vulnerable-1.png?hsLang=es> |
| --- |
| **Instrucciones de acceso a la página vulnerable** |

La página tiene un panel para iniciar sesión, donde el usuario puede introducir el usuario y la contraseña:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/panel-de-login-potencialmente-vulnerable.png?hsLang=es> |
| --- |
| **Panel de login potencialmente vulnerable** |

Usando herramientas de interceptación, como Burp Suite, se puede comprobar que en la petición se envían dos parámetros: “username” y “password”. En este caso, el usuario es “test” y la contraseña “1234”:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/peticion-interceptada-con-Burp-Suite.png?hsLang=es> |
| --- |
| **Petición interceptada con Burp Suite** |

El servidor, después de procesar los datos, devuelve un mensaje de error indicando que el usuario no existe:

|  |
| --- |
| **Respuesta del servidor** |

Un indicador de que el parámetro es vulnerable a inyección de SQL es que, al introducir valores no esperados por el servidor, este devuelva un error de SQL.

En este caso, se puede probar con el parámetro “username” introduciendo una comilla simple:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/intento-inicio-sesion-con-comilla-simple.png?hsLang=es> |
| --- |
| **Intento de inicio de sesión con una comilla simple** |

El servidor devuelve un error no controlado con el siguiente mensaje:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/pagina-de-error.png?hsLang=es> |
| --- |
| **Página de error** |

Este error indica dos cosas:

1. El parámetro es potencialmente vulnerable a inyección de SQL
2. El servidor no sanea los datos introducidos por el usuario
3. La sentencia SQL que el servidor usa para procesar los datos introducidos por el usuario se parecerá a la siguiente:

SELECT \* FROM Users WHERE Username=’usuario’ AND Password=’contraseña’

¿Por qué ha devuelto el servidor un error? Suponiendo que la sentencia anterior, al introducir la comilla simple, el servidor a intentado procesar una sentencia SQL con un error en la sintaxis:

SELECT \* FROM Users WHERE Username=’’’ AND Password=’’

Volviendo al primer intento de inicio de sesión que se ha hecho, podemos suponer que el servidor ha procesado la siguiente sentencia:

SELECT \* FROM Users WHERE Username=’test’ AND Password=’1234’

Como ni el usuario ni la contraseña son correctas, la sentencia anterior devolverá 0 líneas al servidor y este muestra el error de inicio de sesión incorrecto.

Como se desconocen los usuarios que existen, se puede intentar evadir el login. Para ello, se introducen comandos SQL para que la sentencia sea TRUE y se consiga obtener acceso.   
Una manera sería usar los comandos OR juntamente con 1=1, ya que es una sentencia que siempre es verdadera.

Si se introduce ‘OR ‘1’=’1 tanto en el campo de Username como en el de Password, la sentencia SQL procesada sería la siguiente:

SELECT \* FROM Users WHERE Username=’’ OR ‘1=’1’ AND Password=’’ OR ‘1=’1’

Al inyectar los comandos SQL, aunque no se sepa el usuario ni la contraseña, se puede conseguir convertir una sentencia donde las cláusulas son FALSE a TRUE, ya que los ‘ OR ‘1’=’1 provocan que tanto la cláusula del Username como la de la Password sean verdaderas.   
La primera comilla (') deja en blanco la entrada del nombre de usuario. La declaración es actualmente falsa.

El operador "OR" indica a la instrucción que si colocamos algo después que sea “true” (verdadero), toda la consulta se convierte en true (verdadero).

Y finalmente la condición de '1'='1 que siempre se cumple, ya que 1 siempre es igual a 1.

Si se introduce en el login:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/explotando-la-inyeccion-sql.png?hsLang=es> |
| --- |
| **Explotando la inyección SQL** |

El servidor procesa los datos introducidos por el usuario sin validar y devuelve la sesión de administrador:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/explotacion-correcta.png?hsLang=es> |
| --- |
| **Explotación correcta** |

**Ejemplo bWAPP**   
Se carga la aplicación web **bWAPP** y se accede al siguiente enlace: https://192.168.0.21/bWAPP/login.php

Posteriormente se introducen las credenciales: bee/bug y se selecciona el menú **SQL Injection (Search/GET)**

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/menu-bwapp.png?hsLang=es> |
| --- |
| **Menú bWAPP** |

La página potencialmente vulnerable esta formada por una tabla y un campo de búsqueda. Si se realiza una búsqueda vacía, la aplicación lista unas películas:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/listado-de-peliculas.png?hsLang=es> |
| --- |
| **Listado de películas** |

Si se introduce una comilla para intentar provocar un error, el servidor devuelve lo siguiente:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/error-devuelto-por-el-servidor.png?hsLang=es> |
| --- |
| **Error devuelto por el servidor** |

El mensaje indica que la base de datos usada por la aplicación es MySQL. Además, el error indica que el parámetro es potencialmente vulnerable.

Podríamos hacer la suposición de que la sentencia que usa el servidor es similar a la siguiente:

SELECT \* FROM movies WHERE title LIKE ‘title’

El objetivo es averiguar cómo se llama la base de datos actual y que tablas tiene.   
Para ello se realizar la consulta **union**:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/inyeccion-sql.png?hsLang=es> |
| --- |
| **Inyección SQL** |

Como la aplicación pasa los parámetros por GET (se puede ver el parámetro y su contenido en la barra del navegador), no es necesario usar un proxy.

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/inyeccion-sql_2.png?hsLang=es> |
| --- |
| **Inyección SQL** |

La sentencia SQL sería la siguiente:

SELECT \* FROM movies WHERE title LIKE ‘’’ union select 1,2 -- -

El objetivo de esta inyección es adivinar cuantas tablas existen en la base de datos. Manualmente, se tiene que ir incrementando el número hasta dar con el correcto:

- https://192.168.0.21/bWAPP/sqli\_1.php?title=' union select 1,2 -- -
- https://192.168.0.21/bWAPP/sqli\_1.php?title=' union select 1,2,3 -- -
- https://192.168.0.21/bWAPP/sqli\_1.php?title=' union select 1,2,3,4 -- -
- https://192.168.0.21/bWAPP/sqli\_1.php?title=' union select 1,2,3,4,5 -- -
- https://192.168.0.21/bWAPP/sqli\_1.php?title=' union select 1,2,3,4,5,6 -- -
- https://192.168.0.21/bWAPP/sqli\_1.php?title=' union select 1,2,3,4,5,6,7 -- -

Al llegar a ' union select 1,2,3,4,5,6,7 la aplicación no muestra ningún error y a su vez muestra la siguiente tabla con sus correspondientes datos:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/inyeccion-correcta.png?hsLang=es> |
| --- |
| **Inyección correcta** |

Si se prefiere que no salgan todas las películas, se debe realizar la búsqueda con un título no existente:

- https://192.168.0.21/bWAPP/sqli\_1.php?title=hola' union select 1,2,3,4,5,6,7 -- -

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/inyeccion-correcta-sin-peliculas.png?hsLang=es> |
| --- |
| **Inyección correcta, sin películas** |

Al tener las consultas numeradas del 1 al 7 se pueden identificar como aparecen en la tabla:

- Title = valor/atributo 2
- Release = valor/atributo 3
- Character = valor/atributo 5
- Genre = valor/atributo 4

 A partir de ahora, cuando se realicen consultas con el union, se debe tener en cuenta los campos visibles en la tabla. En este caso el 2, 3, 4 y 5.

A continuación, se intenta conseguir el nombre de la base de datos. Para ello, se substituye uno de los campos visibles por **database()**.

- https://192.168.0.21/bWAPP/sqli\_1.php?title=**hola**' union select 1,2,3,4,**database()**,6,7-- -

 El servidor devuelve el nombre de la base de datos en el campo donde antes había el número 5:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/inyeccion-correcta-bwapp.png?hsLang=es> |
| --- |
| **Inyección correcta** |

Otra cosa que se puede conseguir es la versión del servidor, se puede usar **version()**.

- https://192.168.0.21/bWAPP/sqli\_1.php?title=hola' union select 1,2,3,4,**version()**,6,7-- -

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/version-del-servidor.png?hsLang=es> |
| --- |
| **Versión del servidor** |

Una vez se ha obtenido información del servidor, ya se puede ir un paso más allá y listar las tablas que tiene la base de datos.

Para ello se puede usar **table\_name**:   
https://192.168.0.21/bWAPP/sqli\_1.php?title=hola' union select 1,2,3,4,**table\_name**,6,7 from **information\_schema.tables -- -**

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/tablas-de-la-base-de-datos.png?hsLang=es> |
| --- |
| **Tablas de la base de datos** |

A continuación, se añade la condición l **where**: para ver el contenido de **table\_schema**.

https://192.168.0.21/bWAPP/sqli\_1.php?title=hola' union select 1,2,3,4,table\_name,6,7 from **information\_schema.tables where table\_schema=database() -- -**

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/tablas-de-la-aplicacion.png?hsLang=es> |
| --- |
| **Tablas de la aplicación** |

Una tabla interesante es la de usuarios, ya que se pueden conseguir usuarios y contraseñas para poder acceder a la base de datos posteriormente.

https://192.168.0.21/bWAPP/sqli\_1.php?title=hola' union all select 1,column\_name,3,4,5,6,7 from information\_schema.**columns** where table\_name='**users**' and table\_schema=database() -- -

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/columnas-de-la-tabla-users.png?hsLang=es> |
| --- |
| **Columnas de la tabla users** |

La tabla users tiene los siguientes campos interesantes: **login, password, email y secret**. Para ver su contenido, se ejecuta la siguiente comanda:

https://192.168.0.21/bWAPP/sqli\_1.php?title=hola' union select 1,**login,password,email,secret**,6,7 from users **where password<>''-'**

*Nota: en la consulta **where** se indica que muestre las filas que tengan el campo de contraseña no vacío.*

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/contenido-tabla-users.png?hsLang=es> |
| --- |
| **Contenido de la tabla users** |

Se han obtenido dos usuarios con los respectivos hashes de sus contraseñas.

Lo primero que se tiene que hacer al obtener hashes, es averiguar de que tipo son.   
Una herramienta es que viene por defecto en Kali Linux es **hash-identifier**:

- https://github.com/blackploit/hash-identifier

Si se introduce el hash, muestra que el hash es del tipo SHA-1:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/hash-identifier.png?hsLang=es> |
| --- |
| **Hash-identifier** |

Otro método, que requiere conexión a internet, es la página **CyberChef**:

- https://gchq.github.io/CyberChef/

 Arrastramos “Analyse hash” en **Recipe:**

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/menu-analyse-hash.png?hsLang=es> |
| --- |
| **Menú Analyse hash** |

En el apartado **Input** pegamos el **hash**. En el **Output** la web indicará el tipo de hash es. Igual que hash-identifier, CyberChef indica que es **SHA-1**.

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/cyberchef-identificando-el-hash.png?hsLang=es> |
| --- |
| **CyberChef identificando el hash** |

Por último, otro método para verificar el hash, es usar directamente el buscador [https://duckduckgo.com/](https://duckduckgo.com/)

Se escribe hash y a continuación se pega el hash obtenido:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/buscador-DuckDuckGo.png?hsLang=es> |
| --- |
| **Buscador DuckDuckGo** |

Igual que los dos anteriores, DuckDuckGo indica que el hash es SHA-1:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/tipo-de-hash.png?hsLang=es> |
| --- |
| **Tipo de hash** |

Una vez obtenido el tipo, se intentar descifrar. Para ello se pueden usar distintas técnicas.   
La más cómoda es poner el hash en los buscadores. Es muy posible que alguien lo ha descifrado.   
Otra opción es un descifrador online, hay muchísimos, un ejemplo seria el **Descifrador online**: [https://sha1.gromweb.com/ ](https://sha1.gromweb.com/)

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/descifrador-online.png?hsLang=es> |
| --- |
| **Descifrador online** |

Si no se dispone de Internet, se puede usar John the Ripper. Es un programa de criptografía el cual aplica fuerza bruta para descifrar contraseñas. Viene por defecto en Kali Linux, pero se puede descargar de la siguiente web [https://www.openwall.com/john/](https://www.openwall.com/john/).

Lo primero, antes de utilizar John the Ripper, es guardar el hash en un archivo de texto. Para el ejemplo, se ha guardado en el fichero “hash.txt”.

Se puede ejecutar **John the Ripper** de la siguiente forma:   
Windows desde cmd.exe

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/john-the-ripper-en-windows.png?hsLang=es> |
| --- |
| **John the Ripper en Windows** |

Linux desde la terminal

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/john-the-ripper-kali-linux.png?hsLang=es> |
| --- |
| **John the Ripper Kali Linux** |

**John.exe** es para ejecutar el programa y en el caso de la Kali Linux, con poner **John** ya funciona.   
Con el flag **--format=raw-sha1** se indica el tipo de hash que es. En este caso raw-sha1.   
Finalmente, se indica la ruta donde se ha almacenado el hash: “c:\\user\\miusuario\\hash.txt” en el caso de Windows o /Root/Desktop/hash.txt en el caso de Kali Linux.

Después de realizar un ataque de fuerza bruta, **John the Ripper** nos indica que la contraseña es “bug”. Por lo tanto, sabemos que la contraseña tanto del usuario “ **bee**” como “ **A.I.M**” es “ ***bug***”, ya que tenían el mismo hash.

En la página inicial se puede verificar que la contraseña es la correcta:

| <https://blog.isecauditors.com/hubfs/Imported_Blog_Media/verificacion-de-contrasena.png?hsLang=es> |
| --- |
| **Verificación de contraseña** |

### **¿Cómo prevenirlo?**

 El servidor debería:

1. Escapar los caracteres especiales utilizados en las consultas SQL.
2. Delimitar los valores de las consultas para mitigar ataques de inyección SQL
3. Verificar siempre los datos que introduce el usuario.
4. Asignar mínimos privilegios al usuario que conectará con la base de datos
5. Antes de pasar a producción la aplicación, se debería realizar auditorías de seguridad para verificar que los parámetros no son vulnerables

**Referencias:**

- [https://www.owasp.org/index.php/Inyecci%C3%B3n\_SQL](https://www.owasp.org/index.php/Inyecci%C3%B3n_SQL)
- [https://www.owasp.org/index.php/Top\_10\_2007-Fallas\_de\_Inyecci%C3%B3n](https://www.owasp.org/index.php/Top_10_2007-Fallas_de_Inyecci%C3%B3n)
- [https://geeks.ms/gtorres/2010/10/29/tips-para-evitar-sql-injection/](https://geeks.ms/gtorres/2010/10/29/tips-para-evitar-sql-injection/)
- [https://www.genbeta.com/desarrollo/evita-los-ataques-de-inyeccion-de-sql](https://www.genbeta.com/desarrollo/evita-los-ataques-de-inyeccion-de-sql)

***Autor: Héctor Berrocal - CCNA, CEH, ITILF, MCP***   
***Dtpo. Auditoría***

 

[Ver post completo](https://blog.isecauditors.com/2019/12/hacking-de-aplicaciones-web-con-sql-injection.html)

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Internet Security Auditors"
  },
  "dateModified" : "2026-08-07T12:26:45.234Z",
  "datePublished" : "2019-12-17T05:00:00Z",
  "headline" : "Hacking de Aplicaciones Web vulnerables a SQL Injection",
  "image" : {
    "@type" : "ImageObject",
    "height" : 330,
    "url" : "https://25534193.fs1.hubspotusercontent-eu1.net/hubfs/25534193/Imported_Blog_Media/instrucciones-de-acceso-a-la-pagina-vulnerable.png",
    "width" : 629
  },
  "mainEntityOfPage" : "https://blog.isecauditors.com/2019/12/hacking-de-aplicaciones-web-con-sql-injection.html",
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "height" : 60,
      "url" : "/hs/hsstatic/content_shared_assets/static-1.4092/img/default-amp-logo.png",
      "width" : 60
    },
    "name" : "Blog"
  }
}
```