Fuzzer, Gray Hat C#

#11 Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности. Написание фаззера POST-запросов.

Здравствуйте, дорогие друзья.

В этом разделе мы будем использовать BadStore для фаззинга параметров POST-запроса (запроса, используемого для отправки данных на веб-ресурс для обработки), сохраненного на локальном жестком диске. Мы перехватим POST-запрос с помощью Burp Suite — простого в использовании HTTP-прокси, созданного для исследователей безопасности и пентестеров, который находится между Вашим браузером и HTTP-сервером, чтобы вы могли видеть данные, отправляемые туда и обратно.
Загрузите и установите Burp Suite прямо сейчас с http://www.portswigger.net/. (Burp Suite — это Java-архив или JAR-файл, который можно сохранить на флэш-накопителе или другом портативном носителе.) После загрузки Burp Suite запустите его с помощью Java с помощью команд, показанных в листинге 2-15.

После запуска, прокси-сервер Burp Suite должен прослушивать порт 8080. Настройте трафик Firefox на использование прокси-сервера Burp Suite следующим образом:

  1. В Firefox выберите Edit > Preferences. Должно появиться диалоговое окно «Дополнительно».
  2. Выберите вкладку «Сеть».
  3. Нажмите «Настройки…», чтобы открыть диалоговое окно «Настройки подключения», как показано на рисунке ниже.
Figure 2-4: The Connection Settings dialog
  1. Выберите «Настройка прокси вручную» и введите 127.0.0.1 в поле «Прокси HTTP» и 8080 в поле «Порт». Нажмите «ОК», а затем закройте диалоговое окно «Параметры подключения».
    Теперь все запросы, отправленные через Firefox, должны сначала направляться через Burp Suite. (Чтобы проверить это, перейдите на http://google.com/; Вы должны увидеть запрос на панели запросов Burp Suite, как показано на рисунке ниже.)
Burp Suite actively capturing a request for google .com from Firefox

Нажатие кнопки «Переслать» в Burp Suite должно переслать
запрос (в данном случае в Google) и возврат ответа в Firefox.


Написание фаззера POST-запроса

Мы напишем и протестируем наш фаззер POST-запросов на странице «Что нового» BadStore (см. рисунок ниже). Перейдите на эту страницу в Firefox и щелкните пункт меню «Что нового» слева.

The “What’s New” items page of the BadStore web application

Кнопка внизу страницы используется для добавления отмеченных товаров в корзину. Установив Burp Suite между Вашим браузером и сервером BadStore, выберите несколько товаров, используя флажки в правой части страницы, а затем нажмите «Отправить», чтобы инициировать HTTP-запрос на добавление товаров в корзину. Перехват запроса на отправку в Burp Suite должен привести к запросу, подобному листингу 2-16.


Запрос, показанный в листинге 2-16, представляет собой типичный запрос POST с параметрами, закодированными в URL-адресе (набор специальных символов, некоторые из которых являются пробелами и символы новой строки). Обратите внимание, что в этом запросе вместо пробелов используются знаки плюс (+). Сохраните этот запрос в текстовый файл. Мы будем использовать его позже для систематического фаззинга параметров, отправляемых в HTTP-запросе POST.

Параметры HTTP-запроса POST включаются в последнюю строку запроса, которая определяет данные, публикуемые в форме «ключ-значение». (Некоторые запросы POST отправляют составные формы или другие экзотические типы данных, но общий принцип остается прежним.)


Обратите внимание, что в этом запросе мы добавляем в корзину товары с идентификаторами 1000 и 1003. Теперь посмотрите на окно Firefox, и Вы заметите, что эти числа соответствуют столбцу ItemNum. Мы публикуем параметр вместе с этими идентификаторами, по сути сообщая приложению, что делать с данными, которые мы отправляем (а именно, добавлять товары в корзину). Как видите, единственные параметры, которые могут быть подвержены SQL-инъекции, — это два параметра caritem, поскольку именно эти параметры будут интерпретироваться сервером.


Фаззинг начинается

Прежде чем мы начнем фаззинг параметров нашего POST-запроса, нам нужно настроить немного данных, как показано в листинге 2-17.

The Main() method reading a POST request and storing the Host header
The Main() method reading a POST request and storing the Host header

Читаем запрос из файла с помощью File.ReadAllLines() [1], и
передаем первый аргумент приложению фаззинга в качестве аргумента ReadAllLines(). Мы используем ReadAllLines() вместо ReadAllText(), потому что нам нужно разделить запрос, чтобы получить из него информацию (а именно, заголовок Host) перед фаззингом. Прочитав запрос построчно в массив строк и получив параметры из последней строки файла [2], мы объявляем две переменные. Переменная хоста [3] хранит IP-адрес хоста, на который мы отправляем запрос. Ниже объявлен System.Text.StringBuilder [4], который мы будем использовать для построения полного запроса в виде одной строки.

Мы используем StringBuilder, потому что он более производителен, чем использование оператора += с базовым строковым типом (каждый раз, когда Вы вызываете оператор +=, Вы создаете новый строковый объект в памяти). В таком небольшом файле Вы не заметите разницы, но когда Вы имеете дело с большим количеством строк в памяти, Вы заметите. Использование StringBuilder создает в памяти только один объект, что приводит к гораздо меньшим затратам памяти.


Теперь мы просматриваем каждую строку запроса, который был ранее прочитан. Мы проверяем, начинается ли строка с «Host:», и, если да, присваиваем вторую половину строки хоста переменной хоста. (Это должен быть IP-адрес.) Затем мы вызываем функцию replace() [5] в строке, чтобы удалить завершающий \r, который может оставаться в некоторых версиях Mono, поскольку в IP-адресе нет \r. Наконец, мы добавляем строку с \r\n в StringBuilder. Построив полный запрос, мы присваиваем его новой строковой переменной с именем request. (Для HTTP Ваш запрос должен заканчиваться \r\n; в противном случае ответ сервера зависнет.)


Параметры фаззинга

Теперь, когда у нас есть полный запрос для отправки, нам нужно выполнить цикл и попытаться провести фаззинг параметров для SQL-инъекций. В этом цикле мы будем использовать классы System.Net.Sockets.Socket и System.Net.IPEndPoint. Поскольку у нас есть полный HTTP-запрос в виде строки, мы можем использовать базовый сокет для связи с сервером, вместо того, чтобы полагаться на библиотеки HTTP для создания запроса для нас. Теперь у нас есть все необходимое для фаззинга сервера, как показано в листинге 2-18.

Additional code added to Main() method fuzzing the POST parameters

В листинге 2-18 мы создаем новый объект IPEndPoint [1], передавая новый объект IPAddress, возвращаемый IPAddress.Parse(host), и порт, к которому мы будем подключаться, по IP-адресу (80). Теперь мы можем перебирать параметры, полученные ранее из переменной requestLines. Для каждой итерации нам нужно создать новое соединение Socket [2] с сервером, и мы используем AddressFamily.InterNetwork, чтобы сообщить сокету, что это IPv4 (версия 4 Интернет-протокола, в отличие от IPv6), и используем SocketType.Stream, чтобы сообщить сокету, что это потоковый сокет (с сохранением состояния, двусторонний и надежный). Мы также используем ProtocolType.Tcp, чтобы сообщить сокету, что используемый протокол — TCP.
Как только экземпляр этого объекта будет создан, мы можем вызвать Connect() [3], передав наш объект IPEndPoint rhost в качестве аргумента. После того, как мы подключились к удаленному хосту через порт 80, мы можем начать фаззинг параметра. Мы разделяем параметр из цикла foreach на знак равенства (=) [4], и извлекаем значение этого параметра, используя значение во втором индексе массива (полученное в результате вызова метода). Затем мы вызываем функцию replace() [5] в строке запроса, чтобы заменить исходное значение испорченным. Например, если наше значение — «foo» в строке параметров «blah=foo&blergh=bar», мы заменим foo на foo’» (обратите внимание на апостроф, добавленный в конце из foo).

Затем мы получаем массив байтов, представляющий строку, используя Encoding.ASCII .GetBytes() [6], и отправляем его через сокет [7] на порт сервера, указанный в конструкторе IPEndPoint. Это эквивалентно запросу вашего веб-браузера на URL-адрес в адресной строке.
После отправки запроса мы создаем массив байтов, равный размеру ответа, который мы получим, и заполняем его ответом от сервера с помощью метода Receive() [8]. Мы используем Encoding.ASCII.GetString() [9], чтобы получить строку, которую представляет массив байтов, а затем можем проанализировать ответ от сервера. Мы проверяем ответ от сервера, проверяя, содержится ли в данных ответа ожидаемое сообщение об ошибке SQL.


Наш фаззер должен выводить любые параметры, которые приводят к ошибкам SQL, как показано в листинге 2-19.

Как мы видим из результатов фаззера, HTTP-параметр caritem кажется уязвимым для SQL-инъекции. Когда мы вставляем апостроф в текущее значение параметра HTTP, мы получаем ошибку SQL в ответе HTTP, что делает его весьма уязвимым для атак с использованием SQL-инъекций.

На этом все. Всем хорошего дня!

Цикл статей по Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности.