#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.
1 2 |
$ cd ~/Downloads/ $ java -jar burpsuite*.jar |
После запуска, прокси-сервер Burp Suite должен прослушивать порт 8080. Настройте трафик Firefox на использование прокси-сервера Burp Suite следующим образом:
- В Firefox выберите Edit > Preferences. Должно появиться диалоговое окно «Дополнительно».
- Выберите вкладку «Сеть».
- Нажмите «Настройки…», чтобы открыть диалоговое окно «Настройки подключения», как показано на рисунке ниже.
- Выберите «Настройка прокси вручную» и введите 127.0.0.1 в поле «Прокси HTTP» и 8080 в поле «Порт». Нажмите «ОК», а затем закройте диалоговое окно «Параметры подключения».
Теперь все запросы, отправленные через Firefox, должны сначала направляться через Burp Suite. (Чтобы проверить это, перейдите на http://google.com/; Вы должны увидеть запрос на панели запросов Burp Suite, как показано на рисунке ниже.)
Нажатие кнопки «Переслать» в Burp Suite должно переслать
запрос (в данном случае в Google) и возврат ответа в Firefox.
Написание фаззера POST-запроса
Мы напишем и протестируем наш фаззер POST-запросов на странице «Что нового» BadStore (см. рисунок ниже). Перейдите на эту страницу в Firefox и щелкните пункт меню «Что нового» слева.
Кнопка внизу страницы используется для добавления отмеченных товаров в корзину. Установив Burp Suite между Вашим браузером и сервером BadStore, выберите несколько товаров, используя флажки в правой части страницы, а затем нажмите «Отправить», чтобы инициировать HTTP-запрос на добавление товаров в корзину. Перехват запроса на отправку в Burp Suite должен привести к запросу, подобному листингу 2-16.
1 2 3 4 5 6 7 8 9 10 11 |
POST /cgi-bin/badstore.cgi?action=cartadd HTTP/1.1 Host: 192.168.1.75 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:20.0) Gecko/20100101 Firefox/20.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: https://192.168.1.75/cgi-bin/badstore.cgi?action=whatsnew Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 63 cartitem=1000&cartitem=1003&Add+Items+to+Cart=Add+Items+to+Cart |
Запрос, показанный в листинге 2-16, представляет собой типичный запрос POST с параметрами, закодированными в URL-адресе (набор специальных символов, некоторые из которых являются пробелами и символы новой строки). Обратите внимание, что в этом запросе вместо пробелов используются знаки плюс (+). Сохраните этот запрос в текстовый файл. Мы будем использовать его позже для систематического фаззинга параметров, отправляемых в HTTP-запросе POST.
Параметры HTTP-запроса POST включаются в последнюю строку запроса, которая определяет данные, публикуемые в форме «ключ-значение». (Некоторые запросы POST отправляют составные формы или другие экзотические типы данных, но общий принцип остается прежним.)
Обратите внимание, что в этом запросе мы добавляем в корзину товары с идентификаторами 1000 и 1003. Теперь посмотрите на окно Firefox, и Вы заметите, что эти числа соответствуют столбцу ItemNum. Мы публикуем параметр вместе с этими идентификаторами, по сути сообщая приложению, что делать с данными, которые мы отправляем (а именно, добавлять товары в корзину). Как видите, единственные параметры, которые могут быть подвержены SQL-инъекции, — это два параметра caritem, поскольку именно эти параметры будут интерпретироваться сервером.
Фаззинг начинается
Прежде чем мы начнем фаззинг параметров нашего POST-запроса, нам нужно настроить немного данных, как показано в листинге 2-17.
Читаем запрос из файла с помощью 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.
В листинге 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.
1 2 |
$ mono POST_fuzzer.exe /tmp/request Parameter cartitem=1000 seems vulnerable to SQL injection with value: 1000' Parameter cartitem=1003 seems vulnerable to SQL injection with value: 1003' $ |
Как мы видим из результатов фаззера, HTTP-параметр caritem кажется уязвимым для SQL-инъекции. Когда мы вставляем апостроф в текущее значение параметра HTTP, мы получаем ошибку SQL в ответе HTTP, что делает его весьма уязвимым для атак с использованием SQL-инъекций.
На этом все. Всем хорошего дня!