IoT 设备中身份验证绕过的一些漏洞(2)

Catalpa 网络安全爱好者

CVE-2021-32030 & CVE-2021-20090

CVE-2021-32030

2021 年 5 月,来自 Atredis 的安全研究者披露了一个影响几乎所有 ASUS 路由器的身份验证绕过漏洞,利用该漏洞,未经授权的攻击者可以直接访问到后台管理接口,由于 ASUS 路由器在后台提供了开启 ssh 的功能,攻击者只需修改管理员密码并开启 ssh 即可获取 root shell。

漏洞分析

固件下载链接:https://dlcdnets.asus.com/pub/ASUS/wireless/RT-AX56U/FW_RT_AX56U_30043848253.zip

我们以 RT-AX56U 为例,使用 binwalk 对固件解包(解包之后有可能只会得到一个 100000.ubi 文件,可以使用 ubi_reader 继续解包)。解包后在 /usr/sbin/ 目录下找到一个叫做 httpd 的程序,此程序用于处理用户发送的 HTTP 请求。

我们用 IDA 分析程序,搜索 httpd_handle_request 字符串可找到处理请求的入口位置,另外,由于 ASUS 路由器采用了一些开源组件,根据 GPL 规则,ASUS 在(数个版本之前)其官网发布了路由器的源代码,并且衍生出梅林等优秀的官改版固件,我们可以在 github 上找到 merlin 固件项目,利用源代码 辅助分析。

httpd_handle_request 函数逻辑简述如下:

首先对请求进行简单解析,利用 strncasecmp 等字符串操作函数从用户请求中匹配出一些结构,并将这些数据赋值到对应的变量。

1
2
3
4
5
6
7
8
9
10
11
if ( !strncasecmp(v4, "Referer:", 8u) )
{
stringp = v4 + 8;
v87 = &stringp[strspn(stringp, " \t")];
stringp = v87;
v20 = strlen(v87);
v17 = v87;
v18 = v20 + 1;
LABEL_40:
v4 = &v17[v18];
}

然后过滤 URL,防止出现路径穿越。

1
2
3
4
5
6
7
8
9
10
11
12
if ( v28 == 47
|| !strcmp(v26, "..")
|| !strncmp(v26, "../", 3u)
|| strstr(v26, "/../")
|| (v30 = strcmp(&v26[v29 - 3], "/..")) == 0 )
{
v2 = "Illegal filename.";
v3 = "Bad Request";
LABEL_60:
v24 = 400;
return sub_5ED20(v24, v3, v2);
}

之后根据用户访问的 URL 和 Method 执行不同逻辑,但所有需要身份验证的接口都要经过函数 auth_check(sub_5B560),此函数从 Cookie 中获取 asus_token 参数的值,然后带入 sub_59588 进行检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int __fastcall sub_59588(const char *a1)
{
const char *v2; // r0
const char *v3; // r0
int result; // r0

v2 = nvram_get_1("ifttt_token");
if ( !strcmp(a1, v2) )
{
if ( isFileExist("/tmp/IFTTT_ALEXA") > 0 )
Debug2File("/tmp/IFTTT_ALEXA.log", "[%s:(%d)][HTTPD] IFTTT/ALEXA long token success.\n", "check_ifttt_token", 761);
result = 1;
}
else
{
if ( isFileExist("/tmp/IFTTT_ALEXA") > 0 )
Debug2File("/tmp/IFTTT_ALEXA.log", "[%s:(%d)][HTTPD] IFTTT/ALEXA long token fail.\n", "check_ifttt_token", 767);
if ( isFileExist("/tmp/IFTTT_ALEXA") > 0 )
Debug2File(
"/tmp/IFTTT_ALEXA.log",
"[%s:(%d)][HTTPD] IFTTT/ALEXA long token is %s.\n",
"check_ifttt_token",
768,
a1);
if ( isFileExist("/tmp/IFTTT_ALEXA") > 0 )
{
v3 = nvram_get_1("ifttt_token");
Debug2File("/tmp/IFTTT_ALEXA.log", "[%s:(%d)][HTTPD] httpd long token is %s.\n", "check_ifttt_token", 769, v3);
}
result = 0;
}
return result;
}

我们注意到函数先从配置文件中获取 ifttt_token 参数的值,然后将 asus_token 和它比较,如果两者相等,则输出

1
IFTTT/ALEXA long token success.

同时返回 1,表示身份验证通过。

但是在设备的默认配置下,IFTTT 功能没有开启,这导致 ifttt_token 默认值为空, 此时如果用户传入的 asus_token 也是空值,则 strcmp 会返回 0 表示比较成功,这样就能绕过后续的检查,实现身份验证绕过。

新版 auth_check 函数将代码修改为

1
2
3
4
5
6
7
8
9
10
if ( *a1 && *nvram_safe_get_0("ifttt_token") && (v2 = nvram_safe_get_0("ifttt_token"), !strcmp(a1, v2)) )
{
if ( isFileExist("/tmp/IFTTT_ALEXA") > 0 )
Debug2File(
"/tmp/IFTTT_ALEXA.log",
"[%s:(%d)][HTTPD] IFTTT/ALEXA long token success.\n",
"check_ifttt_token",
1020);
result = 1;
}

首先判断 asus_token 是否为空,避免用户传入空值的情况出现,从而修复了此漏洞。

参考链接

https://www.atredis.com/blog/2021/4/30/asus-authentication-bypass

CVE-2021-20090

2021 年 4 月,Tenable 安全研究者披露了影响 Arcadyan 系列网络设备的一些漏洞,后续经确认,有多个厂商采用了 Arcadyan 开发的系统套件,从而遭受这些漏洞的影响。

漏洞分析

根据披露信息,Arcadyan 提供的组件被多个网络设备厂商使用,我们以 ASUS DSL-AC88U 为例进行分析。

固件版本:1.10.05_Build502,下载后 binwalk 即可解包。

关键文件:/usr/sbin/httpd

IDA 加载分析,首先可以定位到名为 process_request 的函数,传入参数为解析后的 http 请求结构体,此函数关键代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
url_decode(&req->url);
printf("[%s] url=[%s], args=[%s], method=[%s]\n", "process_request", &req->url, req->args, req->method);

//...

req->is_url_valid = sub_DEB0(&req->url);
v7 = *(off_54FAC[0] + 5);
if ( (!v7 || v7(req) != 2) && (req->is_url_valid || !check_auth_0(&req->url, 0, req)) )
{
req->dword798C = 0;
v8 = req->method;
if ( !strcmp(v8, "HEAD") )
{
req->dword798C = 1;
if ( req->dword7988 )
{
req->dword798C = 0;
LOG(req, 400, "Invalid HTTP/0.9 method.");
return;
}
goto LABEL_19;
}
if ( !strcmp(v8, "GET") )
{
LABEL_19:
process_get(req);
return;
}
if ( !strcmp(v8, "POST") )
process_post(req);
else
LOG(req, 400, "Invalid or unsupported method.");
}

首先调用 url_decode 函数对 url 进行解码,然后调用 sub_DEB0 函数对 url 进行判断,关键代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __fastcall sub_DEB0(const char *url)
{
const char *v2; // r4
char **i; // r5
size_t v5; // r0
const char *v6; // t1

v2 = allow_urls[0];
if ( !allow_urls[0] )
return 0;
for ( i = allow_urls; ; ++i )
{
v5 = strlen(v2);
if ( !strncasecmp(url, v2, v5) )
break;
v6 = i[1];
v2 = v6;
if ( !v6 )
return 0;
}
return 1;
}

此函数从一个 allow_urls 列表中获取值,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/images/
/lang/
/js/
/css/
/login.htm
/loginpserr.htm
/relogin.htm
/login.cgi
/cgi/cgi_tel_dect_firmware_upgrade_state.js
/wz_setpwd.cgi
/error_page.htm
/cgi/cgi_clients.js
/blocking.htm
/get_ver.htm

先获取 allow_urls 一项的长度,然后用 strncasecmp 函数和用户传入的 URL 比较,如果相等则返回 1,此时 req->is_url_valid 为 1。

之后回到 process_request 函数,判断 req->is_url_valid 的值,如果它等于 1,则不会执行 check_auth_0 函数,相当于当用户访问静态资源时,自动跳过身份验证。

随后判断 method,如果是 POST,则执行 process_post。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void __fastcall process_post(struct_req *req)
{
char *url; // r4

url = &req->url;
printf("[%s] path: %s, args: %s\n", "process_post", &req->url, req->args);
switch ( sub_DF50(url) )
{
case -1:
LOG(req, 302, url);
break;
case 0:
if ( !sub_14F9C(req) )
LOG(req, 501, "POST to non-script");
break;
case 2:
exec_cgi_script(req);
break;
case 3:
LOG(req, 400, url);
break;
default:
return;
}
}

将 URL 传入 sub_DF50 函数,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int __fastcall sub_DF50(const char *url)
{
int v2; // r3
bool v3; // zf
int i; // r4
int v5; // r7
size_t v6; // r6

sub_167A8(url);
v2 = *url;
v3 = v2 == '/';
if ( v2 != '/' )
v3 = v2 == 0;
if ( !v3 )
return 3;
for ( i = 0; i != 528; i += 66 )
{
if ( sub_17584((maybe_post_handlers + i)) )
break;
v5 = maybe_post_handlers + i;
v6 = strlen((maybe_post_handlers + i));
if ( !strncmp(url, v5, v6) && (*(v5 + v6 - 1) == '/' || v6 == strlen(url) || url[v6] == '/') )
{
sub_166D8(v6, url, (v5 + 32));
return *(maybe_post_handlers + i + 64);
}
}
sub_166D8(0, url, dword_61648);
return 0;
}

再将 URL 传入 sub_167A8,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
int __fastcall sub_167A8(int url)
{
int j; // r2
int k; // r3
unsigned __int8 *v3; // r12
int v4; // r2
bool v5; // zf
int v6; // r5
int v7; // r1
int v8; // r2
int v9; // t1

j = 0;
k = 0;
while ( *(url + k) )
{
if ( double_dot[j] == *(url + k) ) // 判断当前位置 url 字符是否为 .
{
if ( double_dot[++j] )
{
++k;
}
else
{
v3 = (url + k + 1); // 指向 .. 下一个字节
v4 = *v3; // 获取字节
v5 = v4 == '/'; // 判断是否为 /
if ( v4 != '/' )
v5 = v4 == 0;
if ( v5 ) // 如果是
{
if ( k <= 3 || *(url + k - 2) == '/' )// 如果 url 长度小于等于3,或者存在 /..
{
v6 = k + 1; // .. 下一个位置
k -= 3;
if ( k >= 0 )
{
for ( ; k && *(url + k) != '/'; --k )// 倒退,直到找到一个 /
;
}
else
{
k = 0;
}
v7 = url + k; // 开头
v8 = *(url + v6); // .. 下一个字节
*(url + k) = v8; // 放到开头
if ( v8 )
{
do
{
v9 = *++v3; // 把后续字节拷贝到 url
*++v7 = v9;
}
while ( v9 );
j = 0;
}
else
{
j = 0;
}
}
else
{
j = 0;
}
}
else
{
j = 0;
}
}
}
else if ( j <= 0 ) // 如果不是 . (且没遇到过 .)就递增到 url 下一个字节
{
++k;
}
else
{
k += 1 - j;
j = 0;
}
}
return url;
}

简单来讲,此函数会将形如 /aaa/../bbb 转换成 /bbb。

转换之后,回到 sub_DF50 函数,将 URL 和 maybe_post_handlers 列表中定义的 handler 比较,如果比较成功,就回到 process_post 用 exec_cgi_script 执行对应的 handler 函数。

于是我们可以构造如 /images/../apply_abstract.cgi 类似的请求,程序先匹配到 /images,绕过登录,然后替换 /../ 导致后续访问的是 /apply_abstract.cgi 接口,实现身份验证绕过。

参考链接

https://zh-cn.tenable.com/security/research/tra-2021-13?tns_redirect=true

总结

身份验证绕过是一类影响比较严重的漏洞,通过此类漏洞可允许攻击者访问到后台更多敏感接口,通过组合利用漏洞可实现对设备的完全控制,从我们复现过的漏洞来看,身份验证绕过基本有以下几类

  1. 设备的登录功能对于用户名/密码处理不恰当,攻击者可通过传入空值/硬编码值/可本地计算的值来通过验证流程。
  2. 设备的会话管理存在问题,攻击者通过控制 Cookie 值绕过会话检查。
  3. 设备对于用户访问的资源路径处理不恰当,攻击者通过路径穿越/插入特殊字符串等通过身份验证,但实际访问到敏感接口。
  4. 设备区分了 PC 和 app 请求,app 请求可能存在弱校验。
  5. 设备直接暴露了敏感接口,攻击者通过访问这些接口得到认证信息。
  • Title: IoT 设备中身份验证绕过的一些漏洞(2)
  • Author: Catalpa
  • Created at : 2021-08-13 00:00:00
  • Updated at : 2024-10-17 08:23:54
  • Link: https://wzt.ac.cn/2021/08/13/bypass_auth2/
  • License: This work is licensed under CC BY-NC-SA 4.0.
On this page
IoT 设备中身份验证绕过的一些漏洞(2)