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 |