با سلام، مهدی مرادلو هستم. در این رایتآپ ، قصد دارم به بررسی یک آسیبپذیری که در بخش تأیید پرداخت پلتفرم دیوار وجود داشت بپردازم.امیدوارم که از خوندن این رایتآپ لذت ببرید .
قبل از شروع نیاز می بینم که توضیحات روی در مورد PSP های ایرانی بدم و توصیه میکنم حتما مستندات درگاه های پرداخت مختلف رو خودتون بخونید تا با ساختار کلی و عملکردشون آشنا بشید.
کل پروسه پرداخت در pspهای ایرانی شمای مثل شکل زیر داره. ( فقط نیازه به توضیح هست من مراحلی که موضوع رو به حاشیه می برد رو قیچی کردم و سعی کردم به شکل ساده تری موضوع رو بیان کنم)
1-پذیرنده سفارش کاربر رو دریافت میکنه و بعد اطلاعات رو در پایگاه داده خودش ذخیره میکنه بعد پذیرنده اطلاعات ترمینال خودش را همراه با مبلغ سفارش و پارامتر های دیگه مثل callbackurl و... به سمت بانک ارسال میکنه و یک Token دریافت می کنه بعد کاربر رو به درگاه بانکی هدایت می کنه تا کاربر پرداختش رو انجام بده .
2-پس از اینکه کاربر اطلاعات بانکی خودش رو وارد کرد و پرداخت در سمت درگاه پرداخت انجام شد درگاه اطلاعات مربوط به پرداخت رو در اختیار کلاینت (مرورگر کاربر) قرار میده و هدایتش میکنه به سایت پذیرنده تا پرداخت تکمیل بشه .
3-پذیرنده اطلاعات رو دریافت می کنه ابتدا پایگاه داده بررسی میکنه که اطلاعات دریافتی تکراری نباشه و سپس اطلاعات پرداخت رو به سمت بانک ارسال میکنه و در صورت معتبر بودن رسید پرداخت وتطابقش با سفارش کاربر پرداخت رو تأیید کرده و خدمات رو به کاربر ارائه میکنه.)رسید دیجیتال رو هم در پایگاه داده خودش ذخیره میکنه که دوباره مصرف نشه)
تا اینجای کار با فرآیندی که یک پرداخت طی میکنه آشنا شدیم پلتفرم دیوار نیز همین روال رو برای پرداخت داره کاربر ابتدا یکی از آیتم های مورد نظرش رو انتخاب میکنه وسپس بروی پرداخت میزنه به درگاه بانکی هدایت میشه ،پرداخت رو انجام میده و بعد اطلاعات پرداخت به اندپوینت تأیید پرداخت دیوار ارسال میشه سپس سامانه پرداخت رو تأیید یا رد میکنه .
بیشتر آسیب پذیری هایی که مربوط به بخش پرداخت پلتفرم ها هست در بخش سوم اتفاق می افته جایی که کاربر اطلاعات رو به پذیرنده ارسال میکنه تا پرداخت تأیید بشه من تقریبا بیشتر مستندات فنی درگاه بانکی مختلف ایرانی رو خوندم متاسفانه هیچ یک از psp ها ساختاری رو پیاده سازی نکردن تا بعد از پرداخت در درگاه بانکی خود pspمستقیم از سرور خود یک درخواست به پذیرنده ارسال کنه و پرداخت رو تأیید کنه ، پیاده سازی این ساختار باعث ایمنی بیشتر سامانه ها میشه چرا که کاربر نمی تونه اطلاعات مربوط به پرداخت رو تغییر بده و یکپارچگی سیستم حفظ میشه البته این به معنی معیوب بودن ساختار فعلی نیست اگر پذیرنده ساختار تأیید پرداخت رو درست پیاده سازی کنه مشکلی پیش نمیاد .
ولی بیشتر PSPهای بین المللی مثل paypal علاوه بر روش قبلی مکانیزم Instant Payment Notification IPN را نیز پیاده سازی کرده اند که سبب امن تر شدن تأیید پرداخت میشه (البته تنها دلیل استفاده از این مکانیزیم امنیت نیست ) /
خلاصه مکانیزمIPN:
· شروع پرداخت در وبسایت فروشنده و هدایت به پیپال.
· تأیید پرداخت در پیپال.
· ارسال پیام IPN به URL مشخص شده توسط فروشنده.
· دریافت و پردازش پیام IPN توسط سرور فروشنده.
· تأیید دریافت و پردازش پیام IPN به پیپال.
· تکمیل تراکنش و ارائه کالا یا خدمات.
آسیب پذیری IDOR
من اولین تستی که روی درگاه پرداخت دیوار انجام دادم آسیب پذیری IDOR بود، میخواستم ببینم ایا میشه یک سفارش رو با مبلغ کمتری پرداخت کرد یانه برای این کار باید ابتدا یک سفارش با مبلغ بیشتر ایجاد میکردم و آیدی اون سفارش رو بدست می آوردم برای بدست آوردن آیدی سفارش ابتدا به بخش پرداخت آگهی رفتم و بروی نردبان کردن آگهی زدم که هزینه این آیتم 60 هزار تومان بود بعد اینکه بروی خرید این آیتم زدم و وارد درگاه پرداخت اینترنتی شدم بروی انصراف از خرید زدم در این حالت یک درخواست به اند پوینت زیر ارسال می شد که شامل پارامتر های مختلفی بود من پارامتر ResNum را از داخل این درخواست یاداشت کردم و سپس درخواست را Dropکردم.
درخواست مربوط به انصراف از خرید
POST /v8/paymentcore/saman/callback/ HTTP/1.1
Host: api.divar.ir
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
Accept-Language: en-US,en;q=0.5
Content-Length: 219
Origin: https://sep.shaparak.ir
MID=10880614&TerminalId=10880614&RefNum=&ResNum=Sa5deqm_lCjitDMgj&State=CanceledByUser
در درخواست روبرو
ResNumهمان آیدی سفارش در پذیرنده (دیوار)هست
RefNumمقدار رسید دیجیتال بانکی است (چون اینجا پرداختی انجام نشده مقدار آن خالی است )
Stateهم وضعیت پرداخت می باشد
حالا باید می رفتم و یک آیتم با مبلغ کمتر رو انتخاب و پرداخت می کردم سپس وقتی اطلاعات پرداخت به پذیرنده ( دیوار ) ارسال می شد مقدار ResNum سفارش 60 هزار تومانی را جایگزین میکردم در آن درخواست ، سپس درخواست را ارسال می کردم اگر سامانه آسیب پذیر بود سفارش 60 هزار تومانی تأیید می شد پس از پرداخت در درگاه بانکی با درخواست زیر مواجه می شدیم که من مقدار ResNum آن را تغییر و ResNum که در مرحله قبل یاداشت کرده بودم را جایگزین کرده و درخواست رو ارسال کردم. سامانه آسیب پذیر نبود .تراکنش لغو و پول به حساب بانکی من برگشت خورد .
درخواست تراکنش موفق
POST /v8/paymentcore/saman/callback/ HTTP/1.1
Host: api.divar.ir
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
Accept-Language: en-US,en;q=0.5
Content-Length: 219
Origin: https://sep.shaparak.ir
MID=10880614&TerminalId=10880614&RefNum=Gs01f23$5123123adsfjklda$SDALDSAOFFADSIDSA211&ResNum= Sa5deqm_lCjitDMgj&State=OK
در سامانه های آسیب پذیر سامانه استعلام RefNum را از بانک دریافت می کند و در صورت معتبر بودن RefNumدیگر بررسی نمی کند که آیا مبلغ RefNum با فاکتوری(ResNum) که در سامانه تأیید می شود یکسان است یا خیر سامانه فاکتور را تأیید و Refnum را برای جلوگیری از Double spending در پایگاه داده ذخیره میکنه.
سامانه دیوار ولی این مورد رعایت شده بود ،دیوار ابتدا یک درخواست به بانک ارسال می کرد و استعلام RefNumرا دریافت می کرد ،بانک در پاسخ استعلام وضعیت پرداخت و مبلغ پرداختی را باز می گرداند ،سپس سامانه دیوار بررسی میکرد که ایا مبلغ فاکتوری که میخواهد تأیید کند (ResNum) با مبلغی که بانک از استعلام RefNum بازگرداند برابر است یا خیر در صورتی که تفاوتی در این دو باشد سامانه دیوار درخواست Reverse را به بانک ارسال و تراکنش رو بازگشت می داد که باعث می شد پول به حساب بانکی پرداخت کننده برگرده تا اینجای کار مشکلی وجود نداشت اما سناریویی در ذهنم نقش بست که اگر درست پیش می رفت می شد با استفاده از یک رسید دیجیتال ابتدا یک فاکتور رو پرداخت کرد و سپس با ایجاد درخواستی متفاوتی سامانه رو مجبور به ارسال درخواست لغو تراکنش به بانک می کرد.
فقط قبل از اینکه مرحله بعد شروع بشه نیازه که در مورد Reverse تراکنش یه توضیحی بدم تا مسئله شفاف تر بشه پذیرنده تا 50 دقیقه بعد از تأیید تراکنش می تونه درخواست اصلاح تراکنش رو به بانک ارسال کنه و سبب لغو تراکنش و بازگشت پول به حساب بانکی واریز کننده بشه .
آسیب پذیری Race conditions
سامانه پس از اینکه تراکنش را تأیید می کند برای جلوگیری از Double spending مقدار Refnum را در پایگاه داده ذخیره می کند و قبل از استعلام هر پرداخت نیز ابتدا پایگاه داده را بررسی میکند که Refnum تکراری نباشه بعد فرآیند استعلام و تأیید پرداخت رو انجام میده .
سناریویی که در ذهنم نقش بسته بود این بود که اگر سامانه به Raceconditions آسیب پذیر یود می شد از یک Refnum بیش از یک بار مصرف کرد یعنی از یک Refnum تو دو تا درخواست استفاده می کردیم وچون درخواست ها همزمان پردازش می شد قبل از اینکه Refnumدر پایگاه داده ذخیره بشه میرفت سمت استعلام از بانک و من می تونستم سناریوم رو اجرا کنم.
من یک پرداخت موفق انجام دهم بدون تغییر در پارامتر های آن و این درخواست را به Repeater ارسال کنم سپس درخواست را تکثیر کرده و در درخواست دوم مقدار ResNum مربوط به یک سفارش با مبلغ متفاوت را قرار داهم سپس دو درخواست را در برپ سوئیت داخل یک گروه قرار داده و درخواست ها را تکنیک single packet attackارسال کنم اگر سناریو من درست اجرا می شد درخواست اول سبب تأیید پرداخت و درخواست دوم چون مبلغ متفاوت بود سبب ارسال درخواست Reverse به بانک و برگشت پول به حساب بانکی می شد.
و تمام 😊 سناریو به بار نشست و فاکتور اول تأیید شد و درخواست دوم باعث شد که دیوار درخواست لغو تراکنش رو به بانک ارسال کنه که سبب بازگشت پول به کارت بانکی من می شد.
بخش دوم پرداخت چند فاکتور با یک رسید دیجیتال
حالا که میشد از یک رسید دیجیتال بیش از یکبار استفاده کرد حدس زدم که امکان پرداخت چند فاکتور با یک رسید دیجیتال نیز وجود داشته باشد برای تست این مورد کافی بود سفارش های هم قیمت ایجاد کرده و مثل مرحله قبل درخواست را با تکنیک single packet attack ارسال کنم که سبب پرداخت همه فاکتورها با یک رسید دیجیتال بانکی می شد.
امیدوارم که از خوندن این رایتآپ لذت برده باشید سعی کردم که همه موارد شفاف و با جزئیات بیان بشه این آسیب پذیری اسفند 1402 به پلتفرم دیوار گزارش شد و شرکت آگه پردازان هوشمند (دیوار) برای این آسیب پذیری 50 میلیون تومان بانتی پرداخت کرد و پس از مدتی هم آسیب پذیری برطرف شد شما هم برای گزارش آسیب پذیری هاتون می تونید به لینک زیر مراجعه کنید و آسیب پذیری های مربوط به پلتفرم دیوار رو ارسال کنید.
https://divar.ir/bugbounty