路由器安全导航:规避常见漏洞与配置陷阱
引言
Wi-Fi 路由器一直是攻击者的诱人目标。一旦被攻破,攻击者可能获得受害者内部网络或敏感数据的访问权限。此外,攻击者不断将新的路由器漏洞纳入其武器库,用于僵尸网络(如 Mirai 僵尸网络)的趋势仍在持续。
消费级设备由于存在许多安全缺陷,对攻击者尤其具有吸引力。安全性较低的设备通常包含多个易于利用的漏洞,使其成为易受攻击的目标。另一方面,更安全的设备提供了宝贵的见解和教训。
本文从技术角度概述了路由器中的漏洞,旨在提高安全团队和开发人员的意识,并提供避免此类漏洞的建议。我们还将回顾影响不同厂商设备的过往漏洞,从中吸取教训。尽管以下内容聚焦于路由器,但所学到的经验同样适用于其他网络设备。
免责声明:本文未涵盖所有漏洞类别。
攻击面
路由器的攻击面可能比预期的更大。这是因为其上运行着各种服务。每个从外部主机(无论是局域网(LAN)还是广域网(WAN)接口)接收请求的服务都呈现出一个攻击面,因为畸形请求可能执行服务中的易受攻击代码路径。下面,我们简要探讨路由器上处理外部请求的常见服务。
管理面板
网络设备的管理面板托管了大量可由设备所有者/管理员更改的配置。每个输入字段都是一个攻击面,因为它们由设备上运行的 Web 服务处理。例如,可能有一个用于阻止往返特定 IP 地址流量的输入字段。Web 服务处理此请求的方式可能易受命令注入攻击。简而言之,管理面板为攻击者提供了巨大的攻击面。
有人可能认为这不是一个非常令人担忧的攻击面,因为攻击者需要先通过身份验证才能访问此攻击面。这是事实,因此许多 CVE 以“已认证”开头,例如“已认证命令注入”,这表明利用该漏洞需要身份验证。然而,攻击者可能会发现身份验证绕过,或者管理面板上可能有一些不验证用户是否已认证的端点。身份验证绕过可以与“已认证漏洞”链式利用;不需要身份验证的端点上的漏洞被归类为“未认证”,例如“未认证缓冲区溢出”。
其他服务
除了管理面板,路由器通常还运行其他处理各种协议(如 FTP、Telnet、动态主机配置协议(DHCP)或通用即插即用(UPnP))请求的服务。这些服务也在路由器上呈现攻击面。
某些服务(如 DHCP 或 UPnP)不需要身份验证。此外,在某些设备上,某些服务可从 WAN 接口访问,这意味着不在本地网络上的远程攻击者也可以访问这些服务并利用其上的任何漏洞。对于如此可访问的服务,确保其安全性尤为重要。
不良配置
首先,我们讨论一些路由器上存在的配置错误。我们将讨论的那些都遵循访问主题,即:
- 通过硬编码凭证访问
- 从远程网络访问服务
- 运行服务或普通用户访问 root 权限
硬编码凭证
设备的固件包含设备操作所需的程序和配置文件的存档。相同的固件分发并安装在同一型号的所有设备上。因此,如果设备上运行的任何服务(例如 FTP 或 Telnet)的凭证在固件中硬编码,则每个设备都将使用相同的凭证。
影响
可以访问固件的攻击者(通常可以从供应商网站下载)可以提取文件并检查它们以获取凭证。如果此类服务暴露在互联网上,世界任何地方的任何人都可能能够在设备上获得 shell(Telnet)、访问敏感数据(FTP)或操纵设备的设置(httpd),具体取决于哪个服务易受攻击。
在危险性较低的情况下,此类易受攻击的服务可能不会暴露在互联网上,而只是暴露在 LAN 上。这不太狡猾,但与设备在同一网络中的任何人都能够访问这些暴露的服务。对于路由器而言,连接到其 Wi-Fi 网络的任何人都可以滥用暴露服务上的硬编码凭证。对于家庭网络中的路由器来说,这 arguably 是可以接受的,因为知道 Wi-Fi 密码的每个人都是家庭成员或可信朋友。然而,对于咖啡馆或餐厅等公共场所的路由器来说,这是不稳定的。
示例
下面,我们分享一些过去由他人报告的路由器上硬编码凭证的示例。
CVE-2022-46637 报告称,ProLink PLDT 家庭光纤 PRS1841 U V2 路由器包含硬编码的 Telnet 和 FTP 凭证。此外,这些服务暴露在互联网上,可以通过 Shodan 找到易受攻击的设备。
CVE-2020-29322 详细说明,版本为 1.07 的 D-Link 路由器 DIR-880L 的 telnet 服务使用硬编码的用户名和密码启动。从公告中,无法确定该服务是否总是在启动时启动,以及该服务是否暴露给 WAN/互联网。然而,如果两者都为真,这将作为任何攻击者获得设备 shell 的简单后门。
CVE-2019-14919 是关于 Billion Smart Energy Router (SG600R2. Firmw v3.02.rc6) 在 Telnet 使用的登录管理二进制文件中包含硬编码密码。文章中未提及该服务是仅暴露给 WAN 还是 LAN。除此之外,与硬编码凭证无关,在管理面板中还发现了一个以 root 权限运行的 Web shell。
建议
我们建议所有需要身份验证的服务(如 FTP 或 Telnet)在管理员通过管理面板为其设置强密码之前被禁用。
至于管理面板密码,常见做法是在工厂为每个设备随机生成密码,并将密码作为贴纸贴在设备底部或作为便条放在包装中。首次登录时,管理员将被要求更改密码,然后才能使用管理面板。
暴露给互联网的服务
如上所述,攻击者可以使用硬编码凭证访问暴露给互联网的服务,而不仅仅是从 LAN 可访问。但是,如果没有硬编码凭证,将管理面板或 FTP 服务器等服务暴露给互联网是否可以接受?
路由器上可能运行许多服务,例如 FTP、SSH、UPnP、管理面板、VPN。尽管凭证可能不是硬编码的,但这些服务中可能存在不需要身份验证并可能导致设备上 RCE 的漏洞。因此,除非必要,否则不将所有服务暴露给互联网仍然更安全。
建议
理想情况下,FTP、Telnet 或 SSH 等服务不应默认开启,而应仅在设备管理员通过管理面板请求时开启。如果管理员想要启用服务,他应该被要求指定服务是暴露给互联网还是仅暴露给 LAN。设备不应在管理员不知情或不同意的情况下将这些服务暴露给互联网。更理想的是,管理员在将这些服务暴露给互联网之前完全了解此类风险并愿意承担它们。
以 root 身份运行的服务
在 PC 上,将普通用户与超级用户(root/管理员)分开是常见的。然而,对于许多消费级路由器来说,情况并非如此。其中许多只有 root 用户,每个服务都以 root 身份运行。在此类设备上,FTP/Telnet/SSH/管理面板 Web 服务以 root 身份运行。因此,当攻击者在服务上获得 RCE 时,他也就获得了具有 root 权限的 shell。当服务被利用时,没有权限分离来防止整个设备被攻破。
我们在下面列出一些示例:
- MI AIoT Router AC2350
- D-Link DIR-890L
- NETGEAR Nighthawk RAX30
上面的文章显示,无需权限提升漏洞即可获得 root shell,这意味着没有权限分离。
建议
每个进程/服务应以不同的用户身份运行,应用最小权限原则。例如,httpd 服务以 web 用户身份运行;upnpd 服务以 upnp 用户身份运行;ftp 服务以 ftp 用户身份运行,依此类推。在此配置下,当服务被利用时,攻击者无法在没有权限提升漏洞的情况下危害其他服务或整个设备。
无密码 sudo?
在我们检查的一些路由器上,它们的管理面板 Web 服务不以 root 身份运行,而是以普通用户身份运行。这很好。然而,为了执行一些系统级操作,他们希望使用需要 root 权限的 shell 命令。例如,iptables 命令。
不良示例
考虑管理面板支持使用 iptables(需要超级用户权限)阻止往返特定 IP 地址流量的场景。为了实现这一点,开发人员可能倾向于引入一个 SUID 二进制文件,其工作方式类似于 sudo 但不需要密码。换句话说,一个 SUID 二进制文件,它将命令字符串作为参数,并以 root 权限将该命令字符串作为 shell 命令运行。一个简单的示例如下(在以下示例中我们将其称为 custom_sudo):
|
|
然后,开发人员可以使用 custom_sudo 运行 iptables 命令,如下所示。
|
|
显然,拥有这样的程序违背了拥有不同用户的目的,当所有用户都可以使用此程序以 root 身份运行任何命令时,违反了最小权限原则。
建议
不建议通过向此类 SUID 程序(如 sudo 或 custom_sudo)提供整个命令字符串来执行需要 root 权限的操作。相反,我们建议拥有一个 SUID 程序,它仅将必要的值作为参数接收,然后在执行所需操作时使用它们。
对于上面阻止 IP 地址的示例,我们建议有一个名为 block_ip_addr 的 SUID 程序,它仅将 IP 地址作为参数接收,然后在内部使用给定的 IP 地址字符串执行 iptables 操作。例如:
|
|
然后,block_ip_addr 的实现可以如下:
|
|
如果对所有需要 root 权限的命令都这样做,则不再需要 custom_sudo 程序,可以将其完全从系统中删除。
但是,如果开发人员坚持使用此类 custom_sudo 程序,请至少验证要执行的程序是否在允许的程序列表中。例如:
|
|
请注意,在上面的检查中,/bin/iptables 以空格字符结尾。这是为了确保命令字符串确实运行 /bin/iptables 程序,而不是类似 iptablesfake 的东西。此外,使用绝对路径(例如 /bin/iptables)而不仅仅是程序名称(例如 iptables),以防止调用错误的程序,因为程序路径是根据 PATH 环境变量确定的。例如,如果 PATH 变量配置为如此,则可能执行攻击者创建的 /home/user/iptables 而不是 /bin/iptables。
此外,确保没有可被利用来绕过此类检查的命令注入漏洞。这可以通过使用 execve 而不是 system 运行所需命令来实现。
简而言之,在考虑需要超级用户权限的操作的实现时,应遵循最小权限原则,以确保所选实现不会向用户提供不必要的更多权限。
总结
所有上述错误配置都有直接的解决方案。然而,这些解决方案的实施需要额外的开发和测试时间。对于消费者来说,希望所有路由器供应商将这些改进视为“必须拥有”而不仅仅是“最好拥有”。
漏洞类别
在本节中,我们将讨论影响路由器上运行的服务的以下漏洞。
- 身份验证绕过
- 命令注入
- 缓冲区溢出
- 格式化字符串漏洞
命令注入和缓冲区溢出是路由器中存在导致 RCE 的两个主要漏洞类别。格式化字符串漏洞也可能导致 RCE,但近年来非常罕见。
身份验证绕过
如果服务需要身份验证,则其攻击面会大大减少。通常,未经认证的客户端只能发送与身份验证相关的请求,或查询有关服务或设备的一些信息。因此,身份验证绕过漏洞对攻击者来说很有价值,因为它打开了整个剩余攻击面。
除此之外,即使没有 RCE,非管理员仍然可以执行身份验证绕过来披露和控制管理面板上的敏感设置。对其他需要身份验证的服务(如 FTP、SSH 或 Telnet)的身份验证绕过也可能导致 shell 访问或敏感信息泄露。因此,不应轻视身份验证绕过。
示例
在以下子节中,我们分享过去报告的路由器上身份验证绕过错误的示例。
CVE-2021-32030:身份验证逻辑中的错误
CVE-2021-32030 报告了 ASUS GT-AC2900 路由器上的身份验证绕过。简而言之,该漏洞是由于身份验证流程中的错误引起的,简化如下:
- 客户端应提供标头字段 asus_token 以验证进入管理面板。
- asus_token 与从 *nvram 检索的 ifttt_token 值进行比较。
- 如果 nvram 不包含 ifttt_token,则返回空字符串,即空字节。
- 如果 asus_token 是空字节,则比较成功,客户端成功通过身份验证。
*nvram 代表非易失性 RAM。它被许多路由器用于存储应在设备重启后保留的配置值。因此称为非易失性,因为内容持久存在,不像普通 RAM,其所有内容在设备关闭时将被清除。
建议
在这种情况下,漏洞是由程序员错误引起的,其中意外的边缘情况输入破坏了身份验证逻辑。相关函数应首先确保 ifttt_token 不是空字符串,然后再将其与客户端提供的 asus-token 进行比较。为了格外小心,开发人员还可以添加额外检查以确保 asus-token 不是空字符串,以防将来可能与从 nvram 检索的另一个令牌进行比较。
CVE-2020-8864:身份验证逻辑中的错误
CVE-2020-8864 报告了 D-Link DIR-882、DIR-878 和 DIR-867 路由器上的身份验证绕过。利用方式与上面 ASUS 路由器中的相同,尽管身份验证逻辑的实现不同,简化如下:
- 客户端提供 LoginPassword 进行身份验证。
- 使用 strncmp 将客户端提供的密码与正确密码进行比较,如下所示:
1
strncmp(db_password, attacker_provided_password, strlen(attacker_provided_password));
- 如果攻击者提交带有空密码的登录请求,strncmp 的第三个参数(长度)将为 0,并返回 0,表示比较的两个字符串相等,这是正确的,因为两个字符串的前 0 个字符相同。结果,身份验证成功。
建议
同样,漏洞是由程序员错误引起的。此处的修复是通过传递 strlen(db_password) 作为第三个参数,而不是 strlen(attacker_provided_password)。
CVE-2020-8863:预期密码值由攻击者控制
CVE-2020-8863 报告了 D-Link DIR-882、DIR-878 和 DIR-867 路由器上的另一个身份验证绕过。
上面关于 CVE-2020-8864 的子节通过省略身份验证流程的一些细节进行了简化。在这里,我们详细描述它,以便我们以后可以准确描述此身份验证绕过。
这些路由器使用家庭网络管理协议(HNAP),一种基于 SOAP 的协议,用于客户端到 Web 服务器的管理面板请求。身份验证过程如下:
- 客户端发送请求消息并从服务器获取身份验证质询。
- 服务器使用值 Challenge 和 PublicKey 响应请求。
- 客户端应将 PublicKey 与密码组合以创建 PrivateKey。然后,使用 PrivateKey 和 Challenge 生成质询响应,该响应将作为 LoginPassword 提交给服务器。
- 服务器将执行相同的计算,如果 LoginPassword 匹配,则意味着客户端知道正确的密码,身份验证成功。
以上描述取自 ZDI 博客上的这篇文章。查看更多详细信息和代码示例。
在 Web 服务器二进制文件中,发现存在检查登录请求中 PrivateLogin 字段的代码路径。如下所示:
|
|
如果提交的 PrivateLogin 字段包含“Username”,则提交的 Username 值用作密码([2])以生成预期的 LoginPassword 值(质询响应),而不是通过调用 GetPassword([1])使用用户的实际密码。简单来说,客户端可以控制用于生成预期质询响应的密码,从而绕过身份验证。
不清楚 PrivateLogin 字段的目的是什么。由于 HNAP 是一种过时的专有协议,网上没有文档,我们很难确定此字段的原始用途。
作为一个要点,确保在实现身份验证协议时,密码等秘密不应与客户端提交的值混合。
总结
管理面板上的身份验证绕过漏洞源于程序员错误,因为他们未能考虑可能破坏身份验证逻辑的边缘情况输入(如空密码)。除此之外,还可能存在协议的有缺陷实现。应特别关注审查身份验证例程的实现,以捕获由于此类错误导致的意外绕过。
命令注入
命令注入是路由器或其他网络和 IoT 设备中常见的漏洞。在本节中,我们讨论易受攻击的代码模式、此类漏洞普遍存在的原因、防止它们的指南,以及过去在各种路由器中的一些示例。
根本原因
命令注入是可能的,因为包含未清理用户输入的命令字符串通过 C 中的 system 或 popen、Python 中的 os.system、Lua 中的 os.execute 或 io.popen 作为 shell 命令执行。例如,
|
|
|
|
在上面的示例中,输入 IP 地址如 127.0.0.1; reboot 将导致命令字符串 ping 127.0.0.1; reboot,它逃逸了预期的 ping 命令并执行任意命令,如