今日は Daily AlpacaHack の Day 3 を解きました!これはweb系の問題で、初めてのMedium難易度の問題でした。Hardじゃないし大丈夫でしょ、と思っていたらめちゃくちゃ難しくて1時間近くかかってしまいました。
そもそも何をすればいいのか・web系問題の取り組み方などがそもそも分からなくて詰まってしまったので、これについても簡単に書きたいと思います!
問題
与えられるもの
- Vanilla JS フロントエンド (frontend) と express バックエンド (backend) の最小構成のwebアプリを定義したフォルダ
- フラグが隠されているっぽい秘密のバックエンド (secret) 。この環境内にFLAGというenv変数があって、それが答えらしい
- アプリを立ち上げるための Dockerfile・composeファイル
- (後になるまで気づかなかったが) 実際このアプリが動いている場所のurl
課題
分からない!
secret内のenv変数を取り出せばいいのは分かるけど、そもそもenvファイル与えられてないんだから無理ゲーじゃね?と思って20分ほど格闘してました。ローカルでアプリ立ち上げてコード内のエンドポイントパスをsecretに変えて...とかやってたけどただ偽物のFLAGが出力されるだけで、何も進捗はありませんでした。
気づいていなかったこと
このアプリのフォルダは、「実際に公開されて動いているアプリの内部構成を説明したもの」に過ぎず、これを読み取り脆弱性を理解することで「この公開サイトに攻撃を仕掛けてFLAGを見つける」というのが課題でした。
FLAGはこの公開サイトが動いている環境内にあるのです。昨日Cryptoの問題を解いたこともあり、自分はローカルでソースコードをいじったりして何かするのかと思っていました
解く
FLAGはsecretのあるエンドポイントを呼ぶことで得られて、これをどうにか呼ぶことが目標だということだけは分かっていました。これはソースコード内でフロントエンドが path という変数をクエリパラメータから抽出している場所があったので、
{そのエンドポイントのベースパス}?{いい感じにsecretのエンドポイントを呼べるようなパス}
という形式のurlを作れば良いのかな、というのは頭をよぎりましたが、もうここまでで頭が混乱しまくっていたのでここは一旦答え見てそこから勉強しようモードになってました。
ということで、最後の部分はClaudeに考え方を教えてもらいました (ネタバレ防止のため詳細は割愛)
単純にクエリパラメータの部分にアクセスしたい secret のパスを書いたとしても、ここでは簡易的に定義されている「waf (web application firewall)」という、「APIリクエストのフィルタ機能」によってそのクエリパラメータが却下されてしまいます。
仮にwafが定義されていurl・パラメータも受けつけてしまうとすると、このように悪い考えを持った人が簡単に「悪意を持ったパラメータ」などをurlに挿入してハッキングされてしまいます。
今回の問題は、この「wafの抜け穴・脆弱性」を見つける問題でした。詳細は割愛しますが、与えられているwafの定義をよーく観察してみると、上手くクエリパラメータを構築すればwafを通り抜けつつ、secretのエンドポイントを呼ぶことができることが分かります。
こうして、半分答えは聞いてしまったものの、なんとか解くことができました。
便利そうなテクニック
-
new URL(url, base)コンストラクタは、url (相対パス) の部分を用いて、「baseのホストを上書きする」ことができる
例えば、new URL("//newhost.com/path", "https://host.com/path")は、https://newhost.com/pathに変換されます (host が newhost に上書きされている) -
このコンストラクタは、「パス正規化・ドットセグメントの除去」というものをする
これは、簡単に言うと「与えられたパスを最も簡単で統一された表現に置き換える」と言うことです。例えば、parent/child1/../child2と言うパスがあった時に、child1/../の部分は「一段階下がって、それから登り直している」ので無駄ですよね。このような部分を除去して、parent/child2のような簡単な表現に置き換えることをパスの正規化というらしいです。