본문 바로가기

Dreamhack/웹해킹

[LEVEL 1] simple_sqli

웹 서비스에 접속하면 상단에 Simple SQLi라는 메뉴바와 함께 로그인 링크가 보이는 초기 화면이 나타납니다.

Login을 누르게 되면 로그인 화면이 보이게됩니다. 

화면에서 제공되는 입력란에 guest라는 아이디와 guest 비밀번호를 입력하여 초기 접근을 시도하였습니다.

guest / guest 계정으로 로그인을 시도하자, 화면에 'hello guest'라는 알림창이 출력되는 것을 확인할 수 있었습니다.

 

자세히 알아보기 위해서 이제 문제 파일을 살펴보겠습니다.

app.py

#!/usr/bin/python3
from flask import Flask, request, render_template, g
import sqlite3
import os
import binascii

app = Flask(__name__)
app.secret_key = os.urandom(32)

try:
    FLAG = open('./flag.txt', 'r').read()
except:
    FLAG = '[**FLAG**]'

DATABASE = "database.db"
if os.path.exists(DATABASE) == False:
    db = sqlite3.connect(DATABASE)
    db.execute('create table users(userid char(100), userpassword char(100));')
    db.execute(f'insert into users(userid, userpassword) values ("guest", "guest"), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}");')
    db.commit()
    db.close()

def get_db():
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = sqlite3.connect(DATABASE)
    db.row_factory = sqlite3.Row
    return db

def query_db(query, one=True):
    cur = get_db().execute(query)
    rv = cur.fetchall()
    cur.close()
    return (rv[0] if rv else None) if one else rv

@app.teardown_appcontext
def close_connection(exception):
    db = getattr(g, '_database', None)
    if db is not None:
        db.close()

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        userid = request.form.get('userid')
        userpassword = request.form.get('userpassword')
        res = query_db(f'select * from users where userid="{userid}" and userpassword="{userpassword}"')
        if res:
            userid = res[0]
            if userid == 'admin':
                return f'hello {userid} flag is {FLAG}'
            return f'<script>alert("hello {userid}");history.go(-1);</script>'
        return '<script>alert("wrong");history.go(-1);</script>'

app.run(host='0.0.0.0', port=8000)

 

여기를 보시게 되면 사용자의 입력을 문자열 보간(f-string) 으로 그대로 SQL에 삽입하고 있으므로 문제가 되는 코드입니다.

userid = request.form.get('userid')
userpassword = request.form.get('userpassword')
res = query_db(f'select * from users where userid="{userid}" and userpassword="{userpassword}"')

 

 

  • 필터링/바인딩 없음. 작은따옴표가 아닌 큰따옴표를 쓰고 있지만, 여기서는 그대로 문자열 경계로 사용되고 있어 따옴표 닫기 + 주석 패턴이 유효함.

그래서 저는 SQL Injection 공격에서 가장 기본적이면서도 널리 알려진 인증 우회 페이로드인 admin" --를 입력하여, 비밀번호 검증 절차를 무력화하고 관리자 계정에 접근을 시도하였습니다.

 

📝 페이로드 원리

일반적인 로그인 쿼리는 아래와 같은 구조입니다:

SELECT * FROM users WHERE userid="입력값" AND password="입력값";

 

여기서 userid에 admin" --를 넣으면 쿼리가 이렇게 변합니다:

SELECT * FROM users WHERE userid="admin" -- " AND password="...";
  • -- 이후의 내용은 주석 처리되어 무시되므로, 비밀번호 조건 자체가 사라집니다.
  • 결국 userid="admin" 조건만 확인하게 되어, 비밀번호 없이 관리자 계정으로 로그인할 수 있습니다.

결과적으로 admin" -- 페이로드를 통해 로그인 우회를 성공하였고, 최종적으로 플래그를 획득할 수 있었습니다.

'Dreamhack > 웹해킹' 카테고리의 다른 글

[LEVEL 1] xss-1  (0) 2025.09.03
[LEVEL 1] csrf-1  (0) 2025.09.03
[LEVEL 1] proxy-1  (1) 2025.08.31
[LEVEL 1] Disgusting Ads  (0) 2025.08.28
[🌱Beginner] php7cmp4re  (0) 2025.08.27