해킹공부/Dreamhack

[Dreamhack] simple_sqli

밍구21 2022. 9. 22. 19:06

문제 정보
로그인 서비스입니다.
SQL INJECTION 취약점을 통해 플래그를 획득하세요. 플래그는 flag.txt, FLAG 변수에 있습니다.

 

문제페이지이다. 문제 정보처럼 로그인 서비스만 구축되어 있다.

 

 

 

#!/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)

위는 문제 코드 전문이다. 

코드에서 자세히 볼 정보는 총 2개이다.

1. userid, userpassword컬럼에 각각 guest,guest인 계정 하나, userid는 admin인 계정은  password를 모른다.

2. Flag는 userid가 admin일 때 출력되다. userid는 로그인페이지에서 아이디로 입력하면 userid 변수로 들어가고 이게 쿼리문으로 들어간다.

 

 

 

 

 

adm

admin 계정의 비밀번호를 모르므로 1번 정보를 이용해 공개되어있는 guest계정으로 로그인 해준다.

 

 

 

alert 창이 뜨고 확인을 누르면 이전 로그인페이지로 돌아가는 것을 알 수 있다.

 

 

 

이번엔 2번 정보를 이용해서 로그인을 해보도록 하겠다. 로그인에 사용되는 쿼리문은 아래와 같다.

 

query_db(f' select * from users where userid="{userid}" and userpassword="{userpassword}" ')

 

내가 아는 정보를 파란색, 내가 모르는 정보를 빨간색으로 칠하였다. userid가 userid가 admin이고 해당 쿼리문이 참이면 flag를 얻을 수 있따. 그러니 내가 아는 정보에서 쿼리문을 끝낼 수 있도록 입력해준다.

 

userid 값으로 admin"--를 입력하면 된다.

userid="까지 이미 코드에 포함되어 있으므로 userid 변수에는 내가 입력하려는 admin을 입력하고 " 큰 따옴표를 닫아준 뒤 코드 뒤에 이어지는 " and userpassword~~~ 부분을 주석처리 해주기 위해 --을 붙여주는 것이다.

 

userpassword가 들어가는 쿼리문 부분은 주석처리 해주었으므로 비밀번호는 아무값이나 친다.

 

 

 

위 같이 플래그를 얻을 수 있었다.

 

db query문 기본 문법을 배울 수 있는 문제였다.