티스토리 뷰

728x90
반응형

문제 설명

Can you escape the query context and log in as admin at my super secure login page?

 

문제 설명을 보면 로그인 페이지로부터 admin 계정으로 접속하라는 것 같네요.

아래는 제공 받은 URL로 접속하면 나오는 첫 페이지입니다.

 

 

소스코드 보기를 하면 /debug 라는 경로가 있다는 것을 주석문에서 확인할 수 있는데요.

접속해보면 아래 Python Flask 의 서버 코드를 확인할 수 있습니다.

 

from flask import Flask, request, render_template, Response, url_for, g
from sqlite3 import dbapi2 as sqlite3
from functools import wraps

app = Flask(__name__)

def get_db():
	db = getattr(g, '_database', None)
	if db is None:
		db = g._database = sqlite3.connect(':memory:', isolation_level=None)
		db.row_factory = sqlite3.Row
		with app.app_context():
			db.cursor().execute('CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT, password TEXT);')
			with app.open_resource('schema.sql', mode='r') as f:
				db.cursor().executescript(f.read())
	return db

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

def query_db(query, args=(), one=False):
	try:
		with app.app_context():
			cur = get_db().execute(query, args)
			rv = [dict((cur.description[idx][0], value) for idx, value in enumerate(row)) for row in cur.fetchall()]
			return (rv[0] if rv else None) if one else rv
	except Exception as e:
		return e

	return None

@app.route('/', methods=['GET', 'POST'])
def login():
	if request.method == 'POST':
		q = "select * from users where username = '%s' AND password = '%s';" % (request.form.get('username', ''), request.form.get('password', ''))

		login = query_db(q, one=True)

		if isinstance(login, Exception):
			error = '%s : %s' % (login.__class__, login)
			return render_template('index.html', query=q, error=error, image=url_for('static', filename='images/dog.png'))

		if login is None:
			return render_template('index.html', query=q, image=url_for('static', filename='images/dog.png'))

		if login.get('username', '') == 'admin':
			return render_template('index.html', query=open('flag').read())

	return render_template('index.html')

@app.route('/debug')
def debug():
	return Response(open(__file__).read(), mimetype='text/plain')

if __name__ == '__main__':
	app.run('0.0.0.0', port=1337)

 

중요한 부분은 login 함수입니다. POST 요청으로 들어왔을 때 form 의 파라미터로 username과 password를 받습니다.

하지만 이 때 python 의 문자열 포맷팅 방식으로 sql query 문을 만들게 되는데요.

 

이렇게 되면 SQL Injection 에 취약한 코드가 만들어집니다.

그리고 SQL 구문의 결과로써 username 이 admin 인 경우에는 flag 라는 파일의 내용을 화면에 출력해준다고 하네요.

 

여기서 또 문제는 admin 의 password 마저 확인하지 않고 단순히 username 이 admin 이기만 한다면

통과되는 방식도 문제라고 할 수 있겠습니다.

물론 근본적인 원인은 파라미터를 문자열 포맷팅한 것이지만 말이죠.

 

select * from users where username = '아이디' AND password = '비밀번호';

 

SQL 구문은 위와 같습니다.

어떤한 특수문자 필터링도 존재하지 않기 때문에 아래와 같이 구문이 만들어질 수 있겠죠.

 

select * from users where username = 'admin'-- -' AND password = '비밀번호';

 

위 구문처럼 실행될 수 있도록 username 에 admin'-- - 라고 입력하고 로그인 버튼을 눌러보겠습니다.

비밀번호는 그냥 123 이라고 입력했습니다.

 

 

그랬더니 위와 같이 Flag 값이 출력된 것을 확인할 수 있었습니다.

 

- 끝 -

728x90
반응형
댓글