War_Game

Dreamhack: simple_sqli_chatgpt

Sono Hacking Study 2024. 10. 1. 19:55

1. 문제 설명

#!/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), userlevel integer);')
    db.execute(f'insert into users(userid, userpassword, userlevel) values ("guest", "guest", 0), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}", 0);')
    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:
        userlevel = request.form.get('userlevel')
        res = query_db(f"select * from users where userlevel='{userlevel}'")
        if res:
            userid = res[0]
            userlevel = res[2]
            print(userid, userlevel)
            if userid == 'admin' and userlevel == 0:
                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. 문제 풀이

DATABASE = "database.db"
if os.path.exists(DATABASE) == False:
    db = sqlite3.connect(DATABASE)
    db.execute('create table users(userid char(100), userpassword char(100), userlevel integer);')
    db.execute(f'insert into users(userid, userpassword, userlevel) values ("guest", "guest", 0), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}", 0);')
    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

userid, userpassword, userlevel: guest-guest-0, admin-랜덤값-0으로 되어있다.

@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:
        userlevel = request.form.get('userlevel')
        res = query_db(f"select * from users where userlevel='{userlevel}'")
        if res:
            userid = res[0]
            userlevel = res[2]
            print(userid, userlevel)
            if userid == 'admin' and userlevel == 0:
                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>'

 -> userlevel = request.form.get('userlevel'): 사용자가 입력한 userlevel 데이터를 가져온다.

-> res = query_db(f"select * from users where userlevel='{userlevel}'"): query_db 함수는 DB에 쿼리를 실행하는 함수이며, 가져온 userlevel을 바탕으로 쿼리문을 실행한다.

->  res는 DB 쿼리 결과를 나타낸다.

-> query_db는 리스트나 튜플 형태로 결과를 반환한다. 아까 userid, userpassword, userlevel 였으니 if문이 저렇게 쓰여지는거다.

-> 사용자로부터 입력받은 userlevel을 직접 SQL 쿼리문에 삽입하기 때문에, SQL Injection 공격이 진행될 수 있다.

 

userid == admin 그리고 userlevel == 0이어야 플래그가 반환된다. 쿼리문에 따라 우리가 0을 입력하면 guest와 admin 둘 다 조회될거다. 이후 조건문에 의해 admin이 아니게 되는것이다.

select * from users where userlevel='{userlevel}'
//이후 조건
if res:
            userid = res[0]
            userlevel = res[2]
            print(userid, userlevel)
            if userid == 'admin' and userlevel == 0:
                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>'

3. 취약점

<시도했지만 실패한 것들>

1) 0'or'1 -> 1이 문자열로 해석되기 때문에 논리적 참이 아님. 생각을 잘 못했다.

2) 0'or'1'='1 -> 참을 만들어, DB의 모든 데이터를 반환하는 것을 생각했음. 하지만 실패

3) 0'or userid='admin -> 이것 또한 실패였음. 왜지

 

<성공한 것들>

1) 0' AND userid='admin -> where절 뒤에 AND를 사용해 조건을 더 붙이려고 했음

2) 0'and userid='admin -> or 조건일때는 안 됬는데 이거는 된다.....?

3) 0' union select * from users where userid='admin -> sql에서 사용되는 union을 사용해 쿼리문 확장

 

 

 

'War_Game' 카테고리의 다른 글

Lord of Injection: germiln  (0) 2024.10.01
root_me: CSRF-0  (0) 2024.09.25