Привет! Добро пожаловать в новую постоянную рубрику «Эксплоиты Давида Голунского». 🙂 Это шутка лишь отчасти: в прошлом году Давид обнаружил интересную уязвимость и до сих пор продолжает радовать нас разными способами ее эксплуатации. Быть может, кому-то дыра в PHPMailer уже набила оскомину, но если ты хочешь шаг за шагом проследить ее эксплуатацию и пополнить свой арсенал новыми трюками, то оставайся с нами!

WARNING

Вся информация носит только ознакомительный характер. Ни автор, ни редакция не несут ответственности за ее ненадлежащее использование.

Уязвима на этот раз лишь одна версия WordPress — 4.6. Многие владельцы сайтов включают автоматическое обновление, что уменьшает масштабы проблемы до уровня единичных инсталляций. Плюс, несмотря на заверения Давида, что эксплоит работает без каких-либо телодвижений и на дефолтном конфиге Apache 2, это не так. По крайней мере, у меня не получилось повторить этого ни на Ubuntu, ни на CentOS, ни на Debian.

Дело тут в особенностях эксплуатации, которая подразумевает нехилые манипуляции с заголовком Host. Они включают в себя использование пробелов, скобок и прочих спецсимволов. Дефолтные конфигурации Apache, которые я протестировал (то есть те, что ставятся из репозиториев командой apt-get или yum), не разрешают этого делать и тут же ругаются ошибкой 400.

Apache 2.4.10 на Debian никак не хочет мириться с пробелом в Host
Apache 2.4.10 на Debian никак не хочет мириться с пробелом в Host

Немного покопавшись в документации индейца, я обнаружил опцию HttpProtocolOptions. Она отвечает за соответствие запросов различным RFC-спецификациям и по умолчанию работает в режиме Strict, то есть «ругаться на все неподходящие запросы». Поэтому для продолжения наших манипуляций изменим ее значение на Unsafe и пойдем дальше.

Моя прошлая статья про эксплуатацию уязвимости PHPMailer касалась непосредственно агента sendmail. Однако наткнуться в реальном мире на систему, где он используется как почтовый демон по умолчанию, довольно непросто. Почти во всех последних версиях современных дистрибутивов Linux (Ubuntu, Debian, CentOS) используются альтернативы вроде Exim и Postfix. И об эксплуатации Exim мы сегодня и поговорим.

 

Причины уязвимости

Начнем с причины уязвимости. Заглянем в pluggable.php.

/wp-includes/pluggable.php:

324:    if ( !isset( $from_email ) ) {
325:        // Get the site domain and get rid of www.
326:        $sitename = strtolower( $_SERVER['SERVER_NAME'] );
327:        if ( substr( $sitename, 0, 4 ) == 'www.' ) {
328:            $sitename = substr( $sitename, 4 );
329:        }
330:
331:        $from_email = 'wordpress@' . $sitename;
332:    }
...
352:    $phpmailer->setFrom( $from_email, $from_name );

Как видишь, данные из переменной $_SERVER['SERVER_NAME'] используются для указания домена (строка 331) в адресе возврата письма (Return-Path, ключ -f).

/wp-includes/class-phpmailer.php

81:     /**
82:      * The Sender email (Return-Path) of the message.
83:      * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
84:      * @var string
85:      */
86:     public $Sender = '';
...
934:     public function setFrom($address, $name = '', $auto = true)
935:     {
...
952:         if ($auto) {
953:             if (empty($this->Sender)) {
954:                 $this->Sender = $address;
955:             }
956:         }

Сама переменная SERVER_NAME берется из запроса, а точнее — из заголовка Host. Поэтому можно попытаться управлять аргументами, которые будут отправлены бинарнику sendmail вместе с ключом -f.

/wp-includes/class-phpmailer.php

1344:     protected function mailSend($header, $body)
...
1352:         if (empty($this->Sender)) {
1353:             $params = ' ';
1354:         } else {
1355:             $params = sprintf('-f%s', $this->Sender);
1356:         }
...
1368:             $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);

/wp-includes/class-phpmailer.php

666:     private function mailPassthru($to, $subject, $body, $header, $params)
667:     {
...
677:             $result = @mail($to, $subject, $body, $header, $params);

И тут мы сталкиваемся с первой проблемой — невозможностью использовать пробел для отделения параметров друг от друга. Причина в том, что перед отправкой функция validateAddress() из библиотеки PHPMailer проверяет получившийся email на соответствие стандарту RFC 2822, а он, разумеется, запрещает использование символа пробела в доменном имени.

Ошибка при использовании пробела в имени домена
Ошибка при использовании пробела в имени домена

Продолжение статьи доступно только подписчикам

Вариант 1. Оформи подписку на «Хакер», чтобы читать все статьи на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта, включая эту статью. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи одну статью

Заинтересовала статья, но нет возможности оплатить подписку? Тогда этот вариант для тебя! Обрати внимание: этот способ покупки доступен только для статей, опубликованных более двух месяцев назад.


Комментарии

Подпишитесь на ][, чтобы участвовать в обсуждении

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

Check Also

Конкурс хаков: пишем на PowerShell скрипт, который уведомляет о днях рождения пользователей Active Directory

В компаниях часто встречается задача уведомлять сотрудников о приближающихся днях рождения…