やられアプリ BadTodo - 2 ZAPでのスキャン

初回。BadTodoのインストールとBurpSuiteなどの初期設定について。
やられアプリ BadTodo - 1 準備 - demandosigno

今回は、手動診断の目安にするため OWASP ZAP を使って BadTodoを一通りスキャンするところまで行います。

事前準備

Dockerコンテナの起動

BurpSuiteの起動

BurpSuite組み込みブラウザでBad Todoを開く

書いておいてなんですが、これ以降の作業だけでかなり手間がかかります。次回以降の検査に必須というわけではないので後に回して次の項目に行くこともお勧めします。
やられアプリ BadTodo - 3.1 SQLインジェクション 認証の回避 - demandosigno

手作業でのクローリング

サイトをクローリング(すべてのページと全リクエストを漏れなく見て回ること)してリクエスト一覧をまとめ、検査対象を決める(重複したリクエストは省くなど)。
また一通り自身で見て回ることでどのような機能があるか把握するという目的もあります。

BadTodo - Google スプレッドシート ←参考にお使いください。

この時パラメータ rnd は検査対象外とし、下記の様な二つのリクエストがあれば id の方だけ検査対象とする。

クエリ文字列 rnd は乱数文字列が入っていますが、アプリ側で利用はされておらずキャッシュバスターとして使われています。診断対象とする必要はありません。キャッシュバスターの意味については徳丸本2版の4.15.2節「キャッシュからの情報漏洩」中の「◆ URLに乱数値を付与する方法」を参照ください。
badtodo/docs/usage.md at main · ockeghem/badtodo · GitHub

参考:BadTodo - 27 キャッシュからの情報漏洩

OWASP ZAPでのクローリング

OWASP ZAP でも一通りクローリングしてみる。
[ツール]→[オプション]→[ネットワーク][Local Servers/Proxies]でZAPのリッスンポートを確認し、BurpSuiteと被るようなら一つずらしておく。

BurpSuiteの方は[Proxy Setting]→[Tools][Proxy]から確認できる。

UpstreamProxyとしてDockerのApacheポート:23128を指定。

ZAP右上の「Firefox マーク」のアイコンからZAP組み込みブラウザを開く。

アドレスバーにhttps://todo.example.jp/を入力して、目次の「Bad Todo List(やられサイト本体)」をクリックしトップページを見てみる。この時点でURLがZAPに取り込まれるので、

ZAPに戻りサイドバーの「サイト」一覧から検査対象(https://todo.example.jp/)を右クリック →[攻撃]→[スパイダー]でクローリング。

一通りURL一覧が出る。

「規定コンテキスト」をダブルクリックしプロパティを開き、覚えやすいコンテキスト名に変更する。

https://todo.example.jp/を右クリック → [コンテキストに含める] から先ほど名前を付けたコンテキストに追加する。

アイコンに赤いマークが付く。

「フロッピーアイコン」をクリックし現状を保存しておく。(適当に名前を付けて保存すると .session という拡張子を付けて保存される)

続けて、サイト一覧に不要なURLがあるため、右クリック→以下の処理から除外する→プロキシ で「プロキシから除外するURL」に入れる。
この時なぜか .* とすべてのURLを意味する正規表現も追記される。これに気付かずその後履歴が更新されなくなり原因が分かるまで困った。(複数選択後の除外時に起こる。一つだけ追加した場合は問題なかった。)
ここで /adminer.php 配下など検査不要なパスもコンテキストから除外しておく。

各種調整

・自動ログインの設定
ID/PASSがZAPのデフォルトなので、検査時には所定のID/PASSを使うよう設定する。

ログインリクエストを右クリック → Flag as Context → From-based Auth Login Request

・ログイン状態の検出
ログアウトのリンクをログイン成功を検出する指標にする。
ログイン後のページ todolist.php のリクエストを選択して、レスポンスから<a href="logout.php" class="">を選択し右クリック → Flag as Context → BadTodo:認証ログイン表示
(このリンクはログイン成功後にしか出ず、ZAPでの最初のクローリングではログイン失敗しているため一度手動でログイン成功させログを取ること)

下図の囲みの部分が設定されることを確認して、そのまま(OKボタンはクリックせずに)次の設定に進みます。

・ユーザの追加(ID/PASSはhttps://todo.example.jp/を開くと目次に書いてあります)

・強制ログインユーザの確認

・セッション管理 Webアプリのセッション管理方法を指定する。

オプション→HTTPセッションに「todosessid」が追加されていることを確認する。

・強制ログインモードをONにして再度スパイダー実行

・ランダムパラメータ rnd が無限に検出されるためオプション → スパイダー検索の設定 で「パラメータの名前のみを考慮する」にする。
(『www.example.org/?foo=123 を訪れた後は /?foo=456 は訪れない。/?bar=789 や /?foo=456&bar=123 は訪れる』ZAP – Options Spider screen

・/index.php 以下が無限に検出されるのでコンテキストから除外する。(そもそもこのページは「実習ガイド」のため検査対象外とする)

・前述したように今回は rnd パラメータは検査対象外とするため、/changeicon.php?id=1 と /changeicon.php?id=1&rnd=64b9fcd182e88 が両方あるような場合は後者は検査対象から除く。

・検出したURLリストをエクスポートして事前に作成したExcel一覧と比べ、漏れがないか調べる。

ZAPではクローリングしきれなかった項目を追加したり(adduser.php, delfile.phpやAPI2つなど)不要な検査を削除したりする手間を考えると、すべて手動でロギングした方が楽な場合が多そうです。

・動的スキャン開始

脆弱性の検出結果が出た。

さらに調整

結果の一つを確認するとエラーが出ている。つまりきちんと検査できていない。
「致命的エラー:セッション管理でエラー発生(セッション数の上限超過)」

エラー文で Bad Todo ソースコードを検索すると session.php に「MAX_SESSION_COUNT = 100」と記載がある。
同時ログイン数は100件が上限で、その規制に引っかかったようです。

検証。
Burpのリピーターやイントルーダーを使い、99回ログインを繰り返す。(Intruderのデフォルトで+1回され計100回)

Bad Todo環境に付属の Adminer で確認。
https://todo.example.jp/adminer.php

101回目のログイン。

同様のエラーとなった。

要修正(セッションの設定がうまくいっていない。または毎回ログイン→ログアウトを行う)
→幾つか確認したが、前述の「・セッション管理」で指定しているにもかかわらず結局セッション引継ぎがうまくいっていないような気がする。今回はソースコードを書き換え「MAX_SESSION_COUNT = 10000」とすることでとりあえず凌ぐことにした。
(後で分かりましたがこれはウェブ健康診断仕様「項番13 (M):クローラへの耐性」に脆弱性ありとするための仕様のようです)

再検査→検査終了。XSSやSQLインジェクションなどが検出されている。

通常は「新規登録」→「削除」といったループを作らないとうまく動作しないリクエストが複数あるはずだが、ここをZAPでどう解決したら良いかがまだ分かっていない。後日に回す。→BadTodoの場合データが無くても削除リクエストはエラーにならないようなのでループなしでも大丈夫そうでした。

次回以降、検出された脆弱性を一つ一つ確認していきます。
やられアプリ BadTodo - 3.1 SQLインジェクション 認証の回避 - demandosigno

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