今回の問題
picoCTFより"Local Authority"を解いていきます。
実際に解いていく
1. 問題文にあるWebサイトに移動する
ふむふむ。ユーザー名とパスワードには、文字と数字のみが使用できるみたいです。
2. 適当な答えを入力してみる
正しいユーザー名とパスワードがわからないので、とりあえず適当な答えを入力して、Webサイトの動きを確認してみます。
もちろん、ログイン失敗です。
これ以上Webページも進めそうにないですね。
3. Webインスペクタを開く
Webインスペクタを開いて、ソースコードを確認します。
すると、HTMLファイルの中にsecure.jsを発見!!
怪しいですな。見てみましょう。
4. secure.jsを確認する
[Elements]タブから[Sources]タブに移動します。
そして、先ほどのsecure.jsの中身を確認します。
なんと、ユーザー名とパスワードを発見しました。
5. 見つけたユーザー名とパスワードを入力する
ユーザー名とパスワードを入力して...
FLAG獲得!!やったーーー!!
...と、喜びたいところですが結局この問題は何だったのか?疑問が残ります。
もう少し考えてみる
さて、ここからが本題です。この問題のカラクリを解き明かしていきますよ。
1. まず初期状態の[Sources]タブを確認してみる
[Sources]タブには、index.htmlとstyle.cssの2つのファイルがありました。
このうち、style.cssはWebサイトの背景色について3行のみの記述でした。
index.htmlの16行目のformタグに注目!
login.phpというURLに、POSTメソッドで入力されたユーザー名とパスワードを送信しています。
2. 次に適当な答えを送信後の[Network]タブを見てみる
サーバーとのやりとりは[Network]タブを見ることで確認できます。[Network]タブは、Webブラウザとサーバーの間でどんな通信が行われたかをリアルタイムで見られる機能です。せっかくなので見てみましょう。
login.phpにリクエストを送信、レスポンスとしてstyle.cssとsecure.jsが返ってきたことを確認できました。
3. [Sources]タブを確認する
一目見てわかるように、HTMLが変化しています。
そして、HTMLファイルにも書かれているように、style.cssとsecure.jsも新しく返ってきています。
4. 核心に迫る
ここで、この問題の重要なポイントに気づきました。
それは、パスワード検証をサーバー側ではなく、クライアント側で行っていることです!
本来、パスワード検証は「サーバー側で受け取って、データベースと照合して成功→セッション発行、失敗→エラー」のようにすべきです。
しかし、今回のようにクライアント側で行ってしまうと、誰でも見られる&改ざんできてしまいます。まさに、これが今回の脆弱性でした。この脆弱性を利用してFLAGを獲得していたのです。
5. FLAG獲得までの流れ
変わった後のHTMLファイルを見てください。
windowオブジェクトに、自分が入力したユーザ名とパスワードが保存されています。
そして、その2つを利用してsecure.jsのcheckPasswordを実行→パスワード検証をしています。
(繰り返しになりますが、今回はパスワード検証がサーバー側ではなく、クライアント側で行われていることがわかるかと思います。)
パスワード検証の結果が
不正解→そのまま現在のページ(Log In Failedと書かれている)で処理が終わる。
正解→login.phpからadmin.phpにリクエストを送信する。
admin.phpにリクエストが送信されると、FLAGが書かれたHTMLに変わります。
まとめ
システムの流れはざっとこんな感じになります
- ユーザー名・パスワードを入力
- login.phpに送信
- ログイン失敗のhtml, secure.java, style.cssが返ってくる
- 同時に、入力された答えが正しいかどうかをチェック(secure.javaをもとに、クライアント側でチェック)
- 不正解ならば、ここで終了
- 正解ならば、admin.phpにリクエストを送信
- FLAGの書いてあるhtml, style.cssが返ってくる
以上です。お疲れ様でした。
補足
・JavaScriptは基本、クライアント側で実行されます。つまり、JavaScriptに書いた時点で誰でも読めるかもということです。