Как я и люблю - мы начнем с условных основ и будем двигаться постепенно всё глубже и глубже. Ближе к концу разберём, как эксплуатировать. При написании статьи было использовано много разных источников. К чему это? А к тому, что я начну со слов, которые нашел в статье у Бума - про него будет дальше. Что ж, приятного чтения.
Помните, что использование полученных знаний и навыков должно быть ограничено законными и этическими рамками, и вмешательство в чужие сети без разрешения является неприемлемым и незаконным действием.
Оглавление
Как это устроено
Типичное состояния гонки
Типы Race Condition
Limit-overrun / TOCTOU
Hidden substates
Time Sensitive Attacks
Обнаружение
Эксплуатация Race condition
How to hack
Инструменты
Burp Turbo Intruder
racepwn
Как защищать веб-приложение
Race Condition в BugBounty
Где можно попрактиковаться
Заключение
Как это устроено
Заходит хакер в кальянную, квест и бар, а ему — у вас race condition!
Омар Ганиев
Для начала, как и везде - определение.
Race condition (состояние гонки) - это ситуация, при которой несколько потоков (или процессов) одновременно пытаются выполнить операции чтения или записи к общим ресурсам без должной синхронизации. Представить можно в формате очереди, один за одним.
Фотография не несёт негативный характер
Сложно? Я Вас понимаю, при разборе тем, всегда так. Попробуем разобрать пример из жизни.
Представьте площадку наподобие Я.маркет, где присутствуют купоны на скидку в 10% «best10». Два потока могут одновременно запросить базу данных и подтвердить, что код скидки «best10» не был применен к корзине, затем оба попытаются применить скидку, в результате чего она будет применена дважды. Обратите внимание на то, что «гоночные» условия не ограничиваются конкретной архитектурой веб‑приложений. Проще всего рассуждать о многопоточном приложении с одной базой данных, но в более сложных системах состояние обычно хранится в еще большем количестве мест.
Однопоточные системы, такие как NodeJS, чуть менее уязвимы, но все равно, есть вероятность возникновения подобных проблем. В прошлой статье, где я разбирал концепцию race condition в WS меня поправили, что там не зависит именно от NodeJS, и меня это заинтересовало. И я стал искать то, где можно изучить подобное на примере, а лучше увидеть куском кода...И нашел. Статья хоть и 2021, но показывает то, как происходит состояние гонки в NodeJS. Самое интересное там происходит в комментариях, как и всегда на habr‑е.
Типичное состояния гонки
Почти все программы и веб-приложения сегодня используют так называемую «многопоточную» обработку, при которой они способны выполнять несколько действий одновременно. Хотя это позволяет приложениям работать значительно быстрее, это приводит к возникновению потенциальных ошибок, если более одного процесса (или «потока») пытаются одновременно получить доступ к одним и тем же данным.
Как правило, состояния гонки протекают по определенной схеме:
Критическая секция: Это участок кода, в котором происходит обращение к общим ресурсам и их модификация.
Синхронизация: Отсутствие надлежащих механизмов синхронизации может позволить нескольким процессам одновременно войти в критическую секцию.
Непредсказуемый результат: Из-за одновременного выполнения конечное состояние общего ресурса становится неопределенным.
Например, когда система SQL выполняет обновление базы данных, во время процесса обновления она создает временный файл. Именно этот временный файл со временем заменяет данные в базе данных. При своевременной атаке злоумышленники могут подменить временный файл SQL-обновления таблицы административного доступа на свой собственный, предоставив себе права администратора в системе.
Типы Race Condition
Данная часть была написана с использованием материала с самого важного сайта для каждого пентестера hacktricks
Limit-overrun / TOCTOU
Это наиболее простой тип Race Condition, когда уязвимости, появляющиеся в местах, ограничивают количество раз выполнения какого-либо действия. Например, использование одного и того же кода скидки в интернет-магазине несколько раз. Очень простой пример можно найти в этой статье (требуется vpn) или в этом отчете h1.
Существует множество разновидностей этого вида атак, в том числе:
Погашение подарочной карты несколько раз
Многократное выставление оценок продукту
Снятие или перевод денежных средств, превышающих остаток на счете
Повторное использование одного решения CAPTCHA
Обход anti-brute-force
Hidden substates
Другие, наиболее сложные RC будут использовать подсистемы в состоянии машины, что может позволить злоумышленнику злоупотреблять состояниями, к которым он не должен был иметь доступа, но для злоумышленника есть небольшое окно для доступа к ним.
Прогнозирование потенциальных скрытых и интересных подсистем.
Первым шагом является определение всех конечных точек, которые либо записывают в нее, либо считывают из нее данные, а затем используют эти данные для каких-то важных целей. Например, пользователи могут храниться в таблице базы данных, которая изменяется при регистрации, редактировании профиля, инициировании сброса пароля и завершении сброса пароля. Мы можем использовать три ключевых вопроса, чтобы исключить конечные точки, которые вряд ли могут стать причиной столкновений. Для каждого объекта и связанных с ним конечных точек задайте следующие вопросы:
Как хранится состояние?
Данные, хранящиеся в постоянной структуре данных на стороне сервера, идеально подходят для эксплуатации. Некоторые конечные точки хранят свое состояние полностью на стороне клиента, например, сброс пароля происходит путем отправки JWT по электронной почте — их можно смело пропустить.
Приложения часто хранят некоторые состояния в пользовательской сессии. Часто они в некоторой степени защищены от вложенных состояний — подробнее об этом позже.Мы редактируем или добавляем?
Операции, редактирующие существующие данные (например, изменение основного адреса электронной почты учетной записи), обладают большим потенциалом «столкновений», в то время как действия, просто добавляющие существующие данные (например, добавление дополнительного адреса электронной почты), вряд ли будут уязвимы для чего‑либо, кроме limit‑overrun attacks.На чем основана операция?
Большинство конечных точек работают с определенной записью, которая ищется с помощью «ключа», например, имени пользователя, маркера сброса пароля или имени файла. Для успешной атаки нам необходимы две операции, использующие один и тот же ключ. Например, представим две правдоподобные реализации сброса пароля:
Поиск подсказок
На этом этапе самое время провести несколько RC‑атак на потенциально интересные конечные точки, чтобы попытаться найти неожиданные результаты по сравнению с обычными. Любое отклонение от ожидаемого ответа, например, изменение одного или нескольких ответов, или эффект второго порядка, например, различное содержимое писем или видимое изменение сеанса, может быть подсказкой, указывающей на то, что что‑то не так.
Prove the concept (Доказательство концепции)
Последний шаг - подтверждение концепции и превращение ее в жизнеспособную атаку.
При отправке пакета запросов можно обнаружить, что ранняя пара запросов вызывает уязвимое конечное состояние, но последующие запросы перезаписывают/недостоверяют его, и конечное состояние оказывается неэксплуатируемым. В этом случае необходимо исключить все лишние запросы — для эксплуатации большинства уязвимостей достаточно двух. Однако если сократить количество запросов до двух, атака станет более чувствительной к времени, поэтому может потребоваться многократное повторение атаки или ее автоматизация.
Time Sensitive Attacks
Иногда можно не обнаружить условий гонки, но техника доставки запросов с точным соблюдением временных рамок все равно может выявить наличие других уязвимостей.
Одним из таких примеров является использование временных меток высокого разрешения вместо криптографически защищенных случайных строк для генерации маркеров безопасности.
Рассмотрим маркер сброса пароля, который рандомизируется только по метке времени. В этом случае можно инициировать два сброса пароля для двух разных пользователей, которые используют один и тот же маркер. Все, что нужно сделать, - это задать время для запросов, чтобы они генерировали одну и ту же временную метку.
Для подтверждения, например, предыдущей ситуации можно просто запросить 2 токена сброса пароля одновременно (используя атаку одним пакетом) и проверить, одинаковы ли они.
Обнаружение
Процесс обнаружения достаточно прост. В общих чертах, все, что вам нужно сделать, это:
Определить одноразовую или ограниченную по скорости конечную точку, которая имеет какое-то влияние на безопасность или другое полезное назначение.
Выдать несколько запросов к этой конечной точке в быстрой последовательности, чтобы проверить, сможете ли вы превысить этот лимит.
Основная сложность заключается в выборе времени для запросов таким образом, чтобы по крайней мере два окна гонки совпали
Даже если вы посылаете все запросы в одно и то же время, на практике существуют различные неконтролируемые и непредсказуемые внешние факторы, которые влияют на то, когда сервер обрабатывает каждый запрос и в каком порядке.
Мне очень понравились схемы от portswigger, я правда не знаю, будет ли это нарушать их правила, но я переведу их на русский язык для лучшего понимания аудиторией.
jitter - буквально - "дрожание". То есть непрогнозируемое и нерегулируемое изменение некоторого параметра.
Эта часть была взята с того же portswigger, а сам burp вы можете приобрести или использовать community edition или использовать версию избавленную от жадности (внимательно выпирайте крякера, но советую 0daylab
Burp Suite 2023.9 добавляет в Burp Repeater новые мощные возможности, позволяющие легко отправлять группу параллельных запросов таким образом, чтобы значительно снизить влияние одного из этих факторов, а именно сетевого джиттера. Burp автоматически настраивает используемую технику в зависимости от версии HTTP, поддерживаемой сервером:
Для
HTTP/1
используется классическая техника синхронизации по последнему байту.Для
HTTP/2
используется техника однопакетной атаки, впервые продемонстрированная компанией PortSwigger Research на конференции Black Hat USA 2023.
Однопакетная атака позволяет полностью нейтрализовать помехи от сетевого джиттера, используя один TCP-пакет для одновременного выполнения 20-30 запросов.
Хотя часто для запуска эксплойта можно использовать всего два запроса, отправка большого количества запросов помогает уменьшить внутреннюю задержку, известную также как джиттер на стороне сервера. Это особенно полезно на начальном этапе обнаружения.
Особенно хочется отметить что тот самый jitter - не единственная проблема. Многие из Вас сталкивались с высоким пингом в играх, тут, схожий принцип работы. Чем ближе вы будете находиться, тем наиболее вероятно получится проэксплуатировать RC.
Эксплуатация Race condition
Тут можно сказать простую фразу - "А давайте просто включим intruder или turbo intruder в много потоков и всё у нас получится. Где наши $300 за бб?". Да, в ней есть доля правды, но это не всегда эффективно. Понятно, что это остается основным способом, но есть ещё. Для объяснения я возьму статью Бума aka лучшие усики комьюнити.
Оригинальная статья, настоятельно рекомендую ознакомиться.
Расщепление HTTP-запроса на две част
Для начала вспомним как формируется HTTP-запрос.
Ну как ты знаешь, первая строка это метод, путь и версия протокола:
GET / HTTP/1.1
Дальше идут заголовки до переноса строки:
Host: google.com
Cookie:
a=1
Но как веб-сервер узнает, что HTTP-запрос закончился?
Давай рассмотрим на примере, введи nc google.com 80, а там
GET / HTTP/1.1
Host: google.com
После того, как нажмешь ENTER, ничего не произойдет. Нажмешь еще раз — увидишь ответ.
То есть, чтобы веб-сервер принял HTTP-запрос, необходимо два перевода строки. А корректный запрос выглядит так:
GET / HTTP/1.1\r\nHost: google.com\r\n\r\n
Если бы это был метод POST (не забываем про Content-Length), то корректный HTTP-запрос был бы таким:
POST / HTTP/1.1
Host: google.com
Content-Length: 3
a=1
или
POST / HTTP/1.1\r\nHost: google.com\r\nContent-Length: 3\r\n\r\na=1
Попробуй отправить подобный запрос из командной строки:
echo -ne "GET / HTTP/1.1\r\nHost: google.com\r\n\r\n" | nc google.com 80
В итоге ты получишь ответ, так как наш HTTP-запрос полноценный. Но если ты уберешь последний символ \n
, то ответа не получишь.
На самом деле многим веб-серверам достаточно использовать в качестве переноса \n
, поэтому важно не менять местами \r
и \n
, иначе дальнейшие трюки могут не получиться.
Что это даёт? Ты можешь одновременно открыть множество соединений на ресурс, отправить 99% своего HTTP-запроса и оставив неотправленным последний байт. Сервер будет ждать пока ты не дошлёшь последний символ перевода строки. После того, как будет ясно, что основная часть данных отправлена — дослать последний байт (или несколько).
Это особенно важно, если речь идет о большом POST-запросе, например, когда необходима заливка файла. Но и даже в небольшом запросе это имеет смысл, так как доставить несколько байт намного быстрее, чем одновременно килобайты информации.
Опять же, настоятельно рекомендую к прочтению, там есть великолепные видосики с поездами
How to hack
В общих чертах, все, что вам нужно сделать, это:
Определить одноразовую или ограниченную по скорости конечную точку, которая имеет какое-то влияние на безопасность или другое полезное назначение.
Выдать несколько запросов к этой конечной точке в быстрой последовательности, чтобы проверить, сможете ли вы превысить этот лимит.
Основная сложность заключается в выборе времени для запросов таким образом, чтобы по крайней мере два окна гонки совпали, вызвав "столкновение". Часто это окно составляет всего миллисекунды, но может быть и короче, как это мы разбирали выше.
Как говорилось раньше, даже если вы посылаете все запросы в одно и то же время, на практике существуют различные неконтролируемые и непредсказуемые внешние факторы, которые влияют на то, когда сервер обрабатывает каждый запрос и в каком порядке.
Инструменты
Обычно, для эксплуатации используют turbo intruder бурпа, но также мы посмотрим на racepwn.
Burp Turbo Intruder
Мы можем попробовать проэксплуатировать Race condition через Repeater, с помощью отправки сгруппированных HTTP-запросов, но зачем это делать, если есть Turbo intruder
И тут встаёт вопрос - "Зачем нам использовать Turbo Intruder если есть стандартный Intruder?". Если максимально коротко, то Turbo Intruder - это Intruder на максималках, с анаболиками (22 встроенных) и возможностью кастомизации атак при помощи Python (если вашего варианта нет в списке, то вы должны обладать навыками в написании скриптов на этом языке).
Как и в обычном интрудере, если выделить часть запроса перед отправкой в расширение, то она в окне запроса заменится на %s
. Это является аналогом символов § §
, и вы можете перемещать его в любую точку запроса.
Теперь немного про его особенности (как в рекламе):
Быстрота: Turbo Intruder использует стек HTTP, созданный вручную с нуля с учетом скорости. В результате на многих целях он может серьезно обогнать даже модные асинхронные скрипты на Go (на самом деле стек можно выбрать, и большинство из них будут тебе знакомы).
Удобство: скучные результаты могут быть автоматически отфильтрованы с помощью усовершенствованного алгоритма дифов, адаптированного из Backslash Powered Scanner. Это означает, что ты можешь запустить атаку и получить полезные результаты в два клика.
Масштабируемость: Turbo Intruder может достичь плоского использования памяти, что позволяет проводить надежные многодневные атаки. Его также можно запускать в headless-окружении через командную строку.
Гибкость: атаки конфигурируются с помощью Python. Это позволяет выполнять сложные требования, например подписанные запросы и многоступенчатые последовательности атак. Кроме того, пользовательский стек HTTP позволяет обрабатывать неправильно сформированные запросы, которые ломают другие библиотеки.
Для детального изучения - Ссылка
racepwn
RacePWN - утилита, написанная на golang, которая реализует интерфейс librace путем установки параметров через конфигурацию, написанную на json.
[
{
"race": {
// Установка параметров гонки
"type": "paralell", // режим гонки
"delay_time_usec": 10000, // временная задержка между двумя частями запроса
"last_chunk_size": 10 // размер куска по последнему запросу
},
"raw": {
"host": "tcp://localhost:8080", // имя хоста и порт
"ssl": false, // использовать флаг ssl
"race_param": [
{
// параметры гонки
"data": "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n", // raw HTTP запрос
"count": 100 // количество пакетов
}
]
}
}
]
Поддерживаются два режима работы. Работает как консольная утилита, так и веб-сервис.
console util:
В этом режиме работы на вход приложения подается конфигурационный файл. Приложение выполняет запросы к серверу.
racepwn < config.json
web service:
В этом режиме утилита начинает работать как веб-сервис. Для работы необходимо выполнить POST-запрос по пути /race с содержимым конфигурационного файла. Имя хоста и порт должны быть указаны с помощью флага -hostname.
racepwn -host "localhost:8080"
Пример:
curl -i -X POST localhost:8080/race --data @<(cat config.json)
Как защищать веб-приложение
Одним из наиболее эффективных способов предотвращения уязвимостей состояния гонки является использование механизмов блокировки. Механизмы блокировки обеспечивают одновременный доступ к общему ресурсу только одного процесса, не позволяя другим процессам вмешиваться в работу ресурса.
Семафоры - это один из видов механизмов блокировки, который часто используется для предотвращения условий гонки. Семафоры работают путем присвоения ресурсу значения, указывающего, доступен он или нет. Когда процесс пытается получить доступ к ресурсу, он проверяет значение семафора. Если ресурс недоступен, процесс ждет, пока он не станет доступен.
Мьютексы - это тип механизма блокировки, аналогичный семафорам. Мьютексы работают, позволяя только одному процессу одновременно получать доступ к общему ресурсу. Когда процесс пытается получить доступ к ресурсу, он проверяет состояние мьютекса. Если к ресурсу уже обращается другой процесс, то он ждет, пока ресурс не станет доступным. Большинство языков программирования имеют встроенную функциональность блокировки данных; например, в Python есть "threading.Lock", а в Go - "sync.Mutex".
Атомарные операции являются разновидностью низкоуровневого механизма синхронизации. Он обеспечивает выполнение операций процесса за один, неделимый шаг. Это не позволяет другим процессам вмешиваться в работу с ресурсом во время выполнения операции.
Race Condition в BugBounty
В этой части статьи я приложу список 15 репортов на h1 (надеюсь, что в скором времени появятся дисклоузы и от ру площадок).
№ | Название | Компания | Вознаграждение | Ссылка |
1 | Race Conditions in OAuth 2 API implementations | The Internet | $2,500 | Ссылка |
2 | Race condition in Flash workers may cause an exploitable double free | Flash (IBB) | $10,000 | Ссылка |
3 | Race condition in performing retest allows duplicated payments | HackerOne | $2,100 | Ссылка |
4 | Adobe Flash Player Race Condition Vulnerability | Flash (IBB) | $2,000 | Ссылка |
5 | Race condition in activating email resulting in infinite amount of diamonds received | InnoGames | $2,000 | Ссылка |
6 | Race Condition allows to redeem multiple times gift cards which leads to free “money” | Reverb.com | $1,500 | Ссылка |
7 | Client-Side Race Condition using Marketo, allows sending user to data-protocol in Safari when form without onSuccess is submitted on www.hackerone.com | HackerOne | $1,250 | Ссылка |
8 | Race condition на market.games.mail.ru | Mail.ru | $1,000 | Ссылка |
9 | Race condition leads to duplicate payouts | HackerOne | $750 | Ссылка |
10 | Race Condition Vulnerability On Pornhubpremium.com | PornHub | $520 | Ссылка |
11 | Race Condition leads to undeletable group member | HackerOne | $500 | Ссылка |
12 | Race condition in claiming program credentials | HackerOne | $500 | Ссылка |
13 | race condition in adding team members | Shopify | $500 | Ссылка |
14 | Race condition (TOCTOU) in NordVPN can result in local privilege escalation | NordVPN | $500 | Ссылка |
15 | Register multiple users using one invitation (race condition) | Keybase | $350 | Ссылка |
Где можно попрактиковаться
Portswigger Academy - ссылка
NodeJs RC - ссылка
Заключение
В завершении хочется сказать спасибо всем тем, кто помогал с данной статьей. Множество авторов, которые может и не узнают, что я их тут упоминал, но всё равно. Надеюсь материал получился не слишком нагруженным и сложным.
P.S. Больше подобной информации и хороших мемов Вы сможете найти тут