Часто при аудите веб-приложений требуется проанализировать ответ веб-сервера и на основе его анализа сделать какие-то выводы (желательно в сводную таблицу результатов). Burp Intruder — популярный инструмент для комбинированных атак на параметры HTTP-запроса. Но кроме извлечения данных из HTML-ответов по простейшей регулярке, он не умеет никак их анализировать. Исправим это и прикрутим к Intruder свой кастомный процессинг ответов сервера на Python.

Задача

Для демонстрации подхода возьмем простую задачу: есть форум, который по URL вида http://forum.local/groups/<ID>/users/ выдает список юзеров из группы с ID = . Список ID групп есть, их около сотни. Задача: найти группу с наибольшим количеством юзеров, а остальные отсортировать по убыванию.

GET /groups/1/users/ HTTP/1.1
Host: forum.local
По URL с ID категории отдается таблица со списком юзеров
По URL с ID категории отдается таблица со списком юзеров

Ответ сервера:

...
<table class="users">
  <tbody>
    <tr class="user">
      <td class="name">Etha Marquardt</td>
      <td class="email">Cyrus.Kemmer@yahoo.com</td>
      <td class="adress">22579-1558, Macedonia, 528 Heathcote Mount</td>
    </tr>
    <tr class="user">
      <td class="name">Alexzander Ritchie I</td>
      <td class="email">Alejandra.Frami86@yahoo.com</td>
      <td class="adress">62299, Cuba, 147 Hudson Plains</td>
    </tr>
    ...
  </tbody>
</table>
...

Очевидно, нам нужно распарсить HTML-страничку и подсчитать количество <tr class="user">, а затем вывести это значение в таблицу результатов. Burp не предоставляет возможностей стороннего постпроцессинга ответов. Научим его!

Решение

Идея состоит в том, чтобы:

  1. Похукать момент получения ответа сервера.
  2. Обработать его своим кодом.
  3. Добавить результат вычислений в тот же самый ответ в специальном формате и отправить «дальше».
  4. Дальше грепнуть это значение по регулярке штатными средствами Burp Intruder.

Чтобы реализовать этот трюк, нужно написать расширение для Burp Suite. Расширения для Burp пишутся на Java, Ruby или Python. Мы напишем на Python.

Для начала нужно установить Jython. Скачай его, разархивируй в любую папку и укажи Бурпу путь к бинарнику во вкладке Extender => Options => Python environment.

Настраиваем окружение для исполнения Python-расширений в Burp
Настраиваем окружение для исполнения Python-расширений в Burp

Для разбора HTML нужно поставить HTML-парсер. Я буду использовать Beautiful Soup. Ставь через pip, но учти, что нужно пользоваться не системным pip’ом, а jython’овским, который лежит в директории бинарника jython (см. предыдущий скрин):

user@localhost:~/jython2.7.0/bin$ ./pip install beautifulsoup4
...

После этого создавай файл response_processor.py и добавляй следующий код:

from burp import IBurpExtender
from burp import IHttpListener
from burp import IHttpRequestResponse
from burp import IResponseInfo
from bs4 import BeautifulSoup

class BurpExtender(IBurpExtender, IHttpListener):
    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        self._callbacks.setExtensionName("Count recipies")
        callbacks.registerHttpListener(self)

    def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
        if not messageIsRequest: # only handle responses
            response = messageInfo.getResponse() # get Response from IHttpRequestResponse instance
            responseStr = self._callbacks.getHelpers().bytesToString(response)
            responseParsed = self._helpers.analyzeResponse(response)

            # body
            body = responseStr[responseParsed.getBodyOffset():]
            soup = BeautifulSoup(body, 'html.parser')
            users = soup.findAll("tr", { "class" : "user" })
            body += '\n<!--USERS:%s-->' % len(users)

            # headers
            headers = responseParsed.getHeaders()
            headers.add('X-Custom-Users: %s' % len(users))

            # combine
            httpResponse = self._callbacks.getHelpers().buildHttpMessage(headers, body)
            # set
            messageInfo.setResponse(httpResponse)

            return

Кратко:

  1. Получаем body.
  2. Парсим его содержимое.
  3. Считаем количество tr’ов класса user.
  4. Добавляем к body строчку !--USERS:N-->', где N — количество юзеров.

Также в блоке есть пример модификации заголовков HTTP-ответа. Правильнее передавать небольшие значения через X-заголовки, но для демонстрации сойдет и так. Больше комментариев у автора оригинального скрипта на Гитхабе.

Загружаем наше расширение в Burp в соответствующей вкладке.

Активируем наше расширение
Активируем наше расширение

Пробуем запустить Intruder с ним и видим, что теперь в body и headers дописываются нужные данные.

Модифицированный body
Модифицированный body

Теперь остается только грепнуть это значение по простейшей регулярке, и вуаля! Обрати внимание, что результаты грепаются как строка, это будет влиять на сортировку.

Разумеется, пример с подсчетом DOM-элементов чисто умозрительный. В реальности ты можешь проводить абсолютно любой постпроцессинг данных с использованием всей мощи Python и пробрасывать данные в таблицу результатов таким нехитрым трюком. Главное, экранируй большие данные, иначе регулярки могут неправильно сработать.

Кстати, перед тем, как писать эту заметку, я засабмиттил тикет разработчику Burp, PortSwigger. В ответ они подтвердили, что штатными средствами или через расширение это сделать нельзя:

You’re right, there isn’t any way to do this natively within Burp. And currently, there is no way for an extension to provide additional data columns in the Intruder attack results.

Как видишь, иногда достаточно проявить немного смекалки и взглянуть на задачу с другой стороны, чтобы найти решение. Удачи 🙂

8 комментариев

  1. Аватар

    clicker314

    06.10.2016 в 13:47

    Годная статья. Спасибо!

  2. Аватар

    vasian_adidas

    06.10.2016 в 15:42

    Желаю рака всем кто принимал активное участие в становлении хакера как платной параши. Пол страны на дошикиках экономит, а у них подписка 4к

    • Аватар

      clicker314

      06.10.2016 в 15:56

      Бесплатно только мухи серут — но толку от этого мало.

      Я бы и 10к платил без проблем, если-бы форум нормальный сделали.

    • Ilya Rusanen

      Илья Русанен

      06.10.2016 в 18:02

      «Хакер» всегда был платным. Ну и да, авторы за «Дошики» статьи писать не рвутся. Может, ты станешь первым?)

  3. Аватар

    aquamoney

    09.10.2016 в 21:08

    Я, может, что-то не понял, но неужели не проще сделать эту задачу на NodeJS с использованием библиотеки request и jquery? Зачем городить вот такое?

    • Ilya Rusanen

      Илья Русанен

      09.10.2016 в 21:42

      Привет!

      Я писал:

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

      Это просто синтетический пример для демонстрации техники работы с хуками Burp.

      На Node.js, разумеется, можно. Можно и с request,и без request, и без jquery и даже даже без Node.js =).

      Но во время аудита для типовых задач чаще используются готовые тулзы, а не пишутся свои. А так-то да, я только за пожонглировать коллбеками)

  4. Аватар

    madglow

    09.02.2017 в 08:32

    Возможно я придираюсь, но «Скачай его, разархивируй в любую папку» не равно «Скачай его и проинсталируй». Просто распакованный архив(в отличии от установленного Jython) не содержит pip в /bin.

  5. Аватар

    r0uly

    29.05.2019 в 11:05

    Отличная статья! Всем, кто когда-либо писал свой плагин для бурпа и сталкивался с проблемой идентификации «особых» ответов сервера — эта статья будет полезна.

Оставить мнение