前回:やられアプリ BadTodo - 10.7 XSSによるCSRF対策の突破 - demandosigno
ウェブアプリケーションの中には、リクエストに対して出力する HTTP レスポンスヘッダのフィールド値を、外部から渡されるパラメータの値等を利用して動的に生成するものがあります。たとえば、HTTP リダイレクションの実装として、パラメータから取得したジャンプ先の URL 情報を、Location ヘッダのフィールド値に使用する場合や、掲示板等において入力された名前等を Set-Cookie ヘッダのフィールド値に使用する場合等が挙げられます。このようなウェブアプリケーションで、HTTP レスポンスヘッダの出力処理に問題がある場合、攻撃者は、レスポンス内容に任意のヘッダフィールドを追加したり、任意のボディを作成したり、複数のレスポンスを作り出すような攻撃を仕掛ける場合があります。
安全なウェブサイトの作り方 p.34
やられアプリ BadTodo - 5 オープンリダイレクト - demandosigno の記事で「ログイン画面表示時のパラメータ "url" を書き換える(=ログイン時のパラメータ"url"を書き換える)と、レスポンスのLocationヘッダに指定のURLが出力される」ことが分かりました。ここをもう少し深堀りしてみます。
通常のリクエストではこうです。
今回はパラメータ url の後ろに "%0d%0aSet-Cookie:xxxtest%3Dxxxxtest%3B" と付けてみます。(%エンコードを直すと"(改行)Set-Cookie:xxxtest=xxxxtest;" です)
ウェブ健康診断仕様 p.16
レスポンスヘッダに、xxxtest=xxxxtest という Set-Cookie ヘッダが存在するため、改行コード "%0d%0a" が有効に働いたことが分かります。
オープンリダイレクトの例で実行したのと同様に、偽のリンクなどから https://todo.example.jp/login.php?url=todolist.php%0d%0aSet-Cookie:TODOSESSID=1234abcd;
へ誘導されログインしたとします。
レスポンスヘッダの Set-Cookie: TODOSESSID=1234abcd; により、利用者のブラウザに攻撃者が指定した任意のセッションIDが登録されます。
次に攻撃者が自分のブラウザの TODOSESSID を先ほど指定した 1234abcd に書き換えページをリロードすると、ログイン状態の管理者に入れ替われます。(替わらない場合はマイページなど他のページに移動したりしてみてください)
利用者からセッションIDを盗み取るのと反対に、攻撃者側に都合の良いIDを利用者に強制するこの方法を「セッションIDの固定化(Session Fixation)攻撃」と言います。
TODOSESSID書き換え後
補足:上記のように任意のIDを指定可能なこともありますが、そうでない場合は一度そのサイトを利用・ログインしてみて有効なセッションIDを記録しておきます。(記録したらログアウトします。この時セッションIDが付け直されていたらそのサイトへの攻撃は難しいです)
とくまるひろしのSession Fixation攻撃入門 | 徳丸浩の日記
セッションIDの固定化攻撃への対策としては「認証後にセッションIDを変更する」があります。
PHPでこの処理を行うには session_regenerate_id 関数が利用できます。
PHP: session_regenerate_id - Manual
外部ドメインへのリダイレクト
オープンリダイレクトの例ではurl=https://www.example.com/
と直接 url パラメータを書き換えましたがurl=todolist.php%0D%0ALocation:+https://www.example.com/
のように今回と同様に改行コードを使いLocationヘッダを挿入することも可能です。
レスポンスには
Location: https://www.example.com/
が出力されます。正常リクエストのLocationヘッダ ↓ は消えます。
Location: todolist.php
改行(%0D%0A)があるため、PHPプログラム中では2行のLocationヘッダを出力します。
Location: todolist.php Location: https://www.example.com/
Apacheがプログラムから受け取ったヘッダ中にLocationヘッダが複数あると、Apacheは最後のLocationヘッダのみをレスポンスとして返すため本来のリダイレクト先が消え、改行の後ろに指定したURLが有効になります。
(安全なWebアプリケーションの作り方 p.244)
PHPにおけるHTTPヘッダインジェクションはまだしぶとく生き残る | 徳丸浩の日記
PHP5.1.2にて、header関数の引数中の改行をチェクし、複数のヘッダを送信しないように改修されました。
これで、PHPでヘッダインジェクションはできなくなった…と思いきや、抜けがありました。PHP5.1.2での修正は、改行を構成するキャリッジリターンとラインフィードのうち、ラインフィードのみをチェックしていて、キャリッジリターンはチェックしていません。一方、大半のブラウザ(IE、Google Chrome、Safari、Opera)ではキャリッジリターンのみでも改行とみなしていて、ヘッダインジェクションが可能な状態でした。
http://example.jp/header.php?url=http://example/top.php%0dSet-Cookie:+PHPSESSID%3DABC
この問題はPHP 5.3.11およびPHP 5.4.0で修正され、最新のPHPではキャリッジリターンのみを使ったヘッダインジェクションはできなくなっています。