LoginSignup
13
3

More than 5 years have passed since last update.

【CVE-2016-10033】PHPMailerに重大な脆弱性

Posted at

「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

class.phpmailer.php
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-Pathsendmail -fMAIL 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とか書いてあるので、直接値を突っ込まれたらバリデーションもへったくれもなかったりする。

13
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
3