سلام، من مجید هستم و توی این پست قصد دارم فرایند کشف آسیب پذیری CVE-2023-34194 رو شرح بدم. همین اول کار بگم که این Research همراه با دوست خوبم پویا انجام شد. نکته دیگه اینکه وقتی گزارش به وندور فرستاده شد متاسفانه قبل ما یه تیم به فاصله 10 روز، همین آسیب پذیری رو پیدا کرده بود. اما چون روش کشف برای ما تجربه جالبی بود ما هم این مقاله رو با اجازه ناشر نوشتیم.
داستان از این قراره که ما تصمیم گرفیتم روش های تحلیل فایل های باینری برای کشف آسیب پذیری رو روی یک تارگت واقعی شروع کنیم تا دستمون راه بیافته! برای اینکار هم Firmware های IOT رو انتخاب کردیم چون معمولا سازوکارهای Exploit Mitigation توی این دسته محصولات کمتر دیده شده. یه روز که در مورد روش های رمزگشایی فایل Firmware ها صحبت میکردیم، به این مقاله برخوردیم و بعد از مطالعه هم تصمیم گرفتیم که بشنیم و روی همین تارگت کار کنیم. حالا تارگت ما شد فریمور RV50 از محصولات شرکت Sierra Wireless.
The AirLink® Raven RV50 is the industry’s lowest power LTE gateway. Simple to install and easy to manage, the Raven RV50 industrial gateway is designed to connect critical assets and infrastructure. Ideal for industrial-grade applications in energy, utilities and smart-city infrastructure, the Raven RV50 provides real-time remote connectivity for SCADA, distribution management systems and metering.
خوب اولین کاری که باید انجام میشد این بود که Firmware رو از سایت اصلی دانلود بشه. برای ما مهم بود که فایل های فایل سیستم دسترسی داشته باشیم، پس با ابزار binwalk اون رو استخراج کردیم و رسیدیم به فایل rootfs.sqfs.uboot.extracted این فایل رمز شده ی فایل سیستم بود (الگوریتم رمزگذاری AES و کلید رمزگشایی هم ورژن Firmware هست).
حالا باید طبق مقاله رمزگشایی انجام میشد. این کار رو کردیم و با استخراج فایل بدست اومده توسط ابزار binwalk، رسید به فایل سیستم دستگاه :)
کارمون تازه شروع شد. شروع به گشتن کریدم و اولین کارمون این شد که فایل اجرایی پنل وب رو پیدا کنیم. سیتم عامل دستگاه یه لینوکس برای سخت افزار ARM بود و برای مدیریت سرویس ها هم از initng استفاده میکرد. توی سرویس های سیستم عامل، سرویس زیر محل فایل باینری رو به ما نشون داد
$ cat /etc/initng/daemon/acemanager.i
اطلاعاتی ک بدست اومد شد این موارد:
· فایل اجرایی مدیریت پنل با نام ACEmanager و در مسیر sbin/ACEmanager/ قرار داره
· فایلهای کلید خصوصی و عمومی پروتکل HTTPS در مسیر etc/ACEmanager/certs/ قرار داره
· فایلهای استاتیک مربوط به پنل مدیریتی وب (شامل .html، .js و .cgi) در مسیر www/ قرار داره
کتابخانه های موردنیاز ACEmanager، در مسیر /lib/ قرار دارن که طبق دستور زیر بدست اومدن:
$ readelf –d /sbin/ACEmanager
حالا نوبت شبیه سازی دستگاه شد. پس برای داشتن یک سیستم عامل با معماری ARM، از ابزارهای qemu-system-arm و Buildroot استفاده کردیم. Buildroot مجموعهای از پیکربندی هاییه که فرآیند ساخت یک محیط لینوکس کامل و قابل بوت رو برای Embedded System ها ساده و خودکار میکنه و از ابزار QEMU استفاده کردیم تا بتونیم این محیط شبیه ساز رو اجرا کنیم.
Buildroot is a set of Makefiles and patches that simplifies and automates the process of building a complete and bootable Linux environment for an embedded system, while using cross-compilation to allow building for multiple target platforms on a single Linux-based development system
QEMU is a free and open-source emulator. It emulates a computer's processor through dynamic binary translation and provides a set of different hardware and device models for the machine, enabling it to run a variety of guest operating systems.
توضیحات پیکربندی های Buildroot و QEMU رو دیگه رد میشم. نوع سخت افزاری که باید شبیه سازی میشد، armv7l GNU/Linux بود. بعد از اون باید فایل هایی که نیاز بود رو، از فایل سیستم رمزگشایی شده Firmware به فایل سیستم شبیه ساز انتقال میدادیم. پس این مراحل رو انجام دادیم:
- ابتدا فایل سیستم شبیهساز به یک پوشه با نام دلخواه (در اینجا try) Mount شد:
$ mkdir try
$ sudo mount squashfs-root try
- بعدش کلیه کتابخانه ها، فایل های استاتیک پنل وب و گواهینامه ها به فایل سیستم شبیه ساز انتقال داده شد:
$ sudo cp squashfs-root/sbin/ACEmanager try/sbin/
$ sudo cp –r squashfs-root/lib/[LIBRARY-NAME].so* try/lib/
$ sudo chmod 755 try/lib/[LIBRARY-NAME].so*
$ sudo mkdir –p try/etc/ACEmanager/certs
$ sudo cp –r squashfs-root/etc/ACEmanager/certs/server.crt try/etc/ACEmanager/certs/server.crt
$ sudo cp –r squashfs-root/etc/ACEmanager/certs/server.key try/etc/ACEmanager/certs/server.key
حالا باید شبیه ساز اجرای میشد. با استفاده از اسکریپت start-qemu.sh، شبیهساز اجرا شد:
دستور اجرایی این فایل برای مشاهده پنل وب همون طور که قبلا نشون داده شد برابر دستور زیره:
$ /sbin/ACEmanager -d "/www" -n -c "/etc/ACEmanager/certs/server.crt" -k "/etc/ACEmanager/certs/server.key"
با اجرای این دستور توسط برنامه Strace مشاهده شد که تعدادی از فایلهای موردنیاز باید از فایل سیستم Frimware به فایل سیستم شبیهسازیشده انتقال داده بشه و توی پوشه tmp هم فایلی با نام swialeosdefs ایجاد بشه:
$ sudo rsync -a --ignore-existing squashfs-root/ try/ $ touch try/tmp/swialeosdefs
درنهایت برای دسترسی به پنل وب بر روی هاست اصلی باید پورت 80 شبیهساز به هاست اصلی متصل میشد. برای این کار باید در فایل start-qemu.sh، مقدار شخص شده در شکل زیر اضافه بشه.
حالا پنل وب فایل اجرایی از هاست اصلی و از طریق پورت 8888 در دسترس ماست.
حالا کارمون این شد که روی نقاط مختلف برنامه عملیات Fuzz انجام بدیم. ما سناریوهای مختلفی انجام دادیم و یکی از اون ها روی فرایند لاگین به پنل وب بود. ابزاری که برای Fuzz کردن انتخاب کردیم Valgrind بود و جهت ایجاد TestCase از ابزار Radamsa استفاده شد. سناریویی که منجر به کشف خطا شد شامل اجرای عملیات Fuzz بر روی مقادیر نام کاربری و کلمه عبور درخواست های HTTP ارسالی بود. برای ارسال و دریافت داده بین سرور و رابط وب از XML استفاده میشه و درخواستی که برای ارسال نام کاربری و کلمه عبور بود هم این شکلی بود:
پس از اجرای این سناریو بر روی مقدار تگ username و password در داده XML ارسالی یک Crash به دست اومد. اسکریپت توسعه دادهشده برای انجام فاز در این مرحله مطابق شکل زیر بود.
حالا ACEmanager رو توسط Valgrind با دستور زیر و کد تولید TestCase رو هم اجرا کردیم:
$ valgrind /sbin/ACEmanager –d “/www” –n –c “/etc/ACEmanager/certs/server.crt”
و این شد که به کرش رسیدیم :)
پیلودی که منجر به Crash شد، مطابق زیر بود (مقدار خالی برای تگ password)
برای بررسی Crash بهدست اومده، با تحلیل دینامیک، خروجی ابزار GDB رو روی ابزار Ghidra بررسی کردیم. ابتدا برنامه را در مد دیباگر اجرا میکنیم.
برنامه بعد از لود شدن در GDBserver با دو سیگنال برخورد میکنه که روندی نرمال دارن و با دستور continue در GDB میشه از اونها عبور کرد. در مرحله آخر GDB اعلام میکنه که در حال ادامهی روند اجرای برنامه ست و فرایند لود کردن کتابخانهها و اسکریپتهای CGI به پایان رسیده و کنسول وب ACEmanager از طریق مرورگر قابلمشاهده شده. این یعنی برنامه با موفقیت اجراشده و وب سرور منتظر دریافت درخواستهای HTTP از طریق رابط وبه.
با ارسال یک درخواست احراز هویت، طبق خروجی GDB، برنامه برای بررسی نام کاربری و کلمه عبور ارسالشده به ماژولهای امنیتی PAM رجوع میکنه و مقادیر Username و Password دریافتی رو چک میکنه. در ادامهی روند Fuzz، اسکریپت به ارسال درخواستها ادامه میده تا جایی که Crash رخ بده.
در این مرحله یک Request با ساختار غیرعادی توسط وب سرور دریافت شده و باعث رخ دادن خطا
Segmentation Fault میشه. این مشکل در تابع ()ACEmanager::connect و در هنگام پردازش درخواست احراز هویت توسط وب سرور اتفاق افتاده. با بررسی بیشتر و تست کردن حالتهای مختلف مشخصشده که عامل اون خالی بودن تگ password در محتوای درخواست ارسالشده هست. بهاینترتیب که اگر این تگ به شکل <password></pasword> و بدون مقدار به سمت سرور ارسال بشه، باعث Crash سرویس و خطای اون میشه.
آسیبپذیری CVE-2021-42260 در کتابخانهی TinyXML و ماژول TiXmlParsingData::Stamp شناساییشده که مربوط به عملیات پردازش درخواستهای تحت وب و پیامهای XML هست. این آسیبپذیری به دلیل تکرار بینهایت یک حلقه به وجود اومده که هیچوقت بهشرط موردنیاز برای خارج شد از اون نمیرسه. برای استفاده از این آسیبپذیری، مهاجم باید یک XML Message با ساختار متناقض به وب سرور ارسال کنه تا برنامه وارد این حالت حلقه بینهایت بشه.
همانطور که اشاره شد، Crash کردن سرویس در تابع ()connect از ACEmanager اتفاق میافته. این تابع وظیفهی دریافت Request و ارسال آن به توابع دیگر برای بررسی نوع درخواست و برقراری ارتباط استفاده میکنه. این تابع در اصل نقش تابع مادر برای پیادهسازی توابع XML Parser و PAM Authentication رو داره و در بین توابعی که از ()ACEManager::Connect فراخوانی میشن، تابع TiXmlParsingData::Stamp دیده میشه. در سناریوی فعلی برای استفاده از این آسیبپذیری از یک HTTP Request که تگ password در اون خالیه استفاده شده چرا که پردازش XML توسط این کتابخانه در ACEmanager انجام میشه.
حالا به بررسی تابع stamp از روش Dynamic Analysis با استفاده از ابزارهای gdbserver remote debugging و Ghidra پرداخته شد (آسیب پذیری در این تابع TinyXML گزارش شده). برای شروع ابتدا یک Breakpoint بر روی تابع main قرار دادیم. با این کار، کتابخانهی TinyXML در پروسهی جاری بارگذاری شده و میشه برای بررسی دقیقتر بر روی توابع اون Breakpoint گذاشت.
در مرحلهی بعد، یک Breakpoint روی تابع stamp گذاشتیم. در این حالت ACEmanager منتظر دریافت HTTP Request هست. با ارسال Request (که در آن تگ password خالی نیست) برنامه، به Breakpoint بعدی در تابع stamp میرسه و محل پارامترهای ارسالشده به تابع stamp شناسایی شد.
همانطور که مشاهده میشه، تابع stamp درخواست XML را بهعنوان پارامتر ورودی از تابع connect دریافت کرده. طبق مستندات رسمی کتابخانهی TinyXML روند بررسی درخواست و خواندن پارامترهای احراز هویت کاربر به شکل زیره:
در ادامه پسورد دریافت شده از Request بهعنوان یکی از پارامترهای تابع بارگذاری شده و درصورتیکه این تگ خالی نباشه پس از اجرای این تابع، برنامه به تابع caller که همان ()ACEManager::connect هست، برمیگرده.
حالا مطابق شکل، همین سناریو را با تگ password خالی اجرا کردیم و فرایند قبلی را تکرار کردیم تا به تابع موردنظر رسیده تا محتویات Stack و پارامترهای تابع بررسی بشن:
طبق مستندات آسیبپذیری، حالت Infinite Loop در این قسمت از کد و در هنگام تجزیه تگ اتفاق میافته. شکل زیر هم مقایسهی کد تابع stamp با کد Disassembler در Ghidra ست.
شکل زیر هم رسیدن برنامه به تابع stamp و حلقهی While موجود در آن را نمایش میده. از اونجا که آسیب پذیری در این حلقه اتفاق میافته، به بررسی دستورالعملها پرداخته شد:
همانطور که در شکل مشاهده شد، حلقه در دستورالعمل:
cmp r10, #1
به حالت Infinite میرسه و برنامه تا بینهایت به همون Breakpoint قبلی برمیگرده. این همون حالتیه که باعث میشه برنامه از تابع stamp خارج نشه و با ادامهی Infinite Loop منجر به مصرف منابع سیستم و درنتیجه کرش کردن سرویس ACEManager بشه. به همین دلیل، اگر Breakpoint های قبلی را غیرفعال کنیم، پس از یک مدتزمان کوتاه و با افزایش میزان استفاده از منابع سیستم، برنامه با خطا سیستمی از تابع stamp به connect بازمیگرده و با سیگنال segmentation fault کرش میکنه.
کد PoC رو هم از اینجا در اختیار دارین.
مرسی که تا آخرش بودی. راه ارتباطی با من در توییتر.