Уязвимости выполнения произвольного кода в PHPMailer и SwiftMailer

    В последние дни было зарегистрировано три уязвимости, касающиеся PHPMailer и SwiftMailer:



    Все три отчёта об уязвимостях упоминают фреймворк Yii наряду с другими PHP фреймворками как уязвимый, потому цель этой статьи — прояснить, кто именно подвержен этой уязвимости и что нужно сделать для того, чтобы обезопасить себя.


    Что касается PHPMailer, Yii никогда официально не предоставлял никаких компонентов, связанных с PHPMailer'ом. Кроме того, Yii никогда не включал PHPMailer в какой-либо код, который официально выпускался командой Yii.


    Упоминание Yii в отчётах скорее всего является копи-пастой из README PHPMailer'a, где сказано, что он может быть использован вместе с фреймворком Yii. Для PHPMailer уже выпущен патч, нужно обновиться до версии как минимум 5.2.20.


    Ситуация касательно SwiftMailer отличается. Для него мы предоставляем расширение yii2-swiftmailer. SwiftMailer также выпустил патч, нужно обновиться до версии как минимум 5.4.5.


    Суть уязвимости


    Так как PHP-функция mail() не предоставляет отдельного параметра для указания адреса отправителя, единственным способом сделать это — является передача строки пятым аргументом ($additional_parameters). Строка должна содержать флаг -f, следом за которым идёт email отправителя (например -fadmin@example.com).


    Это приводит к вызову /usr/bin/sendmail со списком параметров, сформированным из вызова PHP-функции mail(). Например:


    /usr/sbin/sendmail -t -i -fadmin@example.com

    Когда разработчик самостоятельно передаёт 5-й аргумент в функцию mail(), подразумевается, что он читает документацию и знает, что email нужно проверить и экранировать, чтобы он был безопасен для использования в командной строке. В случае, если это не сделано, очевидно, что вся ответственность лежит на разработчике, который допустил инъекцию параметров командной строки.


    Однако, библиотеки PHPMailer и SwiftMailer предоставляют удобный API, который скрывает факт передачи email в командную строку опасным способом. Разработчик надеется, что библиотеки выполняют достаточное экранирование для обеспечения безопасности вызова программы sendmail.


    Обнаруженные уязвимости указывают на то, что библиотеки PHPMailer и SwiftMailer не выполняли достаточного экранирования. Это значит, что если адрес отправителя будет сформирован специальным образом, он будет передан в пятый параметр PHP-функции mail() и выполнен как дополнительные параметры при вызове программы sendmail.


    Например, передача следующей строки, как email адреса, позволяет внедрить в параметры -oQ и -X, которые будут обработаны программой sendmail:


    -f"attacker\" -oQ/tmp/ -X/var/www/cache/phpcode.php  some"@email.com
    
    // Приведет к выполнению
    /usr/sbin/sendmail -t -i -f"attacker" -oQ/tmp/ -X/var/www/cache/phpcode.php  some@email.com

    Более подробную информацию по воспроизведению уязвимости можно прочесть по ссылкам в начале статьи.


    Кто подвержен опасности?


    В первую очередь пользователи, которые используют классы Swift_MailTransport или PHPMailer для отправки сообщений. Эти классы, к свою очередь, используют PHP-функцию mail().


    Что нужно сделать, чтобы обезопасить приложение?


    Учитывая, что уязвимости касаются только поля "От", вам нужно вспомнить, где в ваших приложениях пользователь может указать собственное значение для адреса отправителя. Чаще всего это используется в контактных формах и скриптах гостевых книг.


    Если вы корректно валидируете email на этапе приёма его от пользователя, то эта уязвимость вас скорее всего и не коснётся.


    Хорошей практикой является валидация данных сразу при приёме от пользователя, что помогает избежать обработки невалидных данных в других частях приложения, в том числе передачи сторонним библиотекам, которые могут оказаться уязвимыми.


    Для Yii существует EmailValidator, который не пропускает адреса по примеру тех, которые использовались в данной уязвимости


    Кроме того, в PHP существует нативная функцию filter_var(), которой можно проверить email. Например:


    public function validateEmail() {
        if (filter_var($this->email, FILTER_VALIDATE_EMAIL) === false) {
            throw new InvalidParamException('The email contains characters that are not allowed');
        }
    }

    UPD1: SwiftMailer выпустил патч, нужно обновиться до версии как минимум 5.4.5.

    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 11
    • +1
      Если вы корректно валидируете email на этапе приёма его от пользователя, то эта уязвимость вас скорее всего и не коснётся.

      Строго говоря, "attacker\" -oQ/tmp/ -X/var/www/cache/phpcode.php some"@email.com — это валидный e-mail по RFC 2822. В local-part разрешается quoted-string.

      • +3

        Да, это так. В реальной повседневной жизни этот RFC интерпретируется всеми по-своему, что приводит к разным реализациям валидаторов.


        Учитывая, что пользователей с вычурным адресом по RFC ничтожно мало, нам проще пожертвовать ими в пользу безопасности.

        • 0

          Абсолютно согласен. Я к тому, что у тех, кто именно корректно (по rfc) валидирует, может из этого утверждения создаться ложное ощущение безопасности. Подозреваю, что с разработчиками phpmailer и swiftmailer именно так и произошло — там-то как раз по rfc валидация внутри (по крайнее мере в swiftmailer — точно).

          • 0
            А если следовать хорошему тону то правильней всё же будет не жертвовать пользователями, а правильно экранировать параметры командной строки.
            А это уже перекладывается ответственность на нескольких разработчиков данного пакета, а не на десятки разработчиков разнокалиберных приложений.
            • +1

              Если у вас есть публичные проекты, в которых разрешены email'ы по всем канонам RFC — поделитесь, пожалуйста, количеством пользователей, адреса которых не проходят валидацию по регулярке из EmailValidator в Yii.

          • 0
            А есть почтовый сервис, где можно зарегистрировать такой email?
          • 0
            Symfony валидатор тоже спокойно пропускает конструкцию и считает ее валидной, спасает то что их валидатор емейлов довольно кривоват, поэтому так или иначе использую свой, который удаляет спецсимволы)
            • 0
              Регулярка из симфони: https://www.debuggex.com/r/X4pyOtmb6C8LkhdM
            • +2

              Суть уязвимости, кстати, намного интереснее.


              Первой попыткой фикса в PhpMailer-е было добавление escapeshellarg(), а в SwiftMailer-е так уже и было сделано.


              Нюанс в том, что mail() внутри выполняет escapeshellcmd(), в итоге "наложения" результатов этих функций можно получить возможность выполнить произвольный код — пример такго значения $from можно увидеть в добавленном в SwiftMailer юнит-тесте.


              В таких условиях написать корректную реализацию экранирования для всевозможных шеллов довольно сложно, так что в SwiftMailer сдались и добавили проверку по белому списку символов (непонятно только, почему не регуляркой). Так что необычные, но валидные емейлы теперь тоже не пройдут.

              • +1
                Вот стандартные Yii 1 регулярки в CEmailValidator, который само-собой разумеется все используют для валидации email полей и я что-то не заметил чтоб они что-то пропускали лишнее:

                pattern

                fullPattern

                Так в чем проблема тогда? Что-то вроде: Кто не валидирует поля у себя в проекте сам себе злобный буратино?

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.