[备份]浅谈Log4j2之2.15.0版本RCE

0x00 介绍

CVE-2021-45046发布2天后,和官方对线成功,将我名字4ra1n加入了credit

在第3天在官方安全 页面 发现该漏洞从DoS升级到RCE并提高到9分

经过两天的分析和调试,我在Windows上复现失败,但在MacOS上确实可以成功

(由于家境贫寒买不起Mac所以拜托了天下大木头师傅协助,成功RCE)

 

首先说明一个很多人都在关心的问题:只要不开lookup就不存在漏洞

2.15.0版本中,无论DoS还是RCE都需要开启lookup功能,如果没有特殊配置且不使用ThreadContext等功能的情况下,是不存在漏洞的。但为了进一步的安全最好升级到最新版(目前是2.17.0版本)

 

回顾核心方法,也是本文重点

 

2.14.1版本RCELDAP Server 这样写,注释写了防御方式。简单分析可以看出,假设真的有手段能够绕过了localhost检测,在当前的LDAP Server中也无法继续加载远程对象

 

所以需要想出新的方式来触发,而不是继续利用javaFactory属性,这将在后文中写到

 

0x01 解析绕过

尝试一些URI的绕过:如何让URI.getHost获得到127.0.0.1

ldap://127.0.0.1:1389/badClassName这种方式获取到的一定是127.0.0.1。虽然可以绕过检测,但这里的URI放入LDAP中也只能解析到127.0.0.1,没有操作空间。于是想到,能否让URI.getHost合法(locaohost127.0.0.1)但实际上LDAP Client可能会把输入解析到黑客搭建的LDAP Server的IP呢?

以下内容就是围绕这个思路展开:目标域名是4ra1n.love

参考orange大佬在Black Hat 2017分享的PPT

看到其中的authority的解释,想到能否用@符号做一些事情

可绕过但不可能被解析到4ra1n.love域名

 

看到PPT中另一处

编写对应的代码测试,发现#号也可以做一些事情

 

外国佬传出的POC如下,与我的猜测不谋而合

 

参考上方第三种Payload

 

这里的Host127.0.0.1#.4ra1n.love

如果为4ra1n.love域名开启泛域名解析,那么127.0.0.1#是否会被当成一个子域名,从而访问到真正的目标IP

(泛域名解析方式的绕过是我的一种猜测,感觉最贴近真实,但不确定这是真正的原理)

 

0x02 LDAP分析

以上的PayloadWindows中的测试会报错,LDAP Client初始化时候出现相同的异常:UnknownHostException

尝试使用Wireshark抓包发现没有dns相关信息,也就是说这个异常是发请求之前报出的

 

this.context.getAttributes(name)一路跟到LDAP Client初始化

 

跟到最底层,发现只是一个普通的Socket方法:其中的var1var2正是hostport

 

Socket源码

 

参考InetSocketAddress类构造方法,找到了抛出异常的根源

 

找到底层方法,那么可以尝试造一些Payload测试报错情况

 

继续从InetAddress.getByName跟下去,会到达一处native方法

 

由于Wireshark没有抓到DNS相关的包,在这一系列的流程也没有看到处理特殊符号的代码

而国外佬在有#号的情况下能够不报错,所以我猜测是这个native方法的原因,报错的底层是操作系统和JVM决定的

在官方安全页面写着只有在MacOS中才可以RCE,后来经过测试的确只能在MacOSRCE

 

0x03 RCE分析

假设127.0.0.1#.4ra1n.love可以正常拿到IP地址,接下来需要解决RCE的问题

在文章一开始就有分析到,在2.15.0中禁了LDAPjavaFactory属性导致无法加载远程类,那么还能有什么思路呢

回顾0x00核心代码中的一个if分支

 

分析下lookup底层的LdapCtx.c_lookup方法

 

其中有这样一句针对javaClassName的校验,但仅仅是非空校验

 

跟入decodeObject方法

 

跟入deserializeObject方法,没有什么限制条件

 

可以看到整个过程中没有对javaClassNamejavaSerializedData验证

也就是说核心代码中类名白名单对javaClassName的限制没有用处,可以轻松绕过

然后将javaSerializedData属性设置为gadget的序列化数据,即可在readObject中触发RCE

(其实这个过程正是JDNI绕高版本JDK的一种方式)

 

0x04 RCE过程

上文分析出了一种RCE的方式,但没有真正的实践

LDAP Server中设置javaClassName为基本类型,然后设置javaSerializedDataPayload

这里的java.lang.String可以绕过类目白名单的检测

 

Payload选用了CC6

 

我将写好的LDAP Server部署到远程服务器上(该工具以后分享,最近不太方便)

 

本地引入Log4j2 2.15.0CC依赖

 

配置开启lookup功能

 

打日志

 

由于我的环境是Windows会在处理包含#号的Host时报错,所以在this.context.getAttributes(name);下断点并去掉#

 

由于4ra1n.love域名开启了泛域名解析,所以127.0.0.1.4ra1n.love也会解析到对应的IP

成功利用本地的gadget达到RCE的效果

 

0x05 RCE实现

为了验证在MacOS中的结果,我将漏洞环境打包发给了天下大木头师傅

然后在服务端启动MacOS弹计算器的LDAP Server(该工具以后分享,最近不太方便)

 

木头师傅成功在MacOSRCE,不需要进行其他修改