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

На что способна ада. Делаем утилиту для детекта гипервизора на полузабытом языке

Лучше всего познавать язык на реальном проекте, поэтому, когда я решил поэкспериментироват…