Race Condition چیست ؟ چگونه رخ میدهد ؟

- مفهوم Race Condition چیست ؟ به زبان ساده
- مفهوم تخصصی Race Condition
- نیم نگاهی به مبحث Race Condition
- شناسایی و اکسپلویت Race Condition
- Burp Suite
- شناسایی و اکسپلویت با ابزار Turbo Intruder
- روش Hidden multi-step sequences
- مثال با توضیح :
- مثال دیگر :
- روش Multi-endpoint race conditions
- Aligning multi-endpoint race windows
- Connection warming
- روش Single-endpoint race conditions
- چگونه از آسیب پذیری جلوگیری کنیم !
مفهوم Race Condition چیست ؟ به زبان ساده
آسیب پذیری Race Condition چیست ؟ آسیب پذیری است که به ما این امکان را میدهد
تا یک فانکشن یک بار مصرف را چندین بار اجرا کنیم؛ برای نمونه با پرداخت 10 هزار تومان و انجام حمله Race Condition بر روی تابع تایید کند پرداخت، حساب خود را 50 هزار تومان شارژ کنیم زیرا که توکن تایید پرداخت ما در تابع تایید پرداخت پنج بار بجای یک بار تایید شده است.
این آسیبپذیری معمولا بر روی سایتهای مقصد رخ میدهد نه درگاههای پذیرنده بانکی، از همین رو آسیب پذیری بر روی سایت مقصد است و سایت مقصد باید اقدامات لازم برای Patch این آسیبپذیری را انجام دهد.
مفهوم تخصصی Race Condition
این آسیبپذیری زمانی رخ میدهد که سرور درخواستهای همزمان را بدون بررسی صحیح در کسری از ثانیه پردازش میکند. بسیاری شاید تصور کنند که این آسیبپذیری در سیستم های پردازشی multi thread تنها رخ میدهد؛ اما علت این رخ داد muti thread بودن یک برنامه نیست و این آسیبپذیری ممکن است در سیستم های single thread هم رخ دهد زیرا که علت این آسیبپذیری پردازش همزمان چندین درخواست بدون بررسی و تایید درخواستها در کسری در ثانیه است.
- نکته ی که در مورد این حمله باید دقت کرد، این است که برای پیدا کردن و گزارش این آسیبپذیری باید به منطق کاسب و کار (business logic) سامانه دقت نمود.

نیم نگاهی به مبحث Race Condition
در قسمت بالا صحبت کردیم که برای پیدا کردن این حمله، باید به منطق کسب و کار سامانه نگاهی انداخت و نمیتوان در هر فانکشنی این حمله را پیادهسازی کرد؛ زیرا ممکن است تاثیری امنیتی یا مالی بر روی آن سامانه نداشته باشد.
برای نمونه خیال کنید که ما یک فروشگاه اینترنتی آنلاین داریم؛ طبق رویه معمولا همه فروشگاه های اینترنتی آنلاین یک تابع کوپن تخفیف برای سبد خرید خود دارند. اگر ما کد تخفیف off_30 را داشته باشیم و بر روی سبد خرید خود اعمال کنیم؛ 30 درصد بر روی مبلغ خرید خود تخفیف میگیریم اما اگر بخواهیم دوباره از آن کد استفاده کنیم با پیغام “این کد برای شما اعمال شده است” مواجه می شویم که مانع از استفاده چند باره ما از تخفیف 30 درصدی میشود.

حال فکر کنید که اگر کاربری که قبلا هرگز این کد تخفیف را بر روی سبد خرید خود اعمال نکرده است، سعی کند آن را تقریباً در یک زمان (کسری از ثانیه) چند بار اعمال کند، چه اتفاقی میافتد ؟

اگر به تصویر بالا نگاه کنیم، دو درخواست همزمان با هم ارسال شده اند و برنامه باید درخواست اول را قفل کند و تا مشخص شدن نتیجه درخواست اول، درخواست دوم که درخواست یکسان هست را بررسی نکند؛ چنانچه هر دو درخواست یا چندین درخواست با هم پردازش بشوند و برنامه نویس این مورد را بر اساس قفل کردن سورس یا روش های دیگر مدیریت نکند و چون در دیتابیس این کد قبلا برای کاربر اعمال نشده است و پاسخ “عدم استفاده از کد بگیرد” این تخفیف 2 الی چند بار اعمال میشود که باعث حتی صفر شدن هزینه سبد خرید شما میشود. (این پاراگراف رو با زبون خودم گفتم).
البته این آسیبپذیری تنها در قسمت سبد خرید قابل بررسی نیست و در جایی که مجاز به انجام یکباره یک تابع هستیم، می تواند با توجه به منطق کسب و کار سامانه مورد بررسی قرار بگیرد مانند:
- بازگشت از درگاههای بانکی و تایید خرید
- لایک یا دیس لایک یک عکس یا محصول
- یا اعمال امتیاز مثبت یا منفی برای یک نظر
شناسایی و اکسپلویت Race Condition

Burp Suite
شرکت portswigger در نسخه Burp Suite 2023.9 قابلیتهای جدید و قدرتمندی را به Burp Repeater اضافه کرده است که به شما این امکان را میدهد تا به راحتی گروهی از درخواستهای موازی (parallel requests) را ارسال کنید، البته قبلتر نیز میتوانستید از ابزار turbo intruder در برپسویت برای این حمله استفاده کنید.
تا قبل از آپدیت Burp Suite 2023.9 و ارائه James Kettle در Black Hat USA 2023 معمولا حملات race condition بر روی HTTP/1 با تکنیک classic last-byte synchronization بودند اما با تحقیقات جیمز بر روی این حمله بر روی HTTP/2 حال از روش single-packet attack نیز میتوانیم بر روی HTTP/2 حمله race condtion را پیاده سازی کنیم.
حمله single-packet به ما این امکان را میدهد تا با یک پکت TCP بیست الی سی درخواست را همرمان ارسال نماییم.

اگرچه اغلب میتوانید فقط از دو درخواست برای انجام یک اکسپلویت استفاده کنید، ارسال تعداد زیادی درخواست از این قبیل به کاهش تأخیر داخلی (internal latency) کمک میکند، که به نام server-side jitter نیز شناخته میشود. این به ویژه در مرحله کشف اولیه مفید است.
جزییات بیشتر
برای جزئیات در مورد نحوه استفاده از ویژگی های جدید Burp Repeater برای ارسال چندین درخواست به صورت موازی، به صفحه اSending requests in parallel مراجعه کنید.
برای بینش فنی در مورد مکانیک های اساسی حمله single-packet ، و نگاهی دقیق تر به روش، مقالهSmashing the state machine: The true potential of web race conditions را بررسی کنید.
- آزمایشگاه زیر مثالی از این حمله است که میتوانید این حمله را در دنیای واقعی در این آزمایشگاه بررسی و تست کنید.
شناسایی و اکسپلویت با ابزار Turbo Intruder
علاوه بر استفاده از repeater، از ابزار turbo intruder هم میتوانیم برای اکسپلویت این حمله استفاده کنیم. در ابزار Turbo Intruder تمپلیتهای پیشفرضی برای حملات race condition در http/1 و http/2 وجود دارد؛ اما گاها برای حملات پیچیده تر شاید نیاز به تغییر کدهای پایتونی آن باشد.
برای استفاده از حمله single-packet در HTT/2 باید مراحل زیر را به ترتیب انجام دهید:
- ابتدا بررسی شود که آیا سرور سایت هدف از این ورژن از HTTP پشتیبانی میکند یا خیر
- سپس ابزار Turbo Intruder را از BApp store ابزار Burp Suite دانلود و فعال کنید
- درخواستی که قصد حمله به آن دارید را به turbo intruder ببرید و با اسکریپت race-single-packet-attack.py حمله را آغاز کنید.
def queueRequests(target, wordlists):# if the target supports HTTP/2, use engine=Engine.BURP2 to trigger the single-packet attack
# if they only support HTTP/1, use Engine.THREADED or Engine.BURP instead
# for more information, check out https://portswigger.net/research/smashing-the-state-machine
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)# the ‘gate’ argument withholds part of each request until openGate is invoked
# if you see a negative timestamp, the server responded before the request was complete
for i in range(20):
engine.queue(target.req, gate=’race1′)# once every ‘race1’ tagged request has been queued
# invoke engine.openGate() to send them in sync
engine.openGate(‘race1’)def handleResponse(req, interesting):
table.add(req)
نکته: برای حمله بر روی HTTP/1 میتوانید BURP2 را به BURP در خط 8 در قطعه کد بالا تغییر دهید یا از اسکریپت قدیمی زیر استفاده کنید.
نکته : گاها اکسپلویت نشدن یک حمله race condition به معنای امن بودن یا هاردن بودن
اون تابع در سمت سرور نیست؛ گاهی لازم است که از اسکریپت های مختلفی استفاده کنیم یا تغییراتی در کد پایتونی خود ایجاد کنیم. یا گاهی لازم است که حمله را در ساعت های خاصی انجام دهیم.
برای بررسی کد race-single-packet-attack.py در HTTP/2 می توانید در آزمایشگاه زیر این حمله را بررسی کنید.
روش Hidden multi-step sequences
در عمل، یک single request میتواند، مجموعه ای از فرآیندها را در پشت صحنه (سرور) آغاز کند.
برنامه را از چندین hidden state انتقال دهد و سپس قبل از تکمیل پردازش درخواست، دوباره از آن خارج شود. ما آن را با نام sub-states میشناسیم.
اگر میتوانید یک یا چند درخواست HTTP را شناسایی کنید که باعث تعامل با دادههای مشابه میشوند.
شما به طور بالقوه می توانید از این sub-states برای افشای تغییرات حساس به زمان انواع نقص های business logic که در گردش های multi-step رایج هستند، سوء استفاده کنید. این امکان اکسپلویت از race condition را فراهم می کند که بسیار فراتر از حد مجاز است.
- به عنوان مثال، ممکن است با multi-factor authentication (MFA) آشنا باشید که به شما امکان میدهد
بخش اول ورود به سیستم را با استفاده از اعتبارنامههای شناخته شده انجام دهید، سپس مستقیماً از طریق forced browsing به برنامه بروید و عملاً MFA را کاملاً دور بزنید.
مثال با توضیح :
فرض کنید یک وبسایت برای انجام یک تراکنش مالی، چند مرحله داخلی دارد. مراحل به صورت زیر است :
- مرحله اول: کاربر درخواست انتقال پول را ارسال میکند.
- مرحله دوم: سیستم مقدار پول موجود در حساب کاربر را بررسی میکند.
- مرحله سوم: سیستم پول را از حساب کاربر کم میکند.
- مرحله چهارم: سیستم تراکنش را در حساب گیرنده ثبت میکند.
- مرحله پنجم: پاسخ نهایی به کاربر داده میشود که تراکنش موفقیتآمیز بوده است.
اگر این مراحل پشت صحنه انجام شوند و کاربر فقط یک درخواست ساده برای انتقال پول ارسال کند
سیستم میتواند به سرعت به حالتهای مختلف برود (حالات مخفی یا “Sub-States”) و دوباره به حالت اولیه برگردد.
فرض کنید کاربر به نوعی متوجه شود که میتواند چند درخواست انتقال پول به صورت همزمان ارسال کند.
اگر این درخواستها به صورت همزمان به سرور برسند، ممکن است مراحل مختلف برای درخواستهای مختلف همزمان انجام شوند. به عنوان مثال :
- درخواست اول ممکن است پول را از حساب کاربر کم کند.
- درخواست دوم ممکن است همزمان قبل از اینکه سیستم کاملاً پردازش مرحله اول را تکمیل کند، تلاش کند همان مقدار پول را منتقل کند.
در این حالت، اگر برنامه به درستی race condition را مدیریت نکند، ممکن است پول دو بار منتقل شود، حتی اگر موجودی کافی برای انتقال دوم وجود نداشته باشد. این امر به دلیل این است که سیستم هنوز در حال پردازش مرحله سوم برای درخواست اول است و ممکن است بررسی مقدار پول برای درخواست دوم به درستی انجام نشود.
مثال دیگر :
فرض کنید شما یک سیستم تأیید دو مرحلهای (MFA) دارید. در سیستمهایی که به درستی ایمن نشدهاند،
ممکن است شرایطی ایجاد شود که کاربر بتواند به طور مستقیم پس از وارد کردن نام کاربری و رمز عبور، بدون گذراندن مرحله دوم تأیید، به حساب دسترسی پیدا کند. این امر ممکن است به دلیل توالی مخفی مراحل در پردازش درخواستهای ورود به حساب کاربری باشد که میتواند در شرایط رقابت دچار نقص شود.
این نوع حملات پیچیده هستند و نیاز به درک عمیقی از چگونگی کارکرد داخلی سیستمها دارند، اما اگر موفق باشند، میتوانند باعث مشکلات امنیتی بسیار جدی شوند.
توجه داشته باشید
اگر با این اکسپلویت آشنا نیستید، آزمایشگاه بای پس ساده 2FA را در مبحث آسیبپذیریهای احراز هویت portswigger بررسی کنید.
قطعه کد زیر نشان میدهد که چطور ممکن است یک وبسایت در برابر این حمله آسیبپذیر باشد.
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
روش Multi-endpoint race conditions
یک نوع دیگر از این حملات، ارسال درخواست به چندین نقطه پایانی به طور همزمان است.
برای نمونه به نقص بیزینس لاجیک در فروشگاه های آنلاین فکر کنید که در آن کالایی را به سبد خرید خود اضافه می کنید، هزینه آن را پرداخت می کنید، سپس قبل از force-browsing به صفحه تأیید سفارش، موارد بیشتری را به سبد خرید اضافه می کنید.
- این آسیبپذیری زمانی رخ میدهد که تایید اعتبار برای پرداخت ( payment validation) و تأیید سفارش (order confirmation) در طول پردازش، با یک درخواست واحد انجام شود.

در این صورت، میتوانید بهطور بالقوه در طول race window، از زمانی که پرداخت اعتبارسنجی میشود
تا زمانی که سفارش نهایی میشود، موارد بیشتری را به سبد خود اضافه کنید. (یعنی بیای کیف پولتو شارژ کنی به مبلغ 100 تومان بعد یک جنس 10 تومنی اضافه کنی به سبد خریدت، بعد بیای برای race condition تو repeater یک race window بسازی و اول یک محصول با قیمت بیشتر از اعتبار کیف پول رو به سبد خرید اضافه کنی بعد پرداخت رو تایید کنی و باز اون محصول با قیمت بیشتر از اعتبار کیف پول رو به سبد خرید اضافه کنی) قسمت قرمز رنگ رو باید باهم به صورت همزمان race condition بزنی.
Aligning multi-endpoint race windows
هنگام آزمایش multi-endpoint race conditions، ممکن است با مشکلاتی در تلاش برای ردیف کردن race windows ها برای هر درخواست مواجه شوید، حتی اگر همه آنها را دقیقاً در یک زمان با استفاده از تکنیک single-packet ارسال کنید.
اینجا سرعت اینرنت هم تاثیر داره و همینطور مدت زمانی که سرور به ما پاسخ میده؛ پس ممکنه چند بار تلاش کنید نخوره ولی آسیبپذیر باشه و نیاز به تلاش های بیشتری باشه.

این مشکل رایج در درجه اول ناشی از دو عامل زیر است :
Delays introduced by network architecture (تاخیرهای معرفی شده توسط معماری شبکه) :
به عنوان مثال، هر زمان که سرور فرانت اتصال جدیدی به بکاند برقرار میکند، ممکن است تاخیر وجود داشته باشد. پروتکل مورد استفاده نیز می تواند تاثیر زیادی داشته باشد.
Delays introduced by endpoint-specific processing (تأخیرهای معرفی شده توسط پردازش نقطه پایانی خاص) :
نقاط پایانی مختلف ذاتاً در زمان پردازش متفاوت هستند، گاهی اوقات زمان پردازش آنها متفاوت است. خوشبختانه، راه حل های بالقوه ای برای هر دوی این مسائل وجود دارد.
Connection warming
روش Single-endpoint race conditions
ارسال درخواستهای parallel با مقادیر مختلف به یک endpoint میتواند گاهی اوقات race condition قدرتمندی را ایجاد کند.
مکانیزم بازنشانی رمز عبور را در نظر بگیرید که شناسه کاربری و رمز بازنشانی را در جلسه کاربر ذخیره می کند.
در این سناریو، ارسال دو درخواست تنظیم مجدد رمز عبور به صورت parallel از یک session ، اما با دو نام کاربری متفاوت، به طور بالقوه می تواند باعث برخورد زیر شود:

به وضعیت نهایی زمانی که تمام عملیات کامل شد توجه کنید:
session['reset-user'] = victim
session['reset-token'] = 1234
اکنون session شامل شناسه کاربری قربانی است، اما رمز تنظیم مجدد معتبر برای مهاجم ارسال می شود.
توجه داشته باشید
برای اینکه این حمله کار کند، عملیات های مختلف انجام شده توسط هر فرآیند باید به ترتیب درست انجام شود. برای دستیابی به نتیجه مطلوب، احتمالاً به تلاش های متعدد یا کمی شانس نیاز دارد.
تأیید آدرس ایمیل، یا هر عملیات مبتنی بر ایمیل، به طور کلی هدف خوبی برای single-endpoint race condition است. ایمیلها اغلب در یک رشته پسزمینه پس از اینکه سرور پاسخ HTTP را به مشتری صادر میکند، ارسال میشود و race condition را محتملتر میکند.
چگونه از آسیب پذیری جلوگیری کنیم !
هنگامی که یک درخواست می تواند یک برنامه کاربردی را از طریق حالت های فرعی نامرئی منتقل کند، درک و پیش بینی رفتار آن بسیار دشوار است. این امر دفاع را غیرعملی می کند. برای ایمن سازی صحیح یک برنامه، توصیه می کنیم با اعمال استراتژی های زیر، حالت های فرعی را از تمام نقاط انتهایی حساس حذف کنید :
- از مخلوط کردن داده ها (mixing data) از مکان های ذخیره سازی مختلف خودداری کنید.
- با استفاده از ویژگیهای همزمانی ذخیرهگاه داده، اطمینان حاصل کنید که نقاط انتهایی حساس تغییرات حالت را اتمی میکنند. به عنوان مثال، از یک تراکنش پایگاه داده واحد برای بررسی مطابقت پرداخت با ارزش سبد خرید و تأیید سفارش استفاده کنید.
استفاده از mutex: (Mutual Exclusion)
- قفل سورس
در حال یادگیری ..........