Log4j2-RCE
Log4j2-RCE
S0cke3t前言
最近Log4j2的RCE漏洞可谓是刷爆了整个安全圈,也是被国内外安全圈的大佬们玩出了花。在发布漏洞后笔者也是根据官方commit对此次漏洞进行了调试分析,But….周末杂七杂八的事实在太多一直没来得及记录。今天发出来也算是蹭一下最后的余温….
漏洞描述
Apache Log4j2
是一个基于Java的日志记录工具。该工具重写了Log4j框架,并且引入了大量丰富的特性。该日志框架被大量用于业务系统开发,用来记录日志信息。
由于Apache Log4j2 lookup
功能存在递归解析功能,攻击者可直接构造恶意请求,⽆需进⾏特殊配置,即可触发远程代码执⾏。
影响范围
Java类产品:Apache Log4j 2.x < 2.15.0-rc2
受影响的应用及组件(包括但不限于)如下:Apache Solr、Apache Flink、Apache Druid、Apache Struts2、srping-boot-strater-log4j2
等。
环境搭建
IDEA新建Maven项目,并导入如下依赖
1 | <dependency> |
新建Selvlet
1 | @WebServlet(name = "ServletDemo", value = "/ServletDemo") |
漏洞分析
首先使用互联网上公开的漏洞触发语句进行调试分析
1 | Logger logger |
进入到error函数后首先会调用logIfEnabled
,在该函数中首先会调用isEnabled
判断是否要进行日志记录(具体细节后面会讲)。如果满足一定条件则调用logMessage
进行下一步操作。
在经过一系列操作后我们来到org.apache.logging.log4j.core.pattern!format
在format函数中会匹配${
,当匹配到${
内容后并将其中内容取出调用substitube
进行下一步操作
跟进substitube
省略一些判断后substitube
会调用resolveVariable
,继续跟进
resolveVariable
首先会调用getVariableResolver
获取可用的resolver,随后调用lookup函数
lookup首先根据:
的索引获取到传入的协议类型,随后调用this.strLookupMap.get
获取一个lookup实例。随后调用lookup实例的lookup,跟进
Jndilookup
首先调用JndiManager.getDefaultManager();
获取jndi context
,随后调用jndimanager.lookup
完成最终的jndi注入
在调试完整个流程后,我们不禁会思考这样一个问题,既然在默认配置下logger.error()
可以触发jndi注入那么像logger.info(),logger.warn(),logger.all()
等其他函数是否也能触发jndi注入。
首先我们把先前的代码稍作修改
在默认配置下,我们分别调用error,warn,info
三个函数,并修改dnslog域名前缀。这样我们就知道dnslog是由哪个函数发出的
在发出请求后,dnslog只收到了来自error函数的记录。而其他的warn,info
并没有像我们预期的那样触发jndi注入
出现该问题的关键就在于我们之前提到的isEnable
函数。
在isEnable
中调用了this.privateConfig.filter
,我们看看在filter中具体做了什么
filter首先调用this.config.getfilter
获取配置信息,如果没有获取到配置信息,则对this.intLevel
和level.intLevel
进行比较,只有当this.intLevel
大于或等于level.intLevel
才会进行下一步的日志记录操作
而log4j2默认过滤级别为error
,所以当我们传入过滤级别高于默认过滤级别时不会触发jndi注入。
当我们手动为log4j2指定一个过滤级别
再次发送请求后,可以成功触发dnslog请求
RC1 绕过
在发布该漏洞后,apache官方紧急发布了RC1修复版本,但RC1由于修复不完善导致可进行绕过
如果要在RC1下利用此漏洞,需要用户在打开lookup功能下才能进行JNDI注入。
Payload
1 |
|
篇幅有限,具体绕过分析可参考Apache Log4j2从RCE到RC1绕过