18 июля была опубликована информация о наборе уязвимостей, получившем название HTTPoxy. Используя его, злоумышленники могут подменять переменную окружения HTTP_PROXY, что позволяет им перенаправлять http-запросы к веб-приложениям на свои ресурсы.

Уязвимость была выявлена при участии разработчика компании Vend Доменика Шайрлинка (Dominic Scheirlinck), который в своем блоге на Medium рассказал о том, как она была обнаружена его коллегами в ходе разбора одного из тикетов, поступившем в службу поддержки.

Как это работает


Шайрлинк подробно объясняет принцип работы HTTPoxy. Типичная атака с использованием этого набора уязвимостей выглядит так:

  1. Атакующий создает специально составленный HTTP-запрос, в котором содержится заголовок Proxy;
  2. CGI получает запрос и сохраняет значение заголовка в переменную среды HTTP_PROXY;
  3. CGI приложение запускает собственный веб-клиент, использующий переменную среды HTTP_PROXY в качестве настроек прокси;
  4. Клиент отправляет запрос, который вместо адреса назначения проксируется через сервер атакующего.

К примеру, так может выглядеть код эксплуатации на нескольких популярных языках:

PHP:

$client = new GuzzleHttp\Client();
$client->get('http://api.internal/?secret=foo')

Python:

from wsgiref.handlers import CGIHandler
def application(environ, start_response):
    requests.get("http://api.internal/?secret=foo")
CGIHandler().run(application)

Go:

cgi.Serve(
    http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        res, _ := http.Get("http://api.internal/?secret=foo")
        // [...]

Более подробные PoC можно найти на GitHub в специальном репозитории HTTPoxy.

Интересный момент заключается в том, что сам баг, который используется в атаке HTTPoxy, был обнаружен пятнадцать лет назад. На сайте, посвященном HTTPoxy, представлено подробное описание истории уязвимости.

Согласно ему, в марте 2001 была обнаружена и исправлена ошибка некорректной обработки заголовков HTTP_PROXY в libwww-perl. В апреле того же года проблема была обнаружена в curl (и также исправлена, хотя и не для Windows). В 2012 году команда проекта Ruby разработала HTTP_PROXY для Net::HTTP — в их системе уязвимости не было.

В ноябре 2013 года она была упомянута в листе рассылки NGINX — пользователь Джонатан Мэттьюс описал ошибку, хотя и не был полностью уверен в своей правоте. В феврале 2015 года уязвимость также была упомянута в списке рассылке Apache httpd-dev. И уже в июле 2016 года сотрудник Vend Скот Джири (Scott Geary) обнаружил баг в реальной системе.

Какие системы уязвимы


Как выяснила команда безопасности Vend, уязвимость содержится во многих современных языках и библиотеках.

  • CVE-2016-5386 Go
  • CVE-2016-5387 Apache HTTPD
  • CVE-2016-5388 Tomcat
  • CVE-2016-1000104 mod_fcgi
  • CVE-2016-1000107 Erlang HTTP Server
  • CVE-2016-1000108 YAWS
  • CVE-2016-1000109 HHVM FastCGI
  • CVE-2016-1000110 Python CGIHandler
  • CVE-2016-1000111 Python twisted

Как обнаружить уязвимость в своем софте


Специалисты компании RedHat разработали небольшой скрипт, позволяющий определить, уязвима ли конкретная система к HTTPoxy.

Для этого администратору сервера необходимо установить следующий CGI-скрипт и сделать его исполняемым:

test.cgi:
 #!/bin/sh
echo "Content-Type:text/plain"
?echo ""
echo "HTTP_PROXY='$HTTP_PROXY'"

После этого следует вызвать CGI-скрипт с заголовком Proxy:

curl -H ‘Proxy: AFFECTED’ http://my-server-name/cgi-bin/test.cgi

Если вывод команды выглядит следующим образом — сервер не подвержен уязвимости:

HTTP_PROXY="

Если же вывод выглядит не так, к примеру отображается надпись ниже, то система уязвима:

HTTP_PROXY='AFFECTED'

Как защититься


Конечные пользователи веб-приложений в данной ситуации никак не могут повысить уровень своей безопасности, однако разработчики этого программного обеспечения имеют все возможности по его защите.

Для этого им нужно заблокировать заголовки запросов Proxy — сделать это можно очень быстро. Это не должно повредить функциональности приложения, поскольку такие заголовки являются нестандартными и обычно не используются.

К примеру, вот так можно отключить заголовки Proxy в Apache c помощью файла .htaccess:

<IfModule mod_headers.c>
   RequestHeader unset Proxy
</IfModule>

Многие ИТ-компании и разработчики софта уже заявили о том, приняли меры к защите своих пользователей от уязвимости HTTPoxy — в их числе Akamai, CloudFlare, исправления выпустила и RedHat.

Эксперты Positive Technologies разработали сигнатуру для Suricata IDS, позволяющую обнаружить использование заголовка “Proxy” в HTTP запросах и предотвратить возможные последствия. Сигнатуру и пример эксплуатации можно найти в официальном репозитории twitter.com/AttackDetection/status/756142402268639232.
Поделиться с друзьями
-->

Комментарии (26)


  1. sshikov
    21.07.2016 19:12

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


    1. amarao
      21.07.2016 21:04

      В shell-shock'е немного другое было. То есть в целом это «копаемся в старых программах и старых протоколах в поисках garbage in garbage out», но векторы атаки разные.

      Плюс shell-shock глубже вошёл (благодаря dhcp).


      1. sshikov
        21.07.2016 21:20

        Разумется другое, но суть ровно такая же: никто не проверяет данные, которые приходят снаружи. Такие же ошибки в SQL (попытка подставить параметры в код вместо того, чтобы использовать ?) — на каждом шагу.


        1. amarao
          21.07.2016 21:38

          Да. Смешивают источники — доверенные строки в коде с недоверенными строками со входа от пользователя.


  1. Evengard
    21.07.2016 19:40

    А для чего вообще нужен заголовок Proxy? Какое его истинное предназначение?


    1. Evengard
      21.07.2016 19:49

      А, всё, почитал источник и разобрался в чём суть уязвимости. Дело не в том, что именно Proxy превращается в HTTP_PROXY, а вообще любые заголовки превращаются в переменные окружения с префиксом «HTTP_».


      1. sumanai
        21.07.2016 21:35

        Как я понимаю, если приложение на PHP никак не использует переменные окружения с префиксом HTTP_ и не делает внешних запросов- то опасности нет?


        1. Evengard
          21.07.2016 23:30
          +1

          В общем и целом — да.


  1. G-M-A-X
    21.07.2016 20:53

    1. Дефолтный конфиг для fastcgi от nginx 1.8.1 не передает этот заголовок
    2. В php 7.0.9 пофиксили: www.php.net/ChangeLog-7.php#7.0.9


  1. amarao
    21.07.2016 21:03
    +6

    Вся проблема в модели CGI.

    CGI подразумевает, что мы получаем хидеры от запросов пользователей как переменные среды окружения. В то же самое время весь остальной софт считает, что переменные среды окружения — это довренная информация, которая используется администраторами (и локальными пользователями запускающими софт).

    Давайте я вам сейча ещё более страшную уязвимость назову такого типа:

    LD_PRELOAD.

    И я не шучу. Добиваемся, чтобы наша so-шка оказалась в указанном месте на сервере, выставляем LD_PRELOAD в заголовках запроса, после чего наша so'шка выполняется с правами администратора.

    (Интересно, апач и остальные LD_PRELOAD фильтруют или я только что новую CVE'шку с remote code execution описал?)


    1. evnuh
      21.07.2016 21:04

      не баг, а фича
      На сам деле вроде бы правильно заметили, что добавляется префикс HTTP


      1. amarao
        21.07.2016 21:09

        Это плохая фича. Точнее, плохой дизайн. Путать источники информации — это всё равно, что читать конфиги для софта из ../users/upload, а потом защищать Важные Конфиги от подмены.

        Насчёт HTTP-префикса да, забавно. Получается, что эта «защита» ломается выставлением _PROXY. Плохо прочитал.


        1. merlin-vrn
          22.07.2016 11:44

          "_" (простите за нечаянный смайл ^_^') уже содержится в добавляемом префиксе, поэтому http-заголовок должен быть просто «Proxy».

          И пожалуй да, проблема в архитектуре CGI


    1. selivanov_pavel
      22.07.2016 13:15

      Нет. Вот если бы переменная называлась HTTP_LD_PRELOAD, то была бы новая CVE-шка.


    1. saboteur_kiev
      22.07.2016 14:00

      С правами администратора или веб-сервера?


      1. amarao
        22.07.2016 21:29

        Кто исполняемый файл запускает, тот его переменные и контролирует. Именно потому у всякого рода sudo так строго с фильтрацией переменных окружения — чтобы не было эскалации привилегий.


        1. saboteur_kiev
          25.07.2016 00:35

          Я к тому, что CGI выполняется же через web, следовательно права будут веб сервера, а не администратора?


          1. merlin-vrn
            25.07.2016 09:06

            Но этого достаточно например для того, чтобы стырить или испортить базу данных, к которой это CGI-приложение обращается.


  1. Barafu
    21.07.2016 22:19

    Отлично же! Вот именно по этому я и шифрую свой трафик. А не потому, что…


    1. merlin-vrn
      22.07.2016 11:52
      +3

      И каким образом это вам помогает?

      Скажем, ваш сайт использует API какого-то сервиса, который существует только по HTTP. Ну обратятся к вашему сервису по HTTPS, выставят заголовок Proxy, всё равно при обращении к HTTP API ваш сервис пойдёт по указанному в заголовке адресу.

      Даже если API по HTTPS: как правило, для обращения к API используют что-то вроде curl, а он использует переменную HTTP_PROXY и по умолчанию на верификацию сертификата HTTPS-сервера кладёт болт, так что по адресу Proxy спокойно сможет сидеть MitM и ваш сайт это проглотит. Это браузеры показывают красное окошко с пугалками «идентификация сайта не удалась, открывать его может быть опасно», а тут-то это некому показывать.

      Если эту верификацию затребовать, то да, при обращении к API через фейковый прокси получим ошибку вроде «API-сервер не отвечает, попробуйте позже», что хорошо.


  1. Decker
    21.07.2016 23:54

    Поясните в двух словах, если не сложно… может ли быть данная уязвимость опасна для Apache + PHP подключенного через php5_module?

    Тестовый скрипт, выглядящий как:

    <?php var_dump($_SERVER["HTTP_PROXY"]); ?>

    Показывает что переменная HTTP_PROXY все-таки устанавливается при наличии заголовка Proxy в HTTP запросе от пользователя. Ради интереса попробовал поднять тестовый proxy сервер и передать его в заголовке Proxy в HTTP запросе скрипту, в котором осуществлялся GET-запрос через file_get_contents и с помощью curl_exec в PHP. В обоих случаях несмотря на то что в $_SERVER[«HTTP_PROXY»] содержался IP: порт Proxy запрос не пошел через него. Т.е. получается что $_SERVER[«HTTP_PROXY»] PHP при формировании исходящих запросов игнорировал.

    В итоге получили, что любой пользователь извне может изменить значение $_SERVER[«HTTP_PROXY»], но исходящие запросы все равно не используют Proxy прописанный там. Стоит ли последовать рекомендациям, подключив mod_headers, чтобы вырезать заголовок Proxy или можно считать что система не подвержена данной уязвимости?

    p.s. Или же речь идет только о CGI?


    1. Evengard
      22.07.2016 07:17

      Дело не в $_SERVER, а в $_ENV. Если значение попадёт в $_ENV, значит теоретически уязвимость имеется.


    1. zelenin
      22.07.2016 13:34

      через прокси будет работать библиотеки, берущие прокси из HTTP_PROXY, а не сам php.


      1. Decker
        22.07.2016 20:04

        Спасибо, уже разобрался… в результате получается что getenv('http_proxy') = $_SERVER[«HTTP_PROXY»]… ну а далее действительно все что использует http_proxy из переменных окружения начинает работать через Proxy. Примером уязвимого кода может быть, например, такой:

        function autoDetectProxySettings() {
        if (($httpProxy = getenv('http_proxy')) || ($httpProxy = getenv('HTTP_PROXY'))) {
                     return $httpProxy;
                 }
        }
        
        ... 
        
        // ну а далее ... 
        
        $httpProxy = autoDetectProxySettings();
        
        $ch = curl_init("http://server.url/secret_api");
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        if (isset($httpProxy)) { curl_setopt($ch, CURLOPT_PROXY, $httpProxy); }
        $result = curl_exec($ch);
        


        Аналогично и использующие переменную среды HTTP_PROXY библиотеки. Вообще интересно было бы краткий список наиболее часто используемых уязвимых библиотек.


        1. zelenin
          23.07.2016 02:50

          так точно.
          Список есть в принципе. Самые известные это наверное guzzle для php, и requests для питона.


  1. YuriM1983
    22.07.2016 16:22

    Для python-requests уже давно придумали обход:

    import requests
    session = requests.Session()
    session.trust_env = False
    response = session.get('http://www.stackoverflow.com')

    Но вообще, конечно, подстава.