Уязвимость HTTPoxy позволяет перенаправлять http-запросы веб-приложений



    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.
    Positive Technologies
    298,00
    Компания
    Поделиться публикацией

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

      0

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

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

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

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

            0
            Да. Смешивают источники — доверенные строки в коде с недоверенными строками со входа от пользователя.
        0
        А для чего вообще нужен заголовок Proxy? Какое его истинное предназначение?
          0
          А, всё, почитал источник и разобрался в чём суть уязвимости. Дело не в том, что именно Proxy превращается в HTTP_PROXY, а вообще любые заголовки превращаются в переменные окружения с префиксом «HTTP_».
            0
            Как я понимаю, если приложение на PHP никак не использует переменные окружения с префиксом HTTP_ и не делает внешних запросов- то опасности нет?
              +1
              В общем и целом — да.
          0
          1. Дефолтный конфиг для fastcgi от nginx 1.8.1 не передает этот заголовок
          2. В php 7.0.9 пофиксили: www.php.net/ChangeLog-7.php#7.0.9
            +6
            Вся проблема в модели CGI.

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

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

            LD_PRELOAD.

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

            (Интересно, апач и остальные LD_PRELOAD фильтруют или я только что новую CVE'шку с remote code execution описал?)
              0
              не баг, а фича
              На сам деле вроде бы правильно заметили, что добавляется префикс HTTP
                0
                Это плохая фича. Точнее, плохой дизайн. Путать источники информации — это всё равно, что читать конфиги для софта из ../users/upload, а потом защищать Важные Конфиги от подмены.

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

                  И пожалуй да, проблема в архитектуре CGI
                0
                Нет. Вот если бы переменная называлась HTTP_LD_PRELOAD, то была бы новая CVE-шка.
                  0
                  С правами администратора или веб-сервера?
                    0
                    Кто исполняемый файл запускает, тот его переменные и контролирует. Именно потому у всякого рода sudo так строго с фильтрацией переменных окружения — чтобы не было эскалации привилегий.
                      0
                      Я к тому, что CGI выполняется же через web, следовательно права будут веб сервера, а не администратора?
                        0
                        Но этого достаточно например для того, чтобы стырить или испортить базу данных, к которой это CGI-приложение обращается.
                  0
                  Отлично же! Вот именно по этому я и шифрую свой трафик. А не потому, что…
                    +3
                    И каким образом это вам помогает?

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

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

                    Если эту верификацию затребовать, то да, при обращении к API через фейковый прокси получим ошибку вроде «API-сервер не отвечает, попробуйте позже», что хорошо.
                    0
                    Поясните в двух словах, если не сложно… может ли быть данная уязвимость опасна для 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?
                      0
                      Дело не в $_SERVER, а в $_ENV. Если значение попадёт в $_ENV, значит теоретически уязвимость имеется.
                        0
                        через прокси будет работать библиотеки, берущие прокси из HTTP_PROXY, а не сам php.
                          0
                          Спасибо, уже разобрался… в результате получается что 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 библиотеки. Вообще интересно было бы краткий список наиболее часто используемых уязвимых библиотек.
                            0
                            так точно.
                            Список есть в принципе. Самые известные это наверное guzzle для php, и requests для питона.
                        0
                        Для python-requests уже давно придумали обход:

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

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

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

                        Самое читаемое