Большинство из нас многократно слышали про способность сайтов узнавать информацию о посетителе (и всевозможные предупреждения по этому поводу),
которую невозможно узнать из IP-адреса. Это такие «личные» данные, как операционная система, используемый браузер, язык системы.
Кто же так жестоко палит нас? И как можно это использовать в своих благих намерениях?
Для начала приведу немного теории о том, чем мы пользуемся довольно давно. HTTP (HyperText Transfer Protocol – «протокол передачи гипертекста») – клиент-серверный протокол передачи данных, который служит для получения информации с веб-сайтов. Был предложен создателем всея WWW Тимом Бернерсом-Ли. Структура его проста: тип сообщения, заголовки, тело сообщения. Существуют RFC, стандартизирующие HTTP (последняя версия – 1.1), согласно которым клиент должен посылать серверу заголовки, содержащие ту самую специфическую информацию о системе и браузере. Обычному пользователю это полезно: сайт в зависимости от клиента может выдать специфическую верстку (пример – google.com) или показать информацию на нужном языке. Однако для хакера раскрытие такой информации может быть вредно или даже опасно.
Представим ситуацию: некий Иван зашел на сайт, посмотрел на него и решил взломать. Загрузил проверенные соксы, поставил красивый дефейс и через несколько часов/дней сидел в участке. Ведь несложно сопоставить данные взломщика с данными остальных посетителей и найти настоящий IP (очень редко встретишь сайт без логирования). Да, некоторые факторы не учтены, однако, вариант возможный.
Техническая реализация
Итак, нам необходимо подделывать заголовки, которые браузер шлет серверу. Как кросс-браузерное решение я бы предложил старый быстрый Proxomitron. Изначально он предназначен для удаления рекламы и полного управления содержимым страницы, так что замечательно подходит для наших целей. Работает как HTTP-прокси. С первого взгляда интерфейс Proxomitron’а не очень дружелюбен, однако разобраться в нем – дело нехитрое. Если нужно использовать только подделку заголовков – слева убираем все галки кроме второй. Жмем на «Headers» и редактируем правила подмены: сразу после установки – в списке куча правил, добавить свое можно, нажав на кнопку New. Чтобы задействовать фильтр, нужна галка в поле «out». Обязательно прочитай русский хелп к программе – там отлично все расписано.
Я пользуюсь Mozilla Firefox и предпочитаю вместо внешних программ использовать плагины. Tamper Data позволяет перехватывать запросы и редактировать заголовки в реальном времени – незаменимая вещь при ручной проверке. Все просто: в окне плагина жмем «Запустить перехват» и вмешиваемся, когда необходимо. Имеются пресеты и богатые возможности по изменению значений заголовков.
Для постоянной же подмены заголовков лучше использовать плагин Modify Headers. Сразу после установки необходимо открыть настройки и поставить галку на «Always on», дабы подмена происходила всегда. Настройка элементарная – открыть главное окно плагина и добавить правил. Первое поле – выбор действия («Add» – добавить, «Modify» – изменить, «Filter» – исключить из запроса), второе – название заголовка, третье – значение; в четвертом поле можно оставить записку. Правила можно двигать, включать и выключать.
Вектор поиска
Немного отвлечемся от темы и поразмыслим над тем, из чего будут состоять значения подделываемых заголовков. Все достаточно очевидно при использовании подмены только ради сокрытия своих данных: тут либо фильтрация, либо замена на аналогичные из других браузеров/систем. Совсем другое дело при поиске уязвимостей. Прежде всего, нужно определиться, какой тип брешей мы будем искать.
PHP-include не подходят, ибо файлы никогда не подключают в зависимости от заголовков. Хотя и есть возможные исключения, к примеру, инклуд файла в зависимости от языка. На практике не встречал.
Пассивные XSS основаны на том, что запрос будет делать непосредственно пользователь.
Заставить жертву подменить заголовок едва ли удастся, именно поэтому этот тип нам не подходит.
Единственная возможность использовать найденные подменой заголовков пассивные XSS – это если на страницу пишется Referer (ссылающаяся страница),
да не простой, а дополнительно декодированный (избавленный от %xx).
Тогда можно скриптом перенаправить жертву на себя с нужным параметром и после – на уязвимую страницу, так что параметр запишется в месте Referer’а.
Сработает в идеальных условиях, на практике также не встречал.
Активные XSS подходят. Смысл в том, чтобы веб-приложение занесло в базу наш заголовок, а после показало администратору, естественно, не обработав.
SQL-инъекции подходят. Это самый простой тип, достаточно, как и обычно, подставлять кавычку.
PHP-исполнение кода очень редко встречается вообще, а с участием заголовков – еще реже. Однако бывает. Тут преимущество нашего метода в том, что GET и POST данным менее доверяют.
Итак, теперь необходимо составить строку, которая позволяла бы определить наличие уязвимости. Кавычка обязательна. Далее необходимо выйти из возможных тегов: простейший способ сделать это символами ">. И последнее – алерт, дабы не проспать. В итоге у меня вышло '"><script>alert(document.cookie)</script>. Ставить на обнаружение исполнения кода я не стал; если желаешь, добавь, например, <?'?> чтобы вызвать ошибку и заметить уязвимость. Также можно добавить в конец обратный слеш (пытаясь экранировать закрывающую кавычку), бывают случаи с фильтрацией кавычек, бывают без нее. Считаешь, что строка слишком длинная и может обрезаться? Замени “document.cookie” на «1». Тут главное – приложить фантазию и создать свой идеальной вектор поиска уязвимостей.
Интересные заголовки
Расскажу о наиболее интересных заголовках, с которыми обязательно нужно поиграться при пентесте сайта.
User-Agent
Главный враг шпиона. Больше всех выдает информации о посетителе: браузер, его версию и язык, движок браузера, версию движка, операционную систему. Данные могут быть написаны как угодно, однако примерный формат есть:
Браузер/Версия (Платформа; Шифрование; Система, Язык[; Что-нибудь еще]) [Дополнения].
В качестве платформы чаще всего можно увидеть X11 или Windows, иногда туда прямиком помещают систему, убирая соответствующий заголовок после. «Шифрование» может принимать три значения: “N” (None) – отсутствует, “I” (International) – слабое шифрование ключом до 40 бит, “U” (USA) – сильное шифрование с ключом 128 бит. Сейчас все браузеры используют только сильное шифрование. После скобки добавляется различная информация вроде движка, плагинов, дополнений.
В качестве браузера для совместимости очень часто указывают Mozilla, а уже после информации дописывают реальное название. Это связано с тем, что издавна девелоперы должны были (или просто любили) делать сайты не в соответствии с принятыми стандартами Консорциума Всемирной паутины (World Wide Web Consortium, W3C), а под наиболее распространенные в данное время браузеры, что приводило к еще большему доминированию последних. И сейчас такая практика существует, однако с тем отличием, что популярным браузерам даны дополнительные возможности, связанные с использованием JavaScript’а (например, на распространенном форуме Invision Power Board, в ветке 2.3.x, посмотрите профиль участника с отфильтрованным заголовком и без). Поэтому советую в строку User-Agent’а в любом случае включать определение распространенного браузера.
Referer
Сообщает о странице, с которой пришел пользователь. Заголовок, сильно полезный веб-мастерам для отслеживания путей попадания на их сайты. Написание – ошибка от английского «Referrer» («перенаправляющий»). Большинство браузеров позволяет отключать передачу этого заголовка, однако при этом обычно возникают проблемы с файлообменниками и сайтами-архивами, которым очень жалко отдавать контент без показа рекламы, так что они позволяют скачивать только при наличии их сайта в реферере. Можно подменять ради просмотра отдельных элементов сайта – например, картинок – без загрузки страниц, где они размещены (при условии, что без подделки это сделать не удастся). При пентесте стоит учитывать, что часто в базу записывают URL не полностью, а лишь нужную его часть, поэтому стоит пробовать «http://evil», «http://example.com/evil» и т.д.
X-Forwarded-For
Нестандартный заголовок, используемый неанонимными прокси-серверами для передачи реального IP клиента. Мы не можем вставить кавычку в определяемый сервером IP, зато можем заставить его думать, что это – всего лишь прокси, а настоящий IP – вот он, в X-Forwarded-For. Конечно, далеко не все скрипты используют и полагаются на XFF, но этот заголовок принято хотя бы логировать. Нельзя забывать проверять веб-приложение на наивность (да-да, некоторые сайты, увидев этот заголовок, забывают про обычный IP и пользуются только тем, что передано в данном заголовке). Формат: X-Forwarded-For: client_ip, proxy1_ip, ..., proxyN_ip.
Accept-Language
Сообщает допустимые языки содержания и их приоритет, именно от него зависит язык отображения сайта. Обычно полностью регулируется настройками браузера. Как я уже говорил, теоретически возможен дырявый скрипт с подключением файла, где имя его – предпочитаемый язык. Подменять и тестировать в любом случае стоит.
Accept-Charset
Сообщает допустимые кодировки и их приоритет. Не самый интересный заголовок, но стоит обратить на него внимание, ибо он может выдать твою систему простым «windows-1251».
X-Requested-With
Нестандартный заголовок, сообщает средство запроса. Используется при запросах из JavaScript без перезагрузки страницы. Соответственно полезен для имитации AJAX (Asynchronous Javascript and XML) запросов, для этого необходимо установить его в значение «XMLHttpRequest».
Authorization
Если серверу необходима авторизация пользователя, он об этом прямо сообщает, а браузер предлагает ввести логин и пароль. Именно в заголовке Authorization они передаются в виде «Basic base64(user:pass)». Такую авторизацию намного удобней брутить, чем те, которые располагаются непосредственно на странице (POST).
Cookie
Собственно, в этом заголовке посылаются (внезапно) куки. Для танкистов поясню: это информация, которую сайт сохраняет на компьютере клиента. Подмена данного заголовка полезна тогда, когда изменение кукисов невозможно другим образом.
Применение
Итак, практика. Я проводил исследование, в ходе которого некоторое время использовал «пассивное обнаружение уязвимостей». Суть в том, что достаточно однажды выставить замену заголовков, а после – лишь ловить алерты и исследовать бреши.
Заголовок | Значение | Включен |
User-Agent Opera/10.60 | <script>alert(document.cookie)</script> | Да |
Referer | <script>alert(document.cookie)</script> | Да |
X-Forwarded-For | <script>alert(document.cookie)</script> | Да |
Accept-Language | en,en-US;q=0.9 | Нет |
Authorization | Basic MScyMzo0JzU2 | Нет |
X-Requested-With | XMLHttpRequest | Нет |
Cookie | PHPSESSID=! | Нет |
С первыми тремя, думаю, понятно. Если в ладах с английским, включи на постоянную подмену Accept-Language, дабы принимали за англичанина. Authorization уместно включать только при проверке конкретного сайта, ибо рискуете не понять, если где-нибудь действительно понадобится авторизация. Про применение заголовков X-Requested-With и Cookie я уже писал, однако поясню последний. В PHP довольно удобно хранить данные в сессиях: собственно данные – на сервере, у клиента – только идентификатор в куке «PHPSESSID» (название можно менять, но делают это, естественно, редко). Так вот, иногда этот идентификатор может состоять только из символов a-z, A-Z, 0-9 и '-,', и при передаче чего-то иного вызывается ошибка, раскрывающая абсолютный путь:
Warning: session_start() [function.session-start]: The session id contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in /var/www/data/www/login.php on line 2
Первое, на что я наткнулся – это поплывшая разметка. Да, действительно на многих сайтах происходил выход из какого-нибудь тега (бесполезный), и дальше образовывалась каша. Если ты решишь провести собственно исследование – будь готов. Еще одно: если чувствуешь, что что-то идет не так (например, не дают скачать файл, не показывают картинку во всю ширину, не работает перенаправление), выключай подмену Referer’а.
Никто не знает и не узнает, сколько алертов выловили админы, сколько они их увидели у себя в логах, сколько осталось незамеченными... Однако один случай обнаружения интересной активной XSS есть точно – гугловский сервис FeedBurner, который умеет обрабатывать RSS-фиды и логировать трафик сайта. Но последнее делал он не слишком качественно – не фильтровался Referer.
Довольно весело было заходить на всякие сайты с DLE (DataLife Engine), ибо популярный модуль DLE Referer Module не дружил (и не дружит) с экранированием плохих символов.
Некоторое время назад на php.ru можно было наблюдать ошибки нефильтрации Referer’а и XFF. На данный момент уязвимость закрыта. Из ошибки с реферером:
MySQL Error = You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"')' at line 1
SQL = INSERT INTO oops_sessions (ID,UID,START,LAST,IPS,PAGES,PAGE,DATA,REFFER) VALUES ('dpdu7rh90ehfsc62','0',1238958331,1238958331,'xxx.xxx.xxx.xxx',1,'/','a:1:{s:8:"USERNAME";s:10:"Гость";}','SQL-Inj'here')
Подмена заголовков в PHP
Естественно, после ручного анализа SQL-уязвимости наступает работа автоматики. Подбор столбцов, полей, вынимание логинов, паролей и хешей – все уже давно делают скрипты. Однако большинство из них направлено на GET, POST или Cookie. Покажу, как можно получить страницу, посылая нужные заголовки. Предположим, у нас есть массив с такими заголовками и вызов функции request, которая должна возвращать страницу:
$headers = array (
'User-Agent: Babytoy/0.5',
'Referer: http://refrefref.ref/omg.pl'
);
$html = request_socket('http://127.0.0.1/showmeheaders.php', $headers);
echo $html;
Есть несколько основных видов получения страницы из PHP:
Сокет
Заголовки напишешь в любом случае. Генерация пакета:
$packet = "GET {$url} HTTP/1.1rn"
. "Host: {$host}rn"
. implode("rn", $headers) . "rn"
. "Connection: Closernrn";
- file_get_contents()
Воспользуемся контекстом для задания нужных параметров:
$opts = array (
'http' => array (
'header' => implode("rn", $headers) . "rn"
)
);
$context = stream_context_create($opts);
return file_get_contents($url, false, $context);
Curl
В curl’е все проще простого: вместе с остальными параметрами достаточно указать curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
Заключение
Хотелось бы обратить внимание, что подмена заголовков – не панацея от сливания информации. Не стоит забывать о JavaScript’е, Flash’е и интерактивных элементах, которые тоже вправе много чего разболтать. Используй NoScript и прочие AdBlock’и. Всегда экспериментируй и прикладывай выдумку, ищи там, где не ищет никто. Удачи!
Ссылки:
RFC HTTP/1.1
Узнать предоставляемую информацию
Русский сайт Proxomitron'а
Tamper Data
Modify Headers
Все о User-Agent’е