پارسیا
پارسیا
خواندن ۹ دقیقه·۴ سال پیش

آسیب پذیری Remote Code Execution در PlayStation Now

این اولین بانتیِ PlayStation من و اولین متن فارسیِ من درباره امنیت است. متون فارسی در سایت خودم به دلیل راست به چپ بودن درست نمایش داده نمی‌شدند، این شد که به ویرگول روی آوردم. دستشون درد نکنه که اکانت من را تایید کردند.

نوشتن در مورد امنیت برای من به زبان فارسی سختِ. "خارجی" نشدم (البته فامیل نظرشان متفاوت است) ولی امنیت و اصطلاحاتش را به زبان انگلیسی یاد گرفته ام و زبانِ کار روزمره ام هست. در مقابل مثلاً در زمینه "کلیله و دمنه" به انگلیسی نمی‌توانم صحبت کنم.

  1. این گزارش مربوط به "هک کنسول بازی پلی استیشن" نیست. این گزارش در مورد برنامه ویندوز PlayStation Now یا psnow است که برای استریم بازیهای پلی استیشن استفاده می‌شود.
  2. این مقاله ترجمه آزادی از گزارش من در Hacker One به پلی استیشن در https://hackerone.com/reports/873614 است. پیشنهاد می‌کنم آن را بخوانید.
باگ در Hacker One
باگ در Hacker One

‌مقدمات

این گزارش ترکیبی از سه باگ منطقی مختلف است. هر کدام از این باگ ها در یک محدوده تحقیقاتی مختلف قرار دارند. برای یادگرفتن این باگ ها شاید لازم باشد که موارد زیر را بدانید. در خیلی از موارد توضیحات مفصلی ندادم چون نمی‌شود همه چیز را در گزارش توضیح داد. متاسفانه تمام این لینک ها به زبان انگلیسی هستند (انگلیسی آش کشکِ خاله است، آخرش باید یاد بگیرید?).

  1. نزدیک یک سال قبل من ارائه ای در مورد این باگ ها (این باگ هنوز disclose نشده بود) در Appsec village کنفرانس DEF CON 2020 داشتم. در آن یکی از باگ های مشابه خودم برای برنامه Attack Surface Analyzer مایکروسافت را بررسی کردم. ویدئو و اسلاید این ارائه:
    https://parsiya.net/blog/2020-08-13-localghost-escaping-the-browser-sandbox-without-0-days/
  2. اطلاعات مربوط به هک برنامه های فریمورک الکترون
    https://github.com/doyensec/awesome-electronjs-hacking
  3. پیدا کردن سرورهای localhost و ترافیک برنامه های ویندوز
    https://parsiya.net/blog/2015-08-01-network-traffic-attribution-on-windows
  4. باگ های Tavis Ormandy از Google Project Zero درباره سرورهای localhost
    https://bugs.chromium.org/p/project-zero/issues/list?q=owner%3Ataviso%40google.com%20localhost&can=1

خلاصه

برنامه PlayStation Now نگارش 11.0.2 دارای آسیب پذیری "اجرای کد از راه دور" یا Remote Code Execution یا RCE است. هر وب سایتی که در کامپیوتری که برنامه را اجرا می‌کند باز شود می‌تواند از طریق یک websocket روی آن ماشین کد اجرا کند.

  1. سرور websocket که روی پورت 1235 اجرا می‌شود هدر origin ریکوئست ها را چک نمی‌کند. این به وب سایتهایی که روی ماشین باز شده‌اند اجازه می‌دهد که مستقیم به این سرور متصل شوند و داده بفرستند. websocket ها توسط Same-Origin Policy کنترل نمی‌شوند. یک وب سایت باز شده در مرورگر می‌تواند یک ارتباط websocket با هر سرور در دسترس ایجاد کند.
  2. برنامه psnow یک برنامه Electron به نام AGL را اجرا می‌کند. با فرستادن داده به سرور websocket می‌توان به AGL دستور داد تا هر URL که بخواهیم را باز کند. در نتیجه، هر وب سایت باز شده در ماشین می‌تواند چنین کاری انجام دهد. همچنین می‌توان توسط دستور setUrlDefaultBrowser به AGL دستور داد که یک executable را اجرا کند (مثلاً calc).
  3. در برنامه AGL فیلد nodeIntegration فعال است. یعنی کد جاواسکریپت وب سایت باز شده در AGL میتواند از کتابخانه‌های Node استفاده کند و مثلاً process جدید ایجاد کند.

با سر هم کردن این سه باگ میتوانیم به RCE برسیم.

اصل ماجرا

برنامه PlayStation Now یک برنامه برای استریم بازیهای پلی استیشن روی ویندوز است (البته این برنامه روی کنسول PS4 هم وجود دارد اما اینجا در مورد آن صحبت نمی‌کنیم). این برنامه دو بخش اصلی دارد. QAS و AGL.

برنامه QAS

این برنامه اصلی بر پایه فریمورک Qt5 (کیوت یا cute تلفظ می‌شود). اسم اصلی این برنامه psnowlauncher.exe است. بعد از اجرا دو کار انجام می‌دهد:

  1. برنامه دیگری به نام AGL.exe را اجرا می‌کند.
  2. یک سرور websocket روی localhost:1235 می‌سازد.

برنامه AGL

برنامه AGL بر پایه فریمورک Electron است. این برنامه توسط QAS و به صورت زیر با یک سوییچ url اجرا می‌شود.

&quotC:\Program Files (x86)\PlayStationNow\agl\agl.exe&quot --url=https://psnow.playstation.com/app/1.10.43/105/00d3603f8/

بعد از اجرای برنامه، وب سایتی که با سوییچ url مشخص شده در برنامه باز می‌شود.

فریمورک Electron

هم Qt و هم Electron از فریمورک های پرطرفدار ساخت برنامه های دسکتاپ هستند. فریمورک الکترون بر پایه مرورگر متن باز Chromium ساخته شده است. مرورگرهای مشهوری مانند Google Chrome, Microsoft Edge و Brave هم از Chromium استفاده می‌کنند. در واقع هر صفحه یک برنامه الکترون یک تب مرورگر است که در آن یک وب سایت نشان داده می‌شود.

یکی از اصلی ترین نکات هنگام بررسی یک برنامه الکترون بررسی فیلد nodeIntegration است. در صورت فعال بودن (به صورت پیش فرض غیرفعال است) کد جاوااسکریپت داخل مرورگر می‌تواند از کتابخانه‌های Node استفاده کند. من از دو روش برای چک کردن این فیلد استفاده می‌کنم:

  1. بازکردن کد برنامه و static analysis: کد جاوااسکریپت برنامه Electron معمولاً در یک فایل به نام app.asar است که میتوان آن را به راحتی باز و مشاهده کرد. بعد از دسترسی به کد برنامه سرچ کنید nodeIntegration و ببینید که آیا به مقدار true سِت شده است یا خیر.
  2. بازکردن یک وب سایت در برنامه که مثلاً برنامه ماشین حساب را اجرا می‌کند.

برای اطلاعات بیشتر به این رفرنس که در قسمت مقدمات معرفی کردم مراجعه کنید.

مشکل اول: nodeIntegration

برای تست این فیلد من از روش دوم استفاده کردم چون خیلی راحت‌تر بود. یک صفحه با کد زیر را در یک s3 bucket (سطل؟) ذخیره کردم. این کد ماشین حساب ویندوز را از طریق ماژول child_process اجرا میکند. کدباکس ویرگول تگ های script را فیلتر می‌کند، برای همین از عکس استفاده کرده‌ام:

popping calc with Node
popping calc with Node

سپس برنامه را دستی با سویچ url اجرا کردم.

&quotC:\Program Files (x86)\PlayStationNow\agl\agl.exe&quot --url=https://[redacted].s3.us-east-1.amazonaws.com/node.html

ماشین حساب ویندوز اجرا شد و من فهمیدم که مقدار آن فیلد سِت شده است.

تا اینجا کار خارق العاده ای انجام ندادم، روی کامپیوتر خودم دستی کد اجرا کرده‌ام. به قول این بلاگ، هنوز در سمت دیگر "دریچه" (یعنی روی کامپیوتر خودم) هستم.

مشکل دوم: سرور websocket

همانطور که بالا دیدیم برنامه AGL روی 1235 یک سرور websocket می‌سازد. قدم بعدی پراکسی کردن برنامه توسط Burp بود. برنامه های بر پایه مرورگر Chromium معمولاً از تنظیمات پراکسی ویندوز استفاده می‌کنند.

پراکسی کردن ترافیک برنامه های دسکتاپ

پراکسی این بحث مفصلی است و من تابحال 18 بلاگ درباره آن نوشته‌ام و هنوز جا برای تحقیق زیاد دارد:

بعد از پراکسی کردن این برنامه ها ترافیک زیادی در Burp دیدم. یکی از مشکلات پراکسی کردن با تنظیمات ویندوز این است که بسیاری از برنامه های دیگر و سرویس های ویندوز نیز ترافیک خود را به پراکسی می‌فرستند. برای این کار می‌توانید دامنه‌های بی‌ربط را به TLS Passthrough اضافه کنید تا Burp آنها را پراکسی نکند. برای اطلاعات بیشتر در این زمینه بلاگ زیر را بخوانید:

ترافیک برنامه‌ها را توسط user-Agent شان تشخیص دادم. هر دو برنامه کلمه gkApollo را در user-agent داشتند. پس بقیه ترافیک به درد من نمی‌خورد. برای تشخیص ترافیک این دو برنامه از هم به کلمات دیگر در user-agent دقت کردم:

  • ترافیک برنامه QAS که بر پایه Qt است این کلمه دارد QtWebEngine/5.5.1.
  • ترافیک برنامه AGL که بر پایه الکترون است این دو را دارد Electron/1.4.16 و playstation-now/0.0.0.

توسط یک افزونه به نام Request Highlighter در Burp، ترافیک این دو برنامه را زرد و آبی کردم که تشخیص آنها از هم راحت‌تر باشد.

پروتکل پیام های websocket

پس از پراکسی کردن در Burp ترافیک سرور websocket را می‌بینیم. پیام‌های پروتکل این سرور بسیار ساده بودند و به نظر می‌رسید توسط JSON.stringify جاوااسکریپت ایجاد شده‌اند. پیام ها به این شکل بودند:

{ &quotcommand&quot: &quotisMicConnected&quot, &quotparams&quot: {}, &quotsource&quot: &quotAGL&quot, &quottarget&quot: &quotQAS&quot }
  1. بخش اول فرمان یا command است که می‌گوید این دستور باید چه کاری انجام دهد.
  2. بخش دوم پارامترهای دستور است.
  3. بخشهای سوم و چهارم مبدا و مقصد پیام هستند. در واقع مبدا پیام مهم نیست و فقط مقصد مهم است.

برای دیدن اینکه چه دستورهایی در برنامه وجود دارند دو کار کردم:

  1. برنامه را اجرا کردم و چند کار داخل برنامه انجام دادم سپس ترافیک داخل Burp را دیدم.
  2. کد برنامه الکترون را باز کردم و اسم چند دستور که در Burp دیده بودم را سرچ کردم تا به بخش commandHandler رسیدم. در این بخش همه دستورات سرور وجود داشتند.

دو دستور مهم هستند: setUrl و setUrlDefaultBrowser.

دستور setUrl به برنامه می‌گوید که کدام وب سایت را باز کند. پیام این دستور به این شکل است:

{ &quotcommand&quot: &quotsetUrl&quot, &quotparams&quot: { &quoturl&quot: &quothttps://psnow.playstation.com/app/1.10.43/105/00d3603f8/&quot }, &quotsource&quot: &quotAGL&quot, &quottarget&quot: &quotQAS&quot }

دستور setUrlDefaultBrowser یک فایل را به کمک سیستم عامل باز می‌کند. مثلاَ اگر ورودی آن یک آدرس وب سایت باشد، آن را در مرورگر باز می‌کند و یک فایل ورد در برنامه آفیس (اگر نصب شده باشد) باز می‌شود. چند روز بعد از گزارش باگ به من به یاد یکی از باگ های Tavis Ormandy افتادم که با استفاده از این دستور به RCE دست یافته بود.

اگر یک فایل اجرایی را با این دستور باز کنیم، ویندوز خود آن فایل را اجرا می‌کند. پس دستور زیر ماشین حساب ویندوز را اجرا می‌کند.

{ &quotcommand&quot: &quotsetUrl&quot, &quotparams&quot: { &quoturl&quot: &quotfile:///c:/windows/system32/calc.exe&quot }, &quotsource&quot: &quotAGL&quot, &quottarget&quot: &quotQAS&quot }

با استفاده از این دستور دیگر نیاز به nodeIntegration هم نداریم. اما این دستور یک محدودیت بزرگ دارد. فقط می‌توانیم فایل اجرا کنیم و نمی‌توانیم به فایل اجرایی پارامتر و یا سوییچ بفرستیم.

مشکل سوم: اجرای کد روی ماشین توسط دستورات websocket

در اینجا برنامه AGL (الکترون) به QAS می‌گوید که یک وب سایت را باز کند. در ابتدا من به مبدا و مقصد دقت نکردم و بی‌خیال باگ شدم چون بازکردن سایت در QAS فایده‌ای برای من نداشت. اما چند ساعت بعد وقتی کار دیگری انجام می‌دادم یادم آمد که چرا مقصد را AGL (برنامه الکترون) نگذارم؟ با فرستادن پیام پایین وب سایتی که در بخش اول ساخته بودم (ماشین حساب ویندوز را اجرا میکرد) در برنامه الکترون باز کردم و ماشین حساب ویندوز اجرا شد.

{ &quotcommand&quot: &quotsetUrl&quot, &quotparams&quot: { &quoturl&quot: &quothttps://example.net&quot }, &quotsource&quot: &quotAGL&quot, &quottarget&quot: &quotQAS&quot }

بعد از این کد یک برنامه چت بر پایه websocket را دستکاری کردم و در یک سطل (؟) s3 دیگری قرار دادم. بعد از باز کردن این وب سایت در مرورگر روی کامپیوتری که برنامه psnow در حال اجرا بود می‌توانستم با سرور websocket برنامه صحبت کنم و به آن دستور بفرستم.

امنیت websocket

اولین مرحله در ایجاد ارتباط با یک سرور websocket، فرستادن یک ریکوئست HTTP با تعدادی header خاص است. در حالت عادی Same-Origin Policy یا SOP مرورگر به اسکریپت های یک اریجین اجازه نمیدهد تا با اریجین دیگر ارتباط برقرار کنند (بحث SOP هم مفصل است ولی حتما در مورد آن بخوانید چون یکی از مهمترین بخشهای امنیت مرورگر و وب است). websocket ها شامل SOP نمیشوند. یعنی هر وب سایت میتواند با هر سرور websocket ارتباط برقرار کند.

حل این مشکلات

تا اینجا سه باگ را بررسی کردیم و فهمیدیم که اگر برنامه PlayStation Now را اجرا کنیم. وب سایتی که در مرورگر کامپیوتر ما باز شده است می‌تواند روی ماشین ما کد اجرا کند. این اصلاً خوب نیست.

یکی از مهمترین وظایف من به عنوان یک مهندس امنیت نرم افزار، حل مشکلات امنیتی است.

راحت‌ترین راه حل این مشکل چک کردن هِدِر origin در اولین بخش ایجاد ارتباط websocket است.

این هدر فقط توسط مرورگر می‌تواند اضافه شود (به این هدرها Forbidden Header Name گفته می‌شود). اولین ریکوئست ایجاد یک websocket شکلی مشابه زیر دارد:

GET /chat HTTP/1.1 Host: example.com:8000 Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== - مقدار این هدر معمولاً مهم نیست Sec-WebSocket-Version: 13 Origin: whatever.com

چون نمیتوان به مکانیزم SOP برای فیلتر کردن درخواست های websocket اتکا کرد باید به صورت دستی هدر origin را بررسی کنیم و مواردی که نمی‌خواهیم (مثلاً هر چه که از playstation.com نیست) را رد کنیم.

دومین مشکل این است که سرور روی همه IP های دستگاه در حال شنیدن است. به عبارت دیگر روی 0.0.0.0 بایند شده. این یعنی هر کسی که بتواند به پورت 1235 دستگاه از خارج دسترسی داشته باشد می‌تواند به آن وصل شود. در دنیای واقعی این مشکل آنقدر ترسناک نیست زیرا اکثر مودم و روترها تنها اجازه دسترسی به پورت را در شبکه داخلی می‌دهند.

برای حل این مشکل باید سرور روی localhost بایند شود.

چی یاد گرفتیم؟

متاسفانه در این باگ مرزهای علم را جابجا نکردم و دانش جدیدی تولید نشد. خودم هم چیز جدیدی یاد نگرفتم. اما امیدوارم خواننده چند نکته یادگرفته باشد. اگر بازخورد یا پیشنهادی دارید، پیدا کردن من در اینترنت بسیار راحت است.

باگ‌بانتیامنیت سایبریپلی استیشن
مهندس بیکار امنیت بازیهای کامپیوتری. نوشته‌های انگلیسی من در parsiya.net هستند.
شاید از این پست‌ها خوشتان بیاید