خرید روترهای ارزان قیمت برای یافتن آسیبپذیریها در آنها
فرمویرهای TP-Link به راحتی قابل دسترسی هستند، چون بهصورت رایگان و آنلاین قابل دانلود است.
اما این چیزی نیست که ما به آن نیاز داریم !
اکثر این دستگاهها یک پورت سریال برای دیباگ دارند. اتصال به آن باید کار سادهای باشد با استفاده از یک مبدل USB به UART.
Universal Asynchronous Receiver/Transmitter (UART) یک پروتکل سختافزاری ارتباط سریال (serial) هست که به دو دستگاه اجازه میده اطلاعات رو به صورت بیتبهبیت و بدون نیاز به هماهنگی زمانی (Clock) منتقل کنن. معمولاً برای ارتباط ساده بین میکروکنترلرها، بردها (مثل آردوینو، ESP، STM)، و ابزارهای دیباگ مثل روترها استفاده میشه.
اجزای اصلی ارتباط UART:
TX (Transmit) → ارسال اطلاعات
RX (Receive) → دریافت اطلاعات
معمولاً از 2 تا 4 سیم استفاده میکنه (TX, RX, GND و گاهی VCC)
با استفاده از این روش، شما میتونید بهصورت بالقوه روند بوت دستگاه رو قطع کنید و وارد یک شل سطح پایین بشید؛ چیزی مثل یک محیط دستوری که اجازه میده کارهایی مثل خواندن چیپ EEPROM در آفستهای مختلف انجام بدید.
شما میتواند با جستجو در اینترنت نمونه های زیادی را برای تهیه ببینید و استفاده نمایید .
جدا کردن (Desoldering) چیپ EEPROM
با استفاده از هیتر اقدام به گرم کردن چیپ و جدا سازی پایه ها از مدار می نماییم . این عمل برای دسترسی گرم به چیپ و خواندن و نوشتن برروی چیپ هست .
شما می تواند از این دستگاه در بازار به شکل ها و مدل های مختلف پیدا نمایید .
پروگرمر TL866II Plus یک ابزار سختافزاری همهکاره است که برای خواندن، نوشتن (برنامهریزی)، پاک کردن و بررسی چیپهای حافظه مثل EEPROM، Flash، و حتی بعضی میکروکنترلرها استفاده میشه.
خوب در اولین قدم باید مدل چیپ را شناسایی کنیم .
معمولاً چیپهای EEPROM رویشون اطلاعاتی مثل شماره مدل (مثل 25Q32, 24C08) و سازنده (مثل Winbond، Macronix) نوشته شده.
ما میتونیم روی چیپ عبارت AH1903 25Q32CS1G رو بخونیم، و لوگوی روی چیپ مربوط به شرکت GigaDevice هست.با جستوجوی مدل GD25Q32 در لیست چیپهای پشتیبانیشده توسط نرمافزار minipro، به این مورد میرسیم: GD25Q32 @SOP8
جستجو : minipro در گیت هاب (پ .ن پ : این ویرگول مشکل بررسی پست داره وتمام لینک ها رو با کمترین توجه تبلیغ می شمارد.)
minipro یک نرمافزار متنباز (open source) هست که برای ارتباط با پروگرمر TL866 و نسخههای جدیدش مثل TL866II+ استفاده میشه.
با minipro میتوانیم :
root@kali:~# minipro -p GD25Q32 -r firmware.bin
Found TL866II+ 04.2.86 (0x256)
Warning: Firmware is out of date.
Expected 04.2.118 (0x276)
Found 04.2.86 (0x256)
Chip ID OK: 0xC84016
Reading Code... 6.99Sec OK
فایل فرمویر ۴ مگابایتی است و شامل امضاهای زیر میباشد:
root@kali:~# binwalk firmware.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
53488 0xD0F0 U-Boot version string, "U-Boot 1.1.3 (Jun 14 2018 - 11:06:28)"
66048 0x10200 LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 2986732 bytes
1048576 0x100000 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 2955905 bytes, 610 inodes, blocksize: 262144 bytes, created: 2018-06-14 03:15:45
4063248 0x3E0010 XML document, version: "1.0"
ما بیشتر به squashfs علاقه داریم، چون این بخش حاوی سیستمفایلهای روتر است. با این حال، میتوانیم مشاهده کنیم که کد بوتلودر از آدرس 0xD0F0 شروع میشود، بنابراین احتمالاً اطلاعات جالبی در ابتدای فرمویر ذخیره شده است.
SquashFS یک سیستم فایل فشرده و فقطخواندنی (read-only) است که برای ذخیرهسازی دادهها بهطور فشرده و بهینه طراحی شده است. این سیستم فایل بهطور معمول در دستگاههای با منابع محدود مانند روترها، دستگاههای قابل حمل، و سیستمهای جاسازیشده (embedded systems) استفاده میشود.
آدرس 0xD0F0 یک آدرس هگزادسیمال است که به حافظه در سیستم اشاره دارد. این آدرس معمولاً نشاندهنده محل خاصی در حافظه دستگاه یا فایل است که در آن کد یا داده خاصی ذخیره شده است.
در بسیاری از فرمویرهای روتر، بوتلودر اولین قطعه کدی است که پس از روشن شدن دستگاه اجرا میشود. بوتلودر معمولاً وظیفه دارد تا سختافزار را راهاندازی کند، سیستمعامل را بارگذاری کند و به سیستم اجازه دهد که آماده کار شود.
تمامی فایلهای باینری برای معماری MIPS کامپایل شدهاند که رایجترین معماری برای روترها محسوب میشود. در پوشه /etc چند فایل جالب وجود دارد، بهویژه دو فایل رمزگذاریشده به نامهای default_config.xml و reduced_data_model.xml که در جستوجو درون کل squashfs بهدست میآیند. در این جستوجو، یک تطابق جالب پیدا کردیم: libcmm.so.
MIPS (Microprocessor without Interlocked Pipeline Stages) یک معماری پردازنده (CPU architecture) است که برای پردازش سریع و بهینه اطلاعات طراحی شده است. این معماری توسط MIPS Computer Systems در دهه 1980 توسعه یافت و بهویژه در سیستمهای جاسازیشده، روترها، و دستگاههای شبکهای مورد استفاده قرار میگیرد.
SquashFS یک سیستم فایل فشرده و فقطخواندنی (read-only) است که برای ذخیرهسازی دادهها بهطور فشرده و بهینه طراحی شده است. این سیستم فایل بهطور معمول در دستگاههای با منابع محدود مانند روترها، دستگاههای قابل حمل، و سیستمهای جاسازیشده (embedded systems) استفاده میشود.
کدهای کامپایلشده که به این فایلهای رمزگذاریشده ارجاع میدهند، ممکن است در حال رمزگشایی آنها باشند. بنابراین، ما کتابخانه شیء مشترک (shared object library) را با استفاده از ابزار Ghidra دیساسمبل میکنیم تا تحلیل بیشتری انجام دهیم.
Ghidra یک ابزار مهندسی معکوس (Reverse Engineering) است که توسط آژانس امنیت ملی ایالات متحده (NSA) توسعه داده شده است. این ابزار بهطور ویژه برای تجزیه و تحلیل باینریها و فایلهای اجرایی طراحی شده و به مهندسان معکوس و محققان امنیتی کمک میکند تا کد ماشین یا کدهای کمسطح را تحلیل و بازسازی کنند.
int dm_decryptFile(uint param_1, undefined4 param_2, uint param_3, int param_4) {
int iVar1;
char acStack40[8];
int local_20;
memcpy(acStack40, &encryption_key, 8);
if (param_3 < param_1) {
cdbg_printf(8, "dm_decryptFile", 0xb83, "Buffer exceeded, decrypt buf size is %u, but dm file size is %u", param_3, param_1);
local_20 = 0;
} else {
local_20 = cen_desMinDo(param_2, param_1, param_4, param_3, acStack40, 0);
iVar1 = local_20;
if (local_20 == 0) {
cdbg_printf(8, "dm_decryptFile", 0xb8a, "DES decrypt error\n");
} else {
do {
local_20 = iVar1;
if (((undefined *)(param_4 + local_20))[-1] != '\0') break;
iVar1 = local_20 - 1;
} while (local_20 != 0);
*(undefined *)(param_4 + local_20) = 0;
}
}
return local_20;
}
این کد مربوط به یک تابع به نام dm_decryptFile
است که به نظر میرسد برای انجام عملیات رمزگشایی با استفاده از الگوریتم DES طراحی شده باشد. در این کد، به بررسی ورودیها، انجام عملیات رمزگشایی و بررسی خطا پرداخته میشود.
param_1
: طول دادهای که قرار است رمزگشایی شود.param_2
: ورودی که احتمالاً حاوی اطلاعات کلیدی یا دادهای برای رمزگشایی است.param_3
: اندازه بافر که دادهها در آن ذخیره میشوند.param_4
: به احتمال زیاد آدرس یا موقعیت ذخیرهسازی دادههای رمزگشاییشده.acStack40
کپی میشود.cen_desMinDo
برای انجام عملیات رمزگشایی DES استفاده میشود.cen_desMinDo
برابر با صفر باشد)، پیام خطا نمایش داده میشود.do-while
، چک میشود که آیا آخرین بایت دادهها صفر است یا نه. این برای اطمینان از پایان یافتن صحیح رمزگشایی و پاکسازی فضای ذخیرهسازی استفاده میشود.کپی کردن آرایه 8 بایتی در بافر محلی:
این کد به وضوح یک آرایه 8 بایتی را به یک بافر محلی کپی میکند. این 8 بایت مربوط به کلید DES است که معمولاً 7 بایت داده به همراه یک بایت برای توازن (parity byte) را شامل میشود.
فراخوانیcen_desMinDo
:
این تابع از کتابخانهlibcutil.so
فراخوانی میشود و به نظر میرسد که یک پوشش (wrapper) برای توابع رمزنگاری DES است. به عبارت دیگر، این تابع برای انجام عملیات رمزنگاری یا رمزگشایی DES طراحی شده است.
openssl enc -d -des-ecb -nopad -K XXXXXXXXXXXXXXXX -in default_config.xml > default_config_decrypted.xml
openssl enc
: این دستور برای انجام عملیات رمزنگاری یا رمزگشایی استفاده میشود.-d
: نشاندهنده این است که عملیات رمزگشایی (decrypt) انجام شود.-des-ecb
: نشاندهنده الگوریتم رمزنگاری DES (Data Encryption Standard) با استفاده از حالت ECB (Electronic Codebook) است. در این حالت، هر بلوک داده به صورت جداگانه رمزنگاری میشود.-nopad
: این گزینه به عدم استفاده از پرکردن (padding) اشاره دارد. در نتیجه، ورودی باید اندازهای که در الگوریتم DES معین است (مثلاً 8 بایت) داشته باشد.-K XXXXXXXXXXXXXXXX
: اینجا باید کلید DES (16 کاراکتر hexadecimal) را وارد کنید که برای رمزگشایی استفاده خواهد شد. این مقدار به صورت hex نمایش داده میشود و باید 16 کاراکتر باشد.-in default_config.xml
: این گزینه نشان میدهد که فایل default_config.xml
که شامل دادههای رمزنگاری شده است، ورودی برای رمزگشایی خواهد بود.>
: علامت بزرگتر برای هدایت خروجی به یک فایل جدید استفاده میشود.default_config_decrypted.xml
: این فایل مقصد است که دادههای رمزگشایی شده در آن ذخیره خواهند شد.به طور کلی، این دستور فایل default_config.xml
را که با DES و بدون padding رمزنگاری شده است، رمزگشایی میکند و خروجی رمزگشایی شده را در فایل default_config_decrypted.xml
ذخیره میکند.
خروجی فایل به شکل ذیل می باشد.
<StorageService>
<UserAccount instance="1">
<Enable val="1" />
<Username val="admin" />
<Password val="admin" />
<X_TP_Reference val="0" />
<X_TP_SupperUser val="1" />
</UserAccount>
</StorageService>
در اینجا، اشاره به این است که سیستم از روشهای قدیمیتر ذخیرهسازی اطلاعات و رمزهای عبور استفاده میکند، بهویژه با اشاره به فایلهای passwd که معمولاً شامل اطلاعات کاربری و رمز عبور به صورت متنی است، و اشاره به نبود فایل shadow که معمولاً برای نگهداری اطلاعات رمزهای عبور به صورت امنتر استفاده میشود.
admin:$1$$iC.dUsGpxNNJGeOm1dFio/:0:0:root:/:/bin/sh
dropbear:x:500:500:dropbear:/var/dropbear:/bin/sh
nobody:*:0:0:nobody:/:/bin/sh
خوب به نظر می رسد که رمز عبور کاربر اول ($1) از نوع هش MD5 باشد .
root@kali:~# hashcat -a 0 -m 500 hash /usr/share/wordlists/rockyou.txt
$1$$iC.dUsGpxNNJGeOm1dFio/:1234
Session..........: hashcat
Status...........: Cracked
Hash.Name........: md5crypt, MD5 (Unix), Cisco-IOS $1$ (MD5)
Hash.Target......: $1$$iC.dUsGpxNNJGeOm1dFio/
Time.Started.....: Wed Oct 28 14:10:47 2020 (1 sec)
Time.Estimated...: Wed Oct 28 14:10:48 2020 (0 secs)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 3279 H/s (11.38ms) @ Accel:256 Loops:125 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests
Progress.........: 3072/14344385 (0.02%)
Rejected.........: 0/3072 (0.00%)
Restore.Point....: 0/14344385 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:875-1000
Candidates.#1....: 123456 -> dangerous
عبارت به این معنی است که "admin:1234" رمز عبور پیشفرض برای دسترسی روت به روتر است و میتوان از این اطلاعات برای اتصال به روتر، مثلاً از طریق پورت سریال، استفاده کرد. البته، انتظار نمیرفت که رمزهای پیچیدهای برای اطلاعات پیشفرض تنظیم شده باشند.
کاربر جالب دیگری که در اینجا دیده میشود، dropbear است. این برنامه یک سرور SSH سبک برای سیستمهای تعبیهشده (embedded) است و میتواند به یک کاربر اجازه دهد که بهصورت راه دور به روتر احراز هویت کند. اما اطلاعات ورود dropbear چگونه مقداردهی اولیه میشوند؟
با جستوجو برای دادههای مرتبط با dropbear، دوباره به فایل libcmm.so برخورد میکنیم و به رشتهای بسیار جالب به نام ذیل میرسیم.
/var/tmp/dropbear/dropbearpwd
این فایل در سیستم فایل squashfs وجود ندارد، پس باید در زمان راهاندازی (startup) ایجاد شود.
در واقع، این فایل درون تابعی به نام setDropbearLogin
ساخته میشود.
undefined4 setDropbearLogin(int conf_obj)
{
undefined4 uVar1;
FILE *__stream;
size_t length;
byte *pbVar2;
int iVar3;
char *__s;
char acStack112[36];
byte dest[32];
undefined local_2c;
char *password;
memset(dest,0,0x21);
if ((*(char *)(conf_obj + 0x22) == '\0') || (*(char *)(conf_obj + 0x32) == '\0')) {
cdbg_printf(8,"setDropbearLogin",0xe3,"uname = %s, pswd = %s\n",conf_obj + 0x22,conf_obj + 0x32);
uVar1 = 1;
}
else {
__stream = fopen("/var/tmp/dropbear/dropbearpwd","wb+");
uVar1 = 1;
if (__stream != (FILE *)0x0) {
fprintf(__stream,"username:%s\n",conf_obj + 0x22);
password = (char *)(conf_obj + 0x32);
__s = acStack112;
length = strlen(password);
iVar3 = 0;
cen_md5MakeDigest(dest,password,length);
memset(acStack112,0,0x21);
do {
pbVar2 = dest + iVar3;
iVar3 = iVar3 + 1;
sprintf(__s,"x",(uint)*pbVar2);
__s = __s + 2;
} while (iVar3 != 0x10);
memcpy(dest,acStack112,0x21);
local_2c = 0;
fprintf(__stream,"password:%s\n",dest);
fclose(__stream);
امیدوارم مثل همیشه خروجی Ghidra بهخوبی قابل خوندن باشه. خلاصه کاری که این بخش از کد انجام میده اینه:
/var/tmp/dropbear/dropbearpwd
ایجاد یا باز میکنه"username:"
به همراه نام کاربریای که بهش داده شده توی فایل مینویسه"password:"
به همراه هش رمز عبور (به صورت ۱۶ بایت هگزادسیمال) داخل فایل مینویسهنکته جالب اینجاست که تابع هشکردن هم از همون کتابخونهی آشنا یعنی libcutil.so
میاد. اسمش هم cen_md5MakeDigest
هست.
اما حالا سؤال: این نام کاربری و رمز عبور از کجا میان؟
در واقع اینا از یه ساختار بزرگتر توی برنامه گرفته میشن.
روتر از یه شیء بزرگ استفاده میکنه که توی بخش BSS حافظه ذخیره شده. این شیء یه مدل دادهی سراسری (global) هست که برنامه توی اجرا بهش دسترسی داره و داخلش مینویسه.
undefined4 dm_setObj(uint param_1,ushort *param_2,ushort *param_3)
undefined4 dm_getObj(uint oid,ushort *out_buf,uint size,void *in_buf)
تو این سیستم، از عددهای OID برای اشاره به بخشهای خاصی از تنظیمات داخل حافظه استفاده میشه. مثلاً توی مورد ما، OID شماره ۸ مربوط به اطلاعات لاگین Dropbear هست (مثل نام کاربری و رمز)
iVar1 = dm_getObj(8, &source, 0x62, dropbear_obj_addr);
if (iVar1 != 0) {
cdbg_printf(8, "prepareDropbear", 0x11a, "get OID_USER_CFG error.\n");
}
setDropbearLogin(dropbear_obj_addr);
util_execSystem("prepareDropbear", "dropbearkey -t rsa -f %s",
"/var/tmp/dropbear/dropbear_rsa_host_key");
util_execSystem("prepareDropbear", "dropbearkey -t dss -f %s",
"/var/tmp/dropbear/dropbear_dss_host_key");
util_execSystem("prepareDropbear", "dropbear -p %d -r %s -d %s -A %s", 0x16,
"/var/tmp/dropbear/dropbear_rsa_host_key",
"/var/tmp/dropbear/dropbear_dss_host_key",
"/var/tmp/dropbear/dropbearpwd");
این کد از تابعی میاد که setDropbearLogin
رو فراخوانی میکنه. اون آدرس dropbear_obj_addr
رو از مدل دادههای سراسری (global data model) دریافت میکنه، که در شاخص OID شماره ۸ ذخیره شده. این بخش شامل هم نام کاربری و هم گذرواژهی ساده (plaintext) خواهد بود.
حالا، بعد از بررسی تمام فراخوانیهای dm_setObj (که زمانبر هم بود):
undefined dm_setObj()
dm_setObj XREF[135]: Entry Point(*),
rsl_sys_log:0002c470(c),
rsl_initPingWatchDogObj:0002e0f0
rsl_initL2tpConnPorttriggeringOb
rsl_initDmzHostCfgObj:0003c81c(c
کاملاً مشخص بود که شناسهی شیء (OID) شماره ۸ بهصورت مقدار ثابت در برنامه تعریف نشده، بلکه در واقع از طریق یک حلقه که روی مقادیر متغیر سراسریای به نام configXMLCtx
تکرار میشود، مقداردهی شده است.
با ترسیم نمودار فراخوانی (Call Graph) برای تابع dm_setObj
، و با در نظر گرفتن اینکه پارامتر OID از configXMLCtx
گرفته میشود، متوجه میشویم که اعتبارنامههای ما از فایل XML میآید که قبلاً آن را رمزگشایی کردهایم: default_config.xml
در خصوص این بخش، به دلیل اینکه روتر به دلیل جداسازی قطعات غیرفعال شده است، قطعاً نمیتوانم به آسیبپذیریها اطمینان داشته باشم و برای بررسی دقیقتر نیاز به آزمایش زنده است تا مشخص شود آیا پارامترها قابل تزریق هستند یا خیر. این موضوع به ویژه با توجه به استفاده گسترده از تابع system
مطرح میشود که معمولاً از آن به دلیل مسائل امنیتی پرهیز میشود.
thunk int system(char * __command)
Thunked-Function: <EXTERNAL>::system
assume t9 = 0xb8160
int v0:4 <RETURN>
char * a0:4 __command
system XREF[8]:
Entry Point(*),
rsl_sys_restoreDefaultCfg:0002af
rsl_sys_updateFirmware:0002b280(
util_execSystem:00092aac(c),
util_execSystem_long:00092d38(c),
oal_startUPnP:00098f18(c),
ipt_init:000a20e0(c),
000ecda4(*)
اما خود تابع system
مشکلی ایجاد نمیکند، بلکه تابع util_execSystem
است که به آن وابسته است و در تمام کد به کار رفته است. این تابع 495 ارجاع متقابل دارد، در توابعی مانند delStaticRoute
یا oal_ping
.
تابع از اینجا شروع میشود:
memset(command_line, 0, 0x200);
iVar1 = vsnprintf(command_line, 0x1ff, cmd, &local_res8);
cdbg_printf(8, "util_execSystem", 0x8b, "%s cmd is \"%s\"\n", caller_name, command_line);
if (0 < iVar1) {
iVar1 = 1;
do {
local_22c = system(command_line);
local_22c._1_1_ = (byte)(local_22c >> 8);
local_240 = local_22c & 0x7f;
if ((int)local_22c < 0) {
if (local_22c == 0xffffffff) {
cdbg_printf(8, "util_execSystem", 0x9b, "system fork failed.");
} else {
perror("util_execSystem call error:");
}
}
} while (waitpid);
}
بافر حاوی دستور صفر میشود، سپس از خط فرمانی که به عنوان پارامتر ارسال شده، با استفاده از تابع snprintf
پر میشود و مستقیماً از آنجا (بدون هیچگونه پاکسازی یا بررسی) به تابع system()
ارسال میشود.
این یک مثال خوب از یک وضعیت بالقوه آسیبپذیر است که میتواند منجر به تزریق کد شود:
void oal_ipt_addBridgeIsolationRules(undefined4 param_1){
util_execSystem("oal_ipt_addBridgeIsolationRules", "iptables -t filter -I BRIDGE_ISOLATION -i br+ -o %s -j DROP", param_1);}
متغیر param_1
میتواند حاوی نام یک رابط شبکه باشد که از طریق رابط وب وارد شده است. هنگامی که این متغیر به صورت مستقیم به رشته دستور افزوده میشود، ممکن است حاوی کد اجرایی بش (Bash) باشد که به وسیله آن میتوان دسترسی روت به دستگاه بدست آورد (برای مثال از طریق bind یا reverse shell).
من نتوانستم تمام استفادههای مختلف را بررسی کنم، اما جای تعجب است اگر هیچکدام از این موارد قابل تزریق از طریق رابط مدیریت وب نباشند.
oal_ipt_addBridgeIsolationRules on TP-Link TL-WR840N 6_EU_0.9.1_4.16 devices allows OS command injection because a raw string entered from the web interface (an IP address field) is used directly for a call to the system library function (for iptables). NOTE: oal_ipt_addBridgeIsolationRules is not the only function that calls util_execSystem.