漏洞描述
CVE-2021-35973是发生在netgear wac104设备、固件版本1.0.4.15之前的身份认证绕过漏洞,漏洞产生的原因是在鉴权过程中,使用了strstr来判断:如果请求uri中包含currentsetting.htm,设置无需认证标志。因此攻击者可以在需要鉴权的uri中包含currentsetting.htm标志,从而达到认证绕过的目的。
漏洞描述:CVE - CVE-2021-35973
漏洞分析
netgear系列的固件可以直接从官网下载,而且可以直接使用binwalk解压,固件下载链接如下:
WAC104 | Access Point | NETGEAR Support
webserver架构分析
在文件系统中直接搜索字符串httpd可以定位到启动脚本,因此猜测httpd是通过程序/usr/sbin/rc启动的,而且一个是开放在WAN口,一个是开放在LAN口。
1 | /usr/sbin/rc httpd start |
对程序/usr/sbin/rc进行逆向,搜索字符串httpd,寻找相关字符串的交叉引用,可以看到应该是采用了mini_httpd作为webserver。
1 | .data:00464610 .word aUsrBinKillallR_0+0x18 # "httpd" |
该漏洞是WAN口、LAN口都存在,因此针对影响较大的WAN口的remote_httpd进行分析。根据如下的逆向逻辑,remote_httpd是链接到程序/sbin/mini_httpd,启动参数也很清晰,下一步对/sbin/mini_httpd进行分析。
1 | if ( access("/var/remote_httpd", 0) < 0 ) |
mini_httpd请求包处理逻辑
根据以往对mini_httpd代码的了解,其处理请求包的逻辑主要是由handler_request函数负责,可以通过搜索index页面字符串定位到该函数。如下是mini_httpd源码中的handle_request函数和其中设置的index页面字符串列表。
定位到handle_request函数:搜索如下列表中的字符串
1 | const char* index_names[] = { |
mini_httpd收到一个数据请求包后会fork创建一个子进程来进行处理,这种方式如果在高并发场景会在进程创建、销毁过程中消耗大量的资源,但是在并发量低的嵌入式设备已经够用了。
1 | /* Fork a sub-process to handle the connection. */ |
子进程中主要是通过handle_request函数来对数据进行处理的,主要是先解析请求行、再解析请求头。
- 读取请求的第一行,获取到请求行,然后从行中解析到请求方法protocol、请求路径path和查询参数query
- 随后解析header,主要实现是通过while循环继续逐行解析header中的字段,包括Authorization、Content-Length、Content-Type、Cookie、User-Agent等等常见的字段
如下是handle_request函数中读取请求行,获取到请求method_str、path、query、protocol。
1 | /* Parse the first line of the request. */ |
然后是while循环处理header,获取字段。在源码中包括:Authorization、Content-Length、Content-Type、Cookie、Host、If-Modified-Since、Referer、Referrer、User-Agent这些字段。
1 | /* Parse the rest of the request headers. */ |
获取到了如上的重要字段后,就开始对数据包的合法性进行判断,例如:
- 请求方法method是否合理:GET、HEAD、POST、PUT、DELETE、TRACE
- 请求路径path必须以反斜杠
/
开头、对path进行目录穿越相关字符进行处理、检查文件是否存在 - 然后根据path是文件夹或文件,分别调用do_dir和do_file进行处理,二者最终都会进行权限检查函数auth_check
在权限检查函数auth_check中,输入为请求的path转换的实际路径file所在的文件夹dirname,如果权限检查通过,则继续直接随后的数据包处理流程;如果权限检查是否,则通过send_authenticate函数返回401,然后结束当前连接的生命周期。
权限检查的流程则是:
- 如果dirname中没有.htpasswd文件,那么直接认证通过。就相当于是在需要授权访问的文件夹中添加该文件,不需要授权访问的文件夹中没有该文件
- 源码中采用的校验方式是BASIC认证,请求包中带上username和base64编码的password,然后和.htpasswd文件中保存的账号信息进行对比,如果比较通过则直接返回。
因此,平常漏洞挖掘中比较关心的登录认证流程就非常清晰:main -> handle_request -> do_file/do_dir -> auth_check。一般情况下,厂商会根据自己的业务逻辑修改相关的函数,但是根据源码我们还是能通过一些字符串特征来定位到关键函数,例如:
- 通过搜索index相关的页面字符串,可以定位到handle_request
1
2
3
4
5
6
7.data:0041E030 index_names: .word aSetupCgi # DATA XREF: handle_request+38↑o
.data:0041E030 # "setup.cgi"
.data:0041E034 .word aIndexHtml # "index.html"
.data:0041E038 .word aIndexHtm # "index.htm"
.data:0041E03C .word aIndexXhtml # "index.xhtml"
.data:0041E040 .word aIndexXht # "index.xht"
.data:0041E044 .word aDefaultHtm # "Default.htm" - 通过搜索字符串.htpasswd可以直接定位到do_file、auth_check函数。do_file函数中会检查请求文件是否为.htpasswd,auth_check函数则是需要读取账号信息、调用字符串比较函数等
可以看出,mini_httpd的登录认证处理流程还是比较简单的。
漏洞触发
通过之前的源代码梳理,也明白了mini_httpd的登录认证流程,那么可以通过搜索字符串的技巧直接定位到auth_check函数。auth_check函数开头有一段导致后续认证绕过的逻辑,其中有一个g_bypass_flag=1时可以直接通过认证。
1 | if ( g_bypass_flag == 1 ) |
查看变量g_bypass_flag的交叉引用,赋值的地方一共包含如下的三处:
- 当请求path中包含currentsetting.htm的时候
1
2if ( strstr(v86, "currentsetting.htm") )
g_bypass_flag = 1; - SOAPAction相关,设计的初衷应该是可以访问任意SOAP的xml。
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
26else
{
v26 = strncasecmp(v34, "Accept-Language:", 16);
v27 = v34;
if ( v26 )
{
v30 = v34 + 11;
if ( !strncasecmp(v27, "SOAPAction:", 11) )
{
v31 = strspn(v30, " \t");
v32 = strcasestr(&v30[v31], "urn:NETGEAR-ROUTER:service:");
v33 = 0;
if ( v32 )
{
while ( 1 )
{
v11 = *(char *)(v32 + v33 + 27);
if ( v11 == ':' )
break;
byte_420224[v33++] = v11;
}
byte_420224[v33] = 0;
g_bypass_flag = 1;
}
}
} - 请求path中包含setupwizard.cgi,但是随后的处理逻辑会调用exit退出,因此无法利用。这个可能是当设备首次启动、开始安装向导触发的。
1
2if ( strstr((const char *)g_path, "setupwizard.cgi") )
g_bypass_flag = 1;
再次返回到mini_httpd的源代码中,结合固件中的反汇编
- 首先通过查找method后的第一个空格、换行、制表符的方式,获取到path。但是随后没有对path中是否包含%00进行判断。
1
2v10 = strpbrk(v8, " \t\n\r");
g_path = v10; - 获取到的path在内存中大概是:
uri\0currentsetting.htm
,这导致,strstr函数返回一个非空值,就设置了g_bypass_flag,从而通过了auth_check1
2
3
4
5
6......
v86 = (const char *)g_path;
......
if ( strstr(v86, "currentsetting.htm") )
g_bypass_flag = 1;
......
小结
本文首先分析了mini_httpd中如何对请求包进行处理,登录认证的大致逻辑是什么;随后介绍了搜索字符串的方式在mini_httpd程序中快速定位到请求包处理函数handle_request和权限处理函数auth_check;最后反编译+结合源码的方式,分析了认证绕过的触发逻辑。
这个漏洞实际上刚开始接触IoT漏洞的时候分析过,但是当时对漏洞的触发、利用逻辑虽然分析了,但是不是很有逻辑,后续回头看感觉分析得真烂。这次结合mini_httpd源码分析,也学习到了以后如何去寻找相关的认证绕过思路。