uid에 문자를 넣으면 상단의 '{uid}' 부분이 바뀐다.
import os
from flask import Flask, request
from flask_mysqldb import MySQL
app = Flask(__name__)
app.config['MYSQL_HOST'] = os.environ.get('MYSQL_HOST', 'localhost')
app.config['MYSQL_USER'] = os.environ.get('MYSQL_USER', 'user')
app.config['MYSQL_PASSWORD'] = os.environ.get('MYSQL_PASSWORD', 'pass')
app.config['MYSQL_DB'] = os.environ.get('MYSQL_DB', 'users')
mysql = MySQL(app)
template ='''
<pre style="font-size:200%">SELECT * FROM user WHERE uid='{uid}';</pre><hr/>
<form>
<input tyupe='text' name='uid' placeholder='uid'>
<input type='submit' value='submit'>
</form>
'''
@app.route('/', methods=['POST', 'GET'])
def index():
uid = request.args.get('uid')
if uid:
try:
cur = mysql.connection.cursor()
cur.execute(f"SELECT * FROM user WHERE uid='{uid}';")
return template.format(uid=uid)
except Exception as e:
return str(e)
else:
return template
if __name__ == '__main__':
app.run(host='0.0.0.0')
Error_based SQLI의 app.py이다.
CREATE DATABASE user_db CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON user_db.* TO 'dbuser'@'localhost' IDENTIFIED BY 'dbpass';
USE `user_db`;
CREATE TABLE users (
idx int auto_increment primary key,
uid varchar(128) not null,
upw varchar(128) not null
);
INSERT INTO users (uid, upw) values ('admin', 'DH{**FLAG**}');
INSERT INTO users (uid, upw) values ('guest', 'guest');
INSERT INTO users (uid, upw) values ('test', 'test');
FLUSH PRIVILEGES;
위는 init.sql이다.
이를 표로 나타내면
primary_key | uid | upw |
---|---|---|
0 | 'admin' | '{flag}' |
1 | 'guest' | 'guest' |
2 | 'test' | 'test' |
여기서 중요한 부분은 app.py에서 try문에
cur.execute(f"SELECT * FROM user WHERE uid='{uid}';")
이 부분이 중요하다.
우리는 uid부분에 올바르지 못한 값을 넣어 SQL Injection 공격을 시행할 것이다.
우선 나는 아래와 같이 값을 넣었었다.
- admin' or SELECT extractvalue(1, concat(0x3a,(SELECT upw FROM user WHERE uid = 'admin')));--
(아마 앞에 SELECT가 있어서 굳이 넣을 필요가 없었던 것 같았다.)
일단 이를 통해서 MariaDB라는 것을 알았지만, 큰 소득은 없었다. 그래서 나는 다른 분의 블로그에서 어떻게 해야할지를 찾아보았다.
이 문제는 Error based SQL Injection. 에러를 일으켜 그 에러를 통해 SQL Injection 공격을 하는 방법이다. 이 때 에러를 일으켜서 Injection을 할 수 있는 함수는 몇개가 있다. (내가 뭣도 모르고 써서 에러를 일으킨 것과는 다르다.)
이 떄, extractvalue()라는 함수를 사용해 에러를 일으킨다.
extractvalue()라는 함수에 대해서 알아보자.
extractvalue(xml_frag, xpath_expr)
xml_frag에 xpath_expr과 xpath가 일치하는 xml 노드를 반환하는 함수이다. xml_frag에서 xpath_expr 조건식에 따른 xml 노드를 반환해주는 함수이다. 아래와 같은 예시이면 이해하기 쉽다.
고의로 오류를 내고, xpath_expr을 출력하게 되는데 이때 명령어를 삽입해 원하는 정보를 얻게 하는 방법이다.
1' and extractvalue(1, concat(0x3a, version()));--
이 때 xpath_expr 조건식이 올바르다면 xml 노드를 반환해주겠지만, 오류가 나게 된다. 이때, MariaDB는 해당 문장에서 오류가 났다고 에러를 띄우게 되는데 이때 'concat(0x3a, version())'의 명령어가 실행이 되면서 아래와 같이 띄우게 되는 것이다.
1’ and extractvalue(concat(0x3a, (SELECT concat(uid, 0x3a, upw) FROM user LIMIT 0,1)));--
select concat(uid, 0x3a, upw) FROM user LIMIT 0,1이 실행되었음을 알 수 있다.
1’ and extractvalue(concat(0x3a, (SELECT substr(upw, 20, 25) FROM user LIMIT 0,1)));--
나머지 flag값도 나온 것을 알 수 있다.
참조: