「PHPMailer」に重大な脆弱性、直ちにパッチ適用を
PHPからのメール送信ライブラリ「PHPMailer」に脆弱性、米SANS ISCが注意喚起
さて、では具体的にどんな内容だろうか。
当然ニュースサイトにはそこまで書かれていない。
CVE-2016-10033について。
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-10033
https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html
Exploitが公開されている。
https://www.exploit-db.com/exploits/40968/
https://github.com/opsxcq/exploit-CVE-2016-10033
のだが、ここで公開されているclass.phpmailer.phpは、なんかvalidateAddress()メソッドに$patternselect = 'noregex'
とかいう文字列が書き加えられている。
これはメールアドレスのチェックに正規表現もFILTER_VALIDATE_EMAILも使わないという設定で、@が入ってるか否か程度しか確認しなくなってメアドがフリーパスになる。
そんなのアウトになるのは当然なので、このExploitはCVE-2016-10033本来の問題点を隠すためのカモフラージュだと思われる。
なお通常でも$patternselect = 'noregex'
になるパターンは存在するのだが、
・PCREが入ってない (定数PCRE_VERSIONが定義されていない AND pcreがextension_loadedされていない)
・PHPのバージョンが5.2.0未満
という旧石器時代のPHPでしか起こらないので、考慮に入れる必要はないだろう。
Exploitでは原因がよくわからなかったのでdiffってみよう。
問題のある5.2.17
https://github.com/PHPMailer/PHPMailer/releases/tag/v5.2.17
修正されたとされる5.2.19
https://github.com/PHPMailer/PHPMailer/releases/tag/v5.2.19
1367c1367
< if ($this->Sender != '') {
---
> if (!empty($this->Sender)) {
1444,1445c1444,1445
< if (!empty($this->Sender)) {
< $params = sprintf('-f%s', $this->Sender);
---
> if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
> $params = sprintf('-f%s', escapeshellarg($this->Sender));
1447c1447
< if ($this->Sender != '' and !ini_get('safe_mode')) {
---
> if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) {
1501,1503c1501
< if ('' == $this->Sender) {
< $smtp_from = $this->From;
< } else {
---
> if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1504a1503,1504
> } else {
> $smtp_from = $this->From;
ええ、、、
==とかやめようよ。
さて改修内容から見るに、PHPMailer::Sender
に想定していない値を入れられてしまう問題であることは間違いないだろう。
Senderは何かというとReturn-Path
でsendmail -f
でMAIL FROM
の値。
Senderを設定しているところはPHPMailer::setFrom()だけ。
/**
* Set the From and FromName properties.
* @param string $address
* @param string $name
* @param boolean $auto Whether to also set the Sender address, defaults to true
* @throws phpmailerException
* @return boolean
*/
public function setFrom($address, $name = '', $auto = true)
{
$address = trim($address);
$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
// Don't validate now addresses with IDN. Will be done in send().
if (($pos = strrpos($address, '@')) === false or
(!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
!$this->validateAddress($address)) {
$error_message = $this->lang('invalid_address') . " (setFrom) $address";
$this->setError($error_message);
$this->edebug($error_message);
if ($this->exceptions) {
throw new phpmailerException($error_message);
}
return false;
}
$this->From = $address;
$this->FromName = $name;
if ($auto) {
if (empty($this->Sender)) {
$this->Sender = $address;
}
}
return true;
}
なんだこりゃ。
if文の書き方が生理的に気持ち悪い。
だいたいこういう変なことをやってるところにはバグが入り込むというのが定石ではあるのだが、上記Exploitを試した限りではSenderから変な値は正しく弾かれていた。
ということで、結局どのような値を突っ込めばSenderを突破できるのかはよくわからなかった。
Video PoCが公開されるまで待ちかな。
ここまでがんばって詳細不明とかがっかりだが、せっかく調べたのを捨てるのもアレなので置くだけ置いておこう。
きっとそのうち誰かがもっと詳細な内容を挙げてくれるでしょう。
ちなみにプロパティSenderは、というかほとんどのプロパティがpublicとか書いてあるので、直接値を突っ込まれたらバリデーションもへったくれもなかったりする。