D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Автор petrinh1988
Источник https://xss.is
BurpSuite- отличный программный комплекс, который включает массу инструментов для хакинга. В том числе, есть достаточно большое количество расширений, которые сильно облегчают жизнь хакеру и пентестеру. Но как бы много не было доступных инструментов, всегда чего-то будет не хватать. Поэтому, предлагаю погрузиться в вопрос разработки собственных расширений для Burp.
В статье разберу основные моменты разработки и сделаю несколько демонстрационных расширений, которые можно будет сразу использовать или взять за основу для доработки. Статья не имеет цели повторить справку или закрыть все вопросы, скорее это путеводитель в мир разработки для Burp. При этом, постарался показать максимум вариантов взаимодействия.
Для разработки будет использоваться Python, вернее его реализация Jython. Нет, конечно можно все сделать по феншую, все же Burp написан на Java и подразумевается его кастомизация на Java, но мои навыки в java….короче не хочу, чтобы текла кровь из глаз настоящих java-программистов. Хотя у питонистов тоже будет кровь из глаз, особенно когда я буду вольно называть списки массивами и наоборот.
Важно обсудить один нюанс… у Burp есть два API: Extender API и Montoya API. Причем, API Extender поддерживает Jython, но является устаревшим, т.е. его поддержка когда-то будет прекращена. Это первое API, которое появилось вместе с самим BurpSuite. Но никто не называет сроков, когда от него откажутся. Даже на официальном форуме администрация не может дать четкого ответа по срокам. Возможно никогда.
В свою очередь, Montoya API работает исключительно с jar. Для этого апи можно писать на Jython, но это несколько проблематично.
Что касается самих API, четкого сравнения между ними компания-разработчик, не предоставляет. Cудя по тому, что пишут в интернет, старый апи несет в себе ряд недостатков. Например, отсутствует функционал для анализа веб-сокетов, хотя BurpSuite поддерживает это уже много лет. Или сложности с получением тела HTTP-запроса, так как приходится считать смещение для его получения.
Да, функционал Montoya шире, некоторые процессы с ним выполняются легче. Но, для огромного количества решений, вполне хватит и старой версии API. На нем продолжают создавать новые расширения. Поэтому, в рамках этой статьи буду писать на Jython. Кроме того, Python более доступен для освоения и большинство пентестеров на нем мало-мало пишут.
Если тема с расширениями для BurpSuite будет интересна жителям XSS.is, то в будущем придется сделать выбор… либо собирать готовый JAR из кода Jython, либо переходить на Java.
Обсудим основы, чтобы не возникло путаницы при написании полноценных расширений. Вот как выглядит код расширения, которое просто загрузится и выведет надпись об этом в лог:
Python: Скопировать в буфер обмена
Ничего сложного. Импортировали основной интерфейс, который реализовали создав соответствующий класс. Далее реализовали функцию registerExtenderCallbacks, которая запускается при загрузке расширения. В функции, установили название расширения и вывели текст в окошко BurpSuite.
Чтобы посмотреть, как оно работает, достаточно зайти в Extensions -> Installed, нажать Add, выбрать тип расширения Python и указать путь к файлу. Если не получается добавить расширение, проверьте указан ли путь к файлу jython.jar (см. выше, настройка jython)
Выше реализован просто скелет, который никак не привязан к Burp и не может с ним взаимодействовать. Чтобы это исправить, нужно модифицировать функцию registerExtenderCallbacks(), добавив туда две строчки:
Python: Скопировать в буфер обмена
Таким образом мы получим доступ к интерфейсам IBurpExtenderCallbacks и IExtensionHelpers, тем самым получив возможность полноценно взаимодействовать с Burp, вызывая стандартные методы и функции.
Например, метод issueAlert из IBurpExtenderCallbacks, который позволяет вывести сообщение в стандартное окно вывода Burp. Или хелпер analyzeResponse, позволяющий получить данные ответа для дальнейшего анализа.
Есть смысл разобрать, как расширение может взаимодействовать с различными видами сканирования. Для этого потребуется интерфейс IScannerCheck и реализация его трех методов: doActiveScanь(), doPassiveScan() и consolidateDuplicateIssues().
doPassiveScan(IHttpRequestResponse baseRequestResponse)
Пассивное сканирование не предполагает, что мы делаем какие-то запросы, которые не предусмотрены сайтом или имеют какой-то payload. Метод принимает объект совмещающий в себе информацию, как про запрос к серверу, так и про полученный ответ. Возвращает список найденных уязвимостей, которые будут добавлены в общие результаты. Уязвимости должны быть объектами IScanIssue (об этом ниже).
doActiveScan(IHttpRequestResponse baseRequestResponse, IScannerInsertionPoint insertionPoint)
Активное сканирование предполагает дополнительные, модифицированные запросы с нашими пэйлоадами, поэтому вместе с результатом выполненного запроса, методу передается объект описывающий точку инъекции. На основе точки инъекции, мы можем произвести модификацию, сформировав новый подобный запрос. Результатом выполнения, как и при пассивном сканировании, будет список найденных уязвимостей. .
consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue)
Эту функцию Burp вызывает, чтобы понять, не является ли новая уязвимость дубликатом уже существующей. Наша задача выстроить работу расширения таким образом, чтобы был способ однозначно идентифицировать дубли. Функция получает два объекта уязвиомсти, которые нужно сравнить. На выходе, если уязвимости не совпадают, то возвращается 0, иначе -1.
Что за дубли? Например, наше расширение, в HTTP-ответах сервера, обнаружило end-point’ы приватного API. Каждый отличающийся end-point, это отдельная потенциальная уязвимость, а не дубль. Напоминаю, что уязвимость представляется классом IScanIssue, у которого есть метод getIssueName() возвращающий имя. Значит мы можем каждой уязвимости назначить имя по шаблону “Private API end-point: /api/method/name”, где вместо “/api/method/name” будет конкретный ендпоинт. Тогда реализация функции проверки дублей может иметь вид:
Простой пример реализации, основанный на сравнении имен:
Python: Скопировать в буфер обмена
Иная история, когда уязвимость должна иметь более делатльное описание. Тогда название мы можем делать одинаковым, а для сравнения использовать детали уязвимости.
Самое время поговорить, как же надо реализовать интерфейс IScanIssue, благодаря которому мы можем добавить в BurpSuite найденные уязвимости. Что активное, что пассивное сканирование, должно вернуть список уязвимостей, поэтому обойтись без этого интерфейса никак не получится.
IScanIssue это интерфейс, реализация которого представляет собой класс, структурированно хранящий нужную информацию. На скриншоте ниже весь список методов, которые должны быть в классе. По ним, соответственно, можно понять какие значения и какого типа нужно хранить.
IHttpRequestResponse[] и IHttpService коснусь ниже, при реализации расширений. Данные значения получаются из специального объекта и определенным образом подготавливаются. Остальные значения можно передавать в конструкторе класса или хардкодить в ответах. Например, если вы знаете, что риск найденной уязвимости на 100% всегда будет высоким, метод getSeverity() можно реализовать так:
Python: Скопировать в буфер обмена
Пример того, как может выглядеть полноценный скелет расширения, которое реализует сканирование:
Python: Скопировать в буфер обмена
Не стоит переживать, что какие-то моменты сейчас могут быть непонятны. Чтобы это исправить, приведу примеры нескольких расширений.
Время объединить полученные знания, добавив некоторые команды и собрать живой проект. Для этого оптимально подойдет пассивный сканер, который будет искать чувствительную информацию в HTML-ответах. Например, случайно просочившиеся данные для подключения к базам. Подобное случается из-за ошибок разработчиков.
Принцип работы расширения: BurpSuite, при сканировании, делает запросы к таргету. Ответы проходят через все зарегистрированные сканеры, в том числе и наш пассивный сканер. Сканер регуляркой ищет данные. Если данные найдены, добавляет уязвимость.
Начну с импортов. Здесь ничего нового, что касалось бы Burp:
Python: Скопировать в буфер обмена
Интерфейс расширения, интерфейс чекера и интерфейс уязвимости. Все это уже было выше в скелете. Разве что “re” не было, но он потребуется для работы с регулярными выражениями.
Создам класс и добавлю в него нужные функции, как болванки:
Python: Скопировать в буфер обмена
Python: Скопировать в буфер обмена
Вот как будет выглядеть уязвимость в данном случае:
Name: Found db_password
Details: Maybe found db_password value: p@ssw0rD
За получение описания уязвимости отвечает метод getIssueDetail, который должен быть реализован в классе уязвимости реализующим интерфейс IScanIssue. Соответственно, итоговая функция consolidateDuplicateIssues будет выглядеть следующим образом:
Python: Скопировать в буфер обмена
Python: Скопировать в буфер обмена
Отличия от примера минимальные. В конструкторе передается большее количество параметров, которые класс хранит в себе, и возвращает не в виде хардкода. При необходимости, можно было бы добавить severety, как варьирующийся параметр, вместо жесткого прописанного “Information”. Например, если бы мы разделили найденные данные по разным уровням критичности — только логин “Low”, а логин и пароль “High”.
Если прочитать код класса вместе с функцией consolidateDuplicateIssues, становится понятно, что сначала мы в класс через конструктор закинем что-то вроде “Maybe found db_pass value p@ssw0rD”. После, когда наш сканер будет создавать очередную уязвимость и поместит в нее, например, “Maybe found db_login value shop_admin”, BurpSuite вызовет функцию сравнения, увидит что строки разные и добавит обе уязвимости в отчет.
В первую очередь, потребуется словарь ключевых слов, которые нас интересуют. Чтобы не захламлять статью, привожу укороченный вариант словаря. Более длинный смотрите в итоговом файле. Конечно же, его можно расширять, главное не потерять голову иначе проверка может стать излишне тяжелой.
Python: Скопировать в буфер обмена
Объявил также пустой список для уязвимостей. Если помнишь, функция doPassiveScan() должна возвращать список уязвимостей.
Следующим шагом будет получение самого респонса, который прилетает в функцию из BurpSuite. Для этого вызову функцию getResponse() объекта baseRequestResponse, который получаю как параметр функции doPassiveScan().
Python: Скопировать в буфер обмена
Функция getResponse() возвращает массив байт, поэтому потребуется преобразовать его в строку. Для этого отлично подойдет функция bytesToString(), которую предоставляют хелперы Burp.
Python: Скопировать в буфер обмена
Сохраню url по которому Burp выполнял запрос. Для этого, обращусь к хелперу и вызову функцию analyzeRequest(). Она возвращает IRequestInfo, среди методов которого есть getUrl(). Советую посмотреть справку, там есть и другие методы, которые возвращают все необходимое относящееся к запросу (заголовки, параметры и т.п.)
Python: Скопировать в буфер обмена
Логичным продолжением будет обойти все наши кейворды. На основе каждого ключевого слова создаем поиск по тексту ответа. Если есть совпадения, добавляем их как объекты DBCredsIssue (наш класс уязвимостей) в список уязвимостей. В конце просто возвращаем список готовых уязвимостей.
Но сначала стоит подумать над регулярным выражением. Как могут быть записаны нужные данные? На самом деле, вариаций великое множество. Это может быть, например: db_pass: p@ssw0rD. Без кавычек, разделенные двоеточием и пробелом. Данные могут быть взяты в кавычки, как отдельно, так и целиком. Могут быть разделены символом равно, прямой чертой или, чем черт не шутит, @. Исходя из этого, решил составить регулярку из следующих частей:
Вот такой цикл получился:
Python: Скопировать в буфер обмена
Здесь нужно сделать остановку. На моменте добаления уязвимостей, есть несколько вариантов, как можно поступить. Мы можем просто вывести найденные данные. Для этого достаточно будет создать новый объект DBCredsIssue, передать в него данные по уязвимости и добавить в итоговый список.
Python: Скопировать в буфер обмена
Тест расширения:
Как видно, все прекрасно работает. Но, хочется же сделать качественное решение, чтобы было по-феншую. Как видно на скриншоте, с тестового сервера я получил значение testadministrator–>. В данном случае, –> это закрывающая комментарий скобка. Если не видеть оригинал строки, можно подобные артефакты принять за часть искомой строки и потом рвать волосы на голове, почему не подходят кредсы…
Для этого, потребуется добавить немного кода. А именно, прибегнуть к функции маркировки applyMarkers() из доступных коллбэков. Она принимает IHttpRequestResponse, а также позиции маркировки для запроса и ответа.
Java: Скопировать в буфер обмена
Позиции маркеров это список целочисленных массивов. Каждый массив должен содержать в себе два элемента: начало и конец выделения. Значит нужно зафиксировать каждое отдельное совпадение. Кроме того, потребуется импортировать пакет array, поэтому добавляю в импорты строчку:
Python: Скопировать в буфер обмена
Прямо перед циклом, который обходит найденные уязвимости, добавлю объявление списка, а первой строкой в цикле объявлю целочисленный массив :
Python: Скопировать в буфер обмена
mark_pos будет накапливать в себе все координаты для выделения, а offsets будет использоваться для фиксации каждого отдельного начала и конца строки:
Python: Скопировать в буфер обмена
Но перед тем, как сохранять координаты, их нужно определить. Для определения позиции, буду использовать indexOf из предлагаемых коллбэков. Ну и, для корректной работы, добавлю определение длины ответа.
Python: Скопировать в буфер обмена
Обрати внимание, что использую не строковое представление респонса, а байтовое. Как indexOf из коллюэков, так и функция маркировки, работают именно с байтами. Такой вот нюансик. Если когда-то будешь биться в конвульсиях от того, что все правильно, а выделяется совсем не то, вспомни про этот нюанс.
По поводу параметров функции indexOf, думаю достаточно будет привести ее объявление:
Java: Скопировать в буфер обмена
На всякий случай поясню, почему беру не нулевое значение списка совпадений issue_res[1]. Дело в том, что в регулярном выражении есть несколько групп поиска (если не в курсе, то что в круглых скобках выделяется в отдельные группы), которые помещаются в отдельные элементы списка. Поэтому в нулевой группе будет исключительно разделитель (например, “=”), а в первой искомое значение. Как вариант, использовать последнее значение issue_res[-1], тогда туда попадет, и нулевое и первое значение.
Итак, все готово, остается только поменять создание нового объекта уязвимости, заменив None на массив маркированных ответов:
Python: Скопировать в буфер обмена
Да, чтобы все правильно промаркировалось, нужно передать именно список Interface IHttpRequestResponseWithMarkers, поэтому вызов функции обернул в квадратные скобки [].
Время пробовать…
Уря, товарищи. Согласитесь стало гораздо удобнее и круче? На всякий случай, напоминаю, что функцией applyMarkers() можно разукрасить, как респонсы, так и реквесты. Делайте свои расширения максимально удобными и подробными!
Стандартно можно пользоваться возможностями вывода stdout и stderr на вкладке расширений. Просто переходим в расширения, выбираем свое и смотрим вниз на вкладки Output и Error.
Соответственно, в ошибки выплевывается все, что вызвало ошибку и, в большинстве случаев, мы можем увидеть какая именно строка не понравилась. Закладка Output показывает нам результат работы всех принтов в коде.
Важно понимать, что данный вывод относиться именно к расширению, а не к проекту.
Если нужен PDB, ничего не мешает нам подключить его. Достаточно импортнуть соответствующий пакет и в нужном месте вставить pdb.set_trace(). Плюс перенаправить вывод на системную консоль. Для этого, при добавлении расширения или в свойствах уже добавленного, нужно выбрать “Output to system console”. Как для Stdout, так и для Srderr.
Плюс добавить перенаправление вывода в коде:
Python: Скопировать в буфер обмена
Этот скрин сделан на Community-версии запущенной командой java -jar ...
Но здесь есть нюанс… «нежадный» Burp запускается через отдельный Java лаунчер. Возможно есть способ перенаправления, чтобы вывод Burp шел в консоль, но я как-то до этого не дошел. Хватало обычных print() и вывода ошибок в интерфейс.
Предлагаю попрактиковаться с выводом и написать простой логгер вызовов. Логгер поможет отследить логику работы Burp. Выглядеть он будет таким образом:
Python: Скопировать в буфер обмена
Сканер, который ничего не делает, просто демонстрирует, какие запросы делает BurpSuite при активном и пассивном сканировании.
Здесь появился новый интересный интерфейс IHttpListener. Благодаря ему, можно отслеживать любые HTTP-запросы и ответы сделанные любым инструментом Burp. Единственная функция, которую подразумевает реализация класса, это:
Java: Скопировать в буфер обмена
При этом, toolFlag указывает на то, к какому инструменту Burp относится активность (подробнее ниже на картинке). messgaIsRequest указывает на направление (запрос или ответ). Ну и уже знакомый IHttpRequestResponse, который содержит всю информацию о запросе/ответе.
Чтобы все полноценно работало, при загрузке расширения, нужно зарегистрировать слушателя:
Python: Скопировать в буфер обмена
Код: Скопировать в буфер обмена
В данном случае, мы видим имя параметра Referer и его значение. Но, например, если точка инъекции находится в url, то в имени мы увидели бы цифру. Эта цифра обозначала бы номер части пути, причем домен это 0. Пример, дял понимания:
Код: Скопировать в буфер обмена
Эта информация будет полезна при работе с активными сканированиями.
Все написанное выше можно было бы реализовать, как отдельные приложения без использования Burp. Но тут важно понимать, что такие программные комплексы, как Burp, Acunetix, A-Parser и прочие, в первую очередь, предоставляют инфраструктуру. Удобные инструменты, которые базово решают множество сопутствующих задач и позволяют структурировано хранить нужную информацию. Тем самым разгружая разработчика и позволяя сконцентрироваться на решении практических проблем, обогащая базовый функционал при помощи API. Приведенная в статье информация, это лишь вводная часть, которая поможет быстро влиться в разработку расширений.
К сожалению, статья и так получилась очень большой (как бы ни пытался урезать), при этом за кадром осталось огромное количество интересных тем: активное сканирование; пользовательский интерфейс расширений и свои панели в интерфейсе BurpSuite; работа с контекстным меню; генераторы пэйлоадов для интрудера… короче очень много интересного есть в BS, о чем есть смысл писать. Если материал будет интересен участникам сообщества, обязательно
Источник https://xss.is
BurpSuite- отличный программный комплекс, который включает массу инструментов для хакинга. В том числе, есть достаточно большое количество расширений, которые сильно облегчают жизнь хакеру и пентестеру. Но как бы много не было доступных инструментов, всегда чего-то будет не хватать. Поэтому, предлагаю погрузиться в вопрос разработки собственных расширений для Burp.
В статье разберу основные моменты разработки и сделаю несколько демонстрационных расширений, которые можно будет сразу использовать или взять за основу для доработки. Статья не имеет цели повторить справку или закрыть все вопросы, скорее это путеводитель в мир разработки для Burp. При этом, постарался показать максимум вариантов взаимодействия.
Для разработки будет использоваться Python, вернее его реализация Jython. Нет, конечно можно все сделать по феншую, все же Burp написан на Java и подразумевается его кастомизация на Java, но мои навыки в java….короче не хочу, чтобы текла кровь из глаз настоящих java-программистов. Хотя у питонистов тоже будет кровь из глаз, особенно когда я буду вольно называть списки массивами и наоборот.
Два разных API Burp
Важно обсудить один нюанс… у Burp есть два API: Extender API и Montoya API. Причем, API Extender поддерживает Jython, но является устаревшим, т.е. его поддержка когда-то будет прекращена. Это первое API, которое появилось вместе с самим BurpSuite. Но никто не называет сроков, когда от него откажутся. Даже на официальном форуме администрация не может дать четкого ответа по срокам. Возможно никогда.
В свою очередь, Montoya API работает исключительно с jar. Для этого апи можно писать на Jython, но это несколько проблематично.
Что касается самих API, четкого сравнения между ними компания-разработчик, не предоставляет. Cудя по тому, что пишут в интернет, старый апи несет в себе ряд недостатков. Например, отсутствует функционал для анализа веб-сокетов, хотя BurpSuite поддерживает это уже много лет. Или сложности с получением тела HTTP-запроса, так как приходится считать смещение для его получения.
Да, функционал Montoya шире, некоторые процессы с ним выполняются легче. Но, для огромного количества решений, вполне хватит и старой версии API. На нем продолжают создавать новые расширения. Поэтому, в рамках этой статьи буду писать на Jython. Кроме того, Python более доступен для освоения и большинство пентестеров на нем мало-мало пишут.
Если тема с расширениями для BurpSuite будет интересна жителям XSS.is, то в будущем придется сделать выбор… либо собирать готовый JAR из кода Jython, либо переходить на Java.
Настройка Jython в BurpSuite
Если у вас установлено хотя бы одно расширение для Burp, скорее всего, Jython уже подключен. Но вот инструкция, если этого не сделано:- Скачиваем Jython и кидаем куда удобно. Я закинул просто на диск «C:» да, да, я отношусь к чертовым фанатикам Windows, но клянусь окончательно сменить религию в ближайшее время
- В Burp идем в расширения, далее открываем Параметры и указываем путь к нашему jython в среде разработки Python. Для удобства стыбрил у янки картинку
База
Обсудим основы, чтобы не возникло путаницы при написании полноценных расширений. Вот как выглядит код расширения, которое просто загрузится и выведет надпись об этом в лог:
Python: Скопировать в буфер обмена
Код:
from burp import IBurpExtender
class BurpExtender(IBurpExtender):
def registerExtenderCallbacks(self, callbacks):
callbacks.setExtensionName("Simple Extension")
print( "Simple Extension extension loaded." )
return
Ничего сложного. Импортировали основной интерфейс, который реализовали создав соответствующий класс. Далее реализовали функцию registerExtenderCallbacks, которая запускается при загрузке расширения. В функции, установили название расширения и вывели текст в окошко BurpSuite.
Чтобы посмотреть, как оно работает, достаточно зайти в Extensions -> Installed, нажать Add, выбрать тип расширения Python и указать путь к файлу. Если не получается добавить расширение, проверьте указан ли путь к файлу jython.jar (см. выше, настройка jython)
Выше реализован просто скелет, который никак не привязан к Burp и не может с ним взаимодействовать. Чтобы это исправить, нужно модифицировать функцию registerExtenderCallbacks(), добавив туда две строчки:
Python: Скопировать в буфер обмена
Код:
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
Таким образом мы получим доступ к интерфейсам IBurpExtenderCallbacks и IExtensionHelpers, тем самым получив возможность полноценно взаимодействовать с Burp, вызывая стандартные методы и функции.
Например, метод issueAlert из IBurpExtenderCallbacks, который позволяет вывести сообщение в стандартное окно вывода Burp. Или хелпер analyzeResponse, позволяющий получить данные ответа для дальнейшего анализа.
Активное и пассивное сканирование
Есть смысл разобрать, как расширение может взаимодействовать с различными видами сканирования. Для этого потребуется интерфейс IScannerCheck и реализация его трех методов: doActiveScanь(), doPassiveScan() и consolidateDuplicateIssues().
doPassiveScan(IHttpRequestResponse baseRequestResponse)
Пассивное сканирование не предполагает, что мы делаем какие-то запросы, которые не предусмотрены сайтом или имеют какой-то payload. Метод принимает объект совмещающий в себе информацию, как про запрос к серверу, так и про полученный ответ. Возвращает список найденных уязвимостей, которые будут добавлены в общие результаты. Уязвимости должны быть объектами IScanIssue (об этом ниже).
doActiveScan(IHttpRequestResponse baseRequestResponse, IScannerInsertionPoint insertionPoint)
Активное сканирование предполагает дополнительные, модифицированные запросы с нашими пэйлоадами, поэтому вместе с результатом выполненного запроса, методу передается объект описывающий точку инъекции. На основе точки инъекции, мы можем произвести модификацию, сформировав новый подобный запрос. Результатом выполнения, как и при пассивном сканировании, будет список найденных уязвимостей. .
consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue)
Эту функцию Burp вызывает, чтобы понять, не является ли новая уязвимость дубликатом уже существующей. Наша задача выстроить работу расширения таким образом, чтобы был способ однозначно идентифицировать дубли. Функция получает два объекта уязвиомсти, которые нужно сравнить. На выходе, если уязвимости не совпадают, то возвращается 0, иначе -1.
Что за дубли? Например, наше расширение, в HTTP-ответах сервера, обнаружило end-point’ы приватного API. Каждый отличающийся end-point, это отдельная потенциальная уязвимость, а не дубль. Напоминаю, что уязвимость представляется классом IScanIssue, у которого есть метод getIssueName() возвращающий имя. Значит мы можем каждой уязвимости назначить имя по шаблону “Private API end-point: /api/method/name”, где вместо “/api/method/name” будет конкретный ендпоинт. Тогда реализация функции проверки дублей может иметь вид:
Простой пример реализации, основанный на сравнении имен:
Python: Скопировать в буфер обмена
Код:
def consolidateDuplicateIssues(self, existingIssue, newIssue):
if existingIssue.getIssueName() == newIssue.getIssueName():
return -1
return 0
Иная история, когда уязвимость должна иметь более делатльное описание. Тогда название мы можем делать одинаковым, а для сравнения использовать детали уязвимости.
Самое время поговорить, как же надо реализовать интерфейс IScanIssue, благодаря которому мы можем добавить в BurpSuite найденные уязвимости. Что активное, что пассивное сканирование, должно вернуть список уязвимостей, поэтому обойтись без этого интерфейса никак не получится.
IScanIssue это интерфейс, реализация которого представляет собой класс, структурированно хранящий нужную информацию. На скриншоте ниже весь список методов, которые должны быть в классе. По ним, соответственно, можно понять какие значения и какого типа нужно хранить.
IHttpRequestResponse[] и IHttpService коснусь ниже, при реализации расширений. Данные значения получаются из специального объекта и определенным образом подготавливаются. Остальные значения можно передавать в конструкторе класса или хардкодить в ответах. Например, если вы знаете, что риск найденной уязвимости на 100% всегда будет высоким, метод getSeverity() можно реализовать так:
Python: Скопировать в буфер обмена
Код:
def getSeverity(self):
return “Hight”
Пример того, как может выглядеть полноценный скелет расширения, которое реализует сканирование:
Python: Скопировать в буфер обмена
Код:
from burp import IBurpExtender
from burp import IScannerCheck
from burp import IScanIssue
class BurpExtender(IBurpExtender, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("Simple Extension")
return
def doActiveScan(self, baseRequestResponse, insertionPoint):
pass
def doPassiveScan(self, baseRequestResponse):
pass
def consolidateDuplicateIssues(self, existingIssue, newIssue):
pass
class CustomScanIssue(IScanIssue):
def __init__(self, httpService, url, httpMessages):
self._httpService = httpService
self._url = url
self._httpMessages = httpMessages
def getConfidence(self):
# "Certain", "Firm" or "Tentative"
return "Certain"
def getHttpMessages(self):
return self._httpMessages
def getHttpService(self):
return self._httpService
def getIssueBackground(self):
pass
def getIssueDetail(self):
return "The response contains a custom vulnerability"
def getIssueName(self):
return "Custom vulnerability detected"
def getIssueType(self):
return 0
def getRemediationBackground(self):
pass
def getRemediationDetail(self):
pass
def getSeverity(self):
# "High", "Medium", "Low", "Information" or "False positive"
return "High"
def getUrl(self):
return self._url
Не стоит переживать, что какие-то моменты сейчас могут быть непонятны. Чтобы это исправить, приведу примеры нескольких расширений.
Сбор чувствительной информации
Важно!!! Иногда буду использовать комментарии в коде, чтобы не захламлять статью, но при этом сохранять максимально понятный код. Комментарии привожу только здесь, в тексте статьи. Никогда не используйте русские символы, даже в комментариях. У Burp крыша едет и расширение не работает.
Нажмите, чтобы раскрыть...
Время объединить полученные знания, добавив некоторые команды и собрать живой проект. Для этого оптимально подойдет пассивный сканер, который будет искать чувствительную информацию в HTML-ответах. Например, случайно просочившиеся данные для подключения к базам. Подобное случается из-за ошибок разработчиков.
Принцип работы расширения: BurpSuite, при сканировании, делает запросы к таргету. Ответы проходят через все зарегистрированные сканеры, в том числе и наш пассивный сканер. Сканер регуляркой ищет данные. Если данные найдены, добавляет уязвимость.
Начну с импортов. Здесь ничего нового, что касалось бы Burp:
Python: Скопировать в буфер обмена
Код:
from burp import IBurpExtender
from burp import IScannerCheck
from burp import IScanIssue
import re
Интерфейс расширения, интерфейс чекера и интерфейс уязвимости. Все это уже было выше в скелете. Разве что “re” не было, но он потребуется для работы с регулярными выражениями.
Создам класс и добавлю в него нужные функции, как болванки:
Python: Скопировать в буфер обмена
Код:
class BurpExtender(IBurpExtender, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):
pass
def consolidateDuplicateIssues(self, existingIssue, newIssue):
pass
def doPassiveScan(self, baseRequestResponse):
pass
registerExtenderCallbacks
Здесь, в дополнение к примеру, будет добавлен вызов registerScannerCheck(). Как понятно из названия, метод регистрирует наше расширение среди доступных Burp сканеров. В остальном, функция идентична примеру:Python: Скопировать в буфер обмена
Код:
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
self._callbacks.setExtensionName("DBCredsChecker")
self._callbacks.registerScannerCheck(self)
print("DBCredsChecker loaded")
print("Enjoy your use")
return
consolidateDuplicateIssues
Как писал выше, функция помогает Burp понять, является ли уязвимость дубликатом, сравнивая уязвимость с уже добавленной. В данном случае, оптимально будет сравнивать не название уязвимости, а описание. Дело в том, что учетные данные могут быть достаточно большими и пихать их в название уязвимости не очень удобно. Гораздо правильнее, в названии уязвимости указывать тип найденных данных, а сами данные отправить в описание.Вот как будет выглядеть уязвимость в данном случае:
Name: Found db_password
Details: Maybe found db_password value: p@ssw0rD
За получение описания уязвимости отвечает метод getIssueDetail, который должен быть реализован в классе уязвимости реализующим интерфейс IScanIssue. Соответственно, итоговая функция consolidateDuplicateIssues будет выглядеть следующим образом:
Python: Скопировать в буфер обмена
Код:
def consolidateDuplicateIssues(self, existingIssue, newIssue):
if (existingIssue.getIssueDetail() == newIssue.getIssueDetail()):
return -1
return 0
Класс DBCredsIssue
Раз уж коснулся самих уязвимостей, отойду немного от реализации самого сканера и создам класс самой уязвимости. Назову его DBCredsIssue. На всякий случай, уточняю - класс объявляю вне основного класса BurpExtender. Просто в самом конце файла.Python: Скопировать в буфер обмена
Код:
class DBCredsIssue(IScanIssue):
def __init__(self, httpService, url, httpMessage, name, description):
self._url = url
self._httpService = httpService
self._httpMessage = httpMessage
self._name = name
self._description = description
def getUrl(self):
return self._url
def getHttpMessages(self):
return self._httpMessage
def getHttpService(self):
return self._httpService
def getRemediationDetail(self):
pass
def getIssueDetail(self):
return self._description
def getIssueBackground(self):
pass
def getRemediationBackground(self):
pass
def getIssueType(self):
return 0
def getIssueName(self):
return self._name
def getSeverity(self):
return "Information"
def getConfidence(self):
return "Certain"
Отличия от примера минимальные. В конструкторе передается большее количество параметров, которые класс хранит в себе, и возвращает не в виде хардкода. При необходимости, можно было бы добавить severety, как варьирующийся параметр, вместо жесткого прописанного “Information”. Например, если бы мы разделили найденные данные по разным уровням критичности — только логин “Low”, а логин и пароль “High”.
Если прочитать код класса вместе с функцией consolidateDuplicateIssues, становится понятно, что сначала мы в класс через конструктор закинем что-то вроде “Maybe found db_pass value p@ssw0rD”. После, когда наш сканер будет создавать очередную уязвимость и поместит в нее, например, “Maybe found db_login value shop_admin”, BurpSuite вызовет функцию сравнения, увидит что строки разные и добавит обе уязвимости в отчет.
doPassiveScan
Собственно, основная функция нашего расширения, а по совместительству класса BurpExtender. В ней мы должны получить response, и произвести поиск по данным.В первую очередь, потребуется словарь ключевых слов, которые нас интересуют. Чтобы не захламлять статью, привожу укороченный вариант словаря. Более длинный смотрите в итоговом файле. Конечно же, его можно расширять, главное не потерять голову иначе проверка может стать излишне тяжелой.
Python: Скопировать в буфер обмена
Код:
def doPassiveScan(self, baseRequestResponse):
keywords = [
"db_auth",
"db_string",
"db_conn",
"db_connect",
"db_connection",
"db_database",
"db_dialect",
"db_host",
...
issues = []
...
Объявил также пустой список для уязвимостей. Если помнишь, функция doPassiveScan() должна возвращать список уязвимостей.
Следующим шагом будет получение самого респонса, который прилетает в функцию из BurpSuite. Для этого вызову функцию getResponse() объекта baseRequestResponse, который получаю как параметр функции doPassiveScan().
Python: Скопировать в буфер обмена
responseByte = baseRequestResponse.getResponse()
Функция getResponse() возвращает массив байт, поэтому потребуется преобразовать его в строку. Для этого отлично подойдет функция bytesToString(), которую предоставляют хелперы Burp.
Python: Скопировать в буфер обмена
response = self._helpers.bytesToString(responseByte)
Сохраню url по которому Burp выполнял запрос. Для этого, обращусь к хелперу и вызову функцию analyzeRequest(). Она возвращает IRequestInfo, среди методов которого есть getUrl(). Советую посмотреть справку, там есть и другие методы, которые возвращают все необходимое относящееся к запросу (заголовки, параметры и т.п.)
Python: Скопировать в буфер обмена
url = self._helpers.analyzeRequest(baseRequestResponse).getUrl()
Логичным продолжением будет обойти все наши кейворды. На основе каждого ключевого слова создаем поиск по тексту ответа. Если есть совпадения, добавляем их как объекты DBCredsIssue (наш класс уязвимостей) в список уязвимостей. В конце просто возвращаем список готовых уязвимостей.
Но сначала стоит подумать над регулярным выражением. Как могут быть записаны нужные данные? На самом деле, вариаций великое множество. Это может быть, например: db_pass: p@ssw0rD. Без кавычек, разделенные двоеточием и пробелом. Данные могут быть взяты в кавычки, как отдельно, так и целиком. Могут быть разделены символом равно, прямой чертой или, чем черт не шутит, @. Исходя из этого, решил составить регулярку из следующих частей:
- Регулярка обязательно с модификатором i, т.к. Регистр может быть какой угодно
- Перед искомой строкой идет слово из словаря, например: (?<=db_usr)
- После слова из словаря, может быть или не быть кавычка: ['\"`]? Добавил все варианты кавычек…хотя может быть так, что для ` потребуется \
- Символ разделения названия параметра и самого параметра, опять же, может быть или не быть: \s?(=|:|\||\@\/\\\s)?\s?. Обрамлено возможными пробелами.
- Ну и, собственно говоря, само значение параметра, если есть: ([^\s\"'&]+)
Вот такой цикл получился:
Python: Скопировать в буфер обмена
Код:
...
for key in keywords:
regex = "(?i)(?<=" +key + ")['\"`]?\s?(=|:|\||\@\/\\\s)?\s?['\"`]?([^\s\"'&]+)"
#Запоминаем название уязвимости на основе ключа
issue_name = f"DBCreds{key}"
#Ищем все совпадения в тексте ответа
reg = re.compile(regex, re.DOTALL)
results = reg.findall(response)
#Обходим все совпадения и добавляем их в итоговый список уязвимостей
for issue_res in results:
#Добавление уязвимости в список
...
...
Здесь нужно сделать остановку. На моменте добаления уязвимостей, есть несколько вариантов, как можно поступить. Мы можем просто вывести найденные данные. Для этого достаточно будет создать новый объект DBCredsIssue, передать в него данные по уязвимости и добавить в итоговый список.
Python: Скопировать в буфер обмена
Код:
...
issue_desc = " Maybe found " + key + " value: " + issue_res[1]
new_issue = DBCredsIssue(baseRequestResponse.getHttpService(),
url,
None,
issue_name,
issue_desc)
issues.append(new_issue)
if len(issues):
return issues
return None
Тест расширения:
Как видно, все прекрасно работает. Но, хочется же сделать качественное решение, чтобы было по-феншую. Как видно на скриншоте, с тестового сервера я получил значение testadministrator–>. В данном случае, –> это закрывающая комментарий скобка. Если не видеть оригинал строки, можно подобные артефакты принять за часть искомой строки и потом рвать волосы на голове, почему не подходят кредсы…
Для этого, потребуется добавить немного кода. А именно, прибегнуть к функции маркировки applyMarkers() из доступных коллбэков. Она принимает IHttpRequestResponse, а также позиции маркировки для запроса и ответа.
Java: Скопировать в буфер обмена
IHttpRequestResponseWithMarkers applyMarkers(IHttpRequestResponse httpRequestResponse, java.util.List<int[]> requestMarkers, java.util.List<int[]> responseMarkers)
Позиции маркеров это список целочисленных массивов. Каждый массив должен содержать в себе два элемента: начало и конец выделения. Значит нужно зафиксировать каждое отдельное совпадение. Кроме того, потребуется импортировать пакет array, поэтому добавляю в импорты строчку:
Python: Скопировать в буфер обмена
from array import array
Прямо перед циклом, который обходит найденные уязвимости, добавлю объявление списка, а первой строкой в цикле объявлю целочисленный массив :
Python: Скопировать в буфер обмена
Код:
...
mark_pos = []
for issue_res in results:
offsets = array('i', [0, 0])
...
mark_pos будет накапливать в себе все координаты для выделения, а offsets будет использоваться для фиксации каждого отдельного начала и конца строки:
Python: Скопировать в буфер обмена
Код:
offsets[0] = mark_start
offsets[1] = mark_start + len(issue_res[1])
mark_pos.append(offsets)
Но перед тем, как сохранять координаты, их нужно определить. Для определения позиции, буду использовать indexOf из предлагаемых коллбэков. Ну и, для корректной работы, добавлю определение длины ответа.
Python: Скопировать в буфер обмена
Код:
response_len = len(responseByte)
mark_start = self._helpers.indexOf(responseByte, issue_res[1], True, 0, response_len)
Обрати внимание, что использую не строковое представление респонса, а байтовое. Как indexOf из коллюэков, так и функция маркировки, работают именно с байтами. Такой вот нюансик. Если когда-то будешь биться в конвульсиях от того, что все правильно, а выделяется совсем не то, вспомни про этот нюанс.
По поводу параметров функции indexOf, думаю достаточно будет привести ее объявление:
Java: Скопировать в буфер обмена
indexOf(byte[] data, byte[] pattern, boolean caseSensitive, int from, int to)
На всякий случай поясню, почему беру не нулевое значение списка совпадений issue_res[1]. Дело в том, что в регулярном выражении есть несколько групп поиска (если не в курсе, то что в круглых скобках выделяется в отдельные группы), которые помещаются в отдельные элементы списка. Поэтому в нулевой группе будет исключительно разделитель (например, “=”), а в первой искомое значение. Как вариант, использовать последнее значение issue_res[-1], тогда туда попадет, и нулевое и первое значение.
Итак, все готово, остается только поменять создание нового объекта уязвимости, заменив None на массив маркированных ответов:
Python: Скопировать в буфер обмена
Код:
new_issue = DBCredsIssue(baseRequestResponse.getHttpService(),
url,
[self._callbacks.applyMarkers(baseRequestResponse, None, mark_pos)],
issue_name,
issue_desc)
Да, чтобы все правильно промаркировалось, нужно передать именно список Interface IHttpRequestResponseWithMarkers, поэтому вызов функции обернул в квадратные скобки [].
Время пробовать…
Уря, товарищи. Согласитесь стало гораздо удобнее и круче? На всякий случай, напоминаю, что функцией applyMarkers() можно разукрасить, как респонсы, так и реквесты. Делайте свои расширения максимально удобными и подробными!
Отладка расширения
Окей, с тем как работает расширение все боль-мень понятно. Но как отлаживать?Стандартно можно пользоваться возможностями вывода stdout и stderr на вкладке расширений. Просто переходим в расширения, выбираем свое и смотрим вниз на вкладки Output и Error.
Соответственно, в ошибки выплевывается все, что вызвало ошибку и, в большинстве случаев, мы можем увидеть какая именно строка не понравилась. Закладка Output показывает нам результат работы всех принтов в коде.
Важно понимать, что данный вывод относиться именно к расширению, а не к проекту.
Если нужен PDB, ничего не мешает нам подключить его. Достаточно импортнуть соответствующий пакет и в нужном месте вставить pdb.set_trace(). Плюс перенаправить вывод на системную консоль. Для этого, при добавлении расширения или в свойствах уже добавленного, нужно выбрать “Output to system console”. Как для Stdout, так и для Srderr.
Плюс добавить перенаправление вывода в коде:
Python: Скопировать в буфер обмена
Код:
from burp import IBurpExtender
import pdb
import sys
class BurpExtender(IBurpExtender, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):
sys.stdout = callbacks.getStdout()
sys.stderr = callbacks.getStderr()
pdb.set_trace()
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("SimpleExtension")
return
Этот скрин сделан на Community-версии запущенной командой java -jar ...
Но здесь есть нюанс… «нежадный» Burp запускается через отдельный Java лаунчер. Возможно есть способ перенаправления, чтобы вывод Burp шел в консоль, но я как-то до этого не дошел. Хватало обычных print() и вывода ошибок в интерфейс.
Предлагаю попрактиковаться с выводом и написать простой логгер вызовов. Логгер поможет отследить логику работы Burp. Выглядеть он будет таким образом:
Python: Скопировать в буфер обмена
Код:
from burp import IBurpExtender
from burp import IScannerCheck
from burp import IHttpListener
class BurpExtender(IBurpExtender, IScannerCheck, IHttpListener):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._callbacks.registerScannerCheck(self)
self._callbacks.registerHttpListener(self)
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("Get Data")
return
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
if messageIsRequest:
print('\n=========== HTTP Listener ===========')
print('TOOL FLAF: ' + str(toolFlag))
self._printRequestData(messageInfo)
def doActiveScan(self, baseRequestResponse, insertionPoint):
print('\n============ Active Scan ============')
self._printRequestData(baseRequestResponse)
insp_base_value = insertionPoint.getBaseValue()
insp_point_name = insertionPoint.getInsertionPointName()
insp_type = self._helpers.bytesToString(insertionPoint.getInsertionPointType())
print("INSERTION POINT")
print("Base Value: " + insp_base_value)
print("Point Name: " + insp_point_name)
print("Point Type: " + insp_type)
return None
def doPassiveScan(self, baseRequestResponse):
print('\n============ Passive Scan ============')
self._printRequestData(baseRequestResponse)
return None
def consolidateDuplicateIssues(self, existingIssue, newIssue):
return 0
def _printRequestData(self, requestResponse):
rq_url = self._helpers.analyzeRequest(requestResponse).getUrl()
rq_params = self._helpers.analyzeRequest(requestResponse).getParameters()
rq_method = self._helpers.analyzeRequest(requestResponse).getMethod()
rq_headers = self._helpers.analyzeRequest(requestResponse).getHeaders()
rq_content_type = self._helpers.analyzeRequest(requestResponse).getContentType()
rq_body_offset = self._helpers.analyzeRequest(requestResponse).getBodyOffset()
print("URL: " + rq_url.toString())
if len(rq_params):
print('Params:')
for param in rq_params:
print(param.getName() + ' = ' + param.getValue())
print("Method: " + rq_method)
if len(rq_headers):
print('Headers:')
for header in rq_headers:
print(header)
print("Content-Type: " + str(rq_content_type))
print("Body offset: " + str(rq_body_offset))
Сканер, который ничего не делает, просто демонстрирует, какие запросы делает BurpSuite при активном и пассивном сканировании.
Здесь появился новый интересный интерфейс IHttpListener. Благодаря ему, можно отслеживать любые HTTP-запросы и ответы сделанные любым инструментом Burp. Единственная функция, которую подразумевает реализация класса, это:
Java: Скопировать в буфер обмена
processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo)
При этом, toolFlag указывает на то, к какому инструменту Burp относится активность (подробнее ниже на картинке). messgaIsRequest указывает на направление (запрос или ответ). Ну и уже знакомый IHttpRequestResponse, который содержит всю информацию о запросе/ответе.
Чтобы все полноценно работало, при загрузке расширения, нужно зарегистрировать слушателя:
Python: Скопировать в буфер обмена
Код:
def registerExtenderCallbacks(self, callbacks):
…
callbacks.registerHttpListener(self)
…
Пример вывода после запуска сканера:
Нет смысла выкладывать отдельно каждый из вариантов вывода. Наиболее интересный результат doActiveScan, а именно точка внедрения:Код: Скопировать в буфер обмена
Код:
============ Active Scan ============
URL: https://www.liverpoolfc.com:443/_next/static/chunks/33.bfa7156c7c12eecb3df3.js
Method: GET
Headers:
GET /_next/static/chunks/33.bfa7156c7c12eecb3df3.js HTTP/2
Host: www.liverpoolfc.com
Sec-Ch-Ua: "Not/A)Brand";v="8", "Chromium";v="126"
Accept-Language: ru-RU
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.127 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Accept: */*
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: script
Referer: https://www.liverpoolfc.com/
Accept-Encoding: gzip, deflate, br
Content-Type: 0
Body offset: 514
INSERTION POINT
Base Value: https://www.liverpoolfc.com/
Point Name: Referer
Point Type: 32
В данном случае, мы видим имя параметра Referer и его значение. Но, например, если точка инъекции находится в url, то в имени мы увидели бы цифру. Эта цифра обозначала бы номер части пути, причем домен это 0. Пример, дял понимания:
Код: Скопировать в буфер обмена
Код:
URL: https://www.expedia.de:443/cl/1x1.gif
INSERTION POINT
Base Value: 1x1.gif
Point Name: 2
Point Type: 37
Эта информация будет полезна при работе с активными сканированиями.
Вместо заключения
Все написанное выше можно было бы реализовать, как отдельные приложения без использования Burp. Но тут важно понимать, что такие программные комплексы, как Burp, Acunetix, A-Parser и прочие, в первую очередь, предоставляют инфраструктуру. Удобные инструменты, которые базово решают множество сопутствующих задач и позволяют структурировано хранить нужную информацию. Тем самым разгружая разработчика и позволяя сконцентрироваться на решении практических проблем, обогащая базовый функционал при помощи API. Приведенная в статье информация, это лишь вводная часть, которая поможет быстро влиться в разработку расширений.
К сожалению, статья и так получилась очень большой (как бы ни пытался урезать), при этом за кадром осталось огромное количество интересных тем: активное сканирование; пользовательский интерфейс расширений и свои панели в интерфейсе BurpSuite; работа с контекстным меню; генераторы пэйлоадов для интрудера… короче очень много интересного есть в BS, о чем есть смысл писать. Если материал будет интересен участникам сообщества, обязательно