如何预防 下一个类似Log4j 的零日安全漏洞

JFrog 安全研究小组关于如何检测和防止代码中未知安全漏洞的高级见解

作者:Asaf Karas、Oren Ish-Shalom、Ilya Khivrich,2022 年 5 月 17 日

注:本博文曾发表在《Dark Reading》上

众所周知,软件测试并非易事。在谷歌上搜索由基本 CRLF(换行符)问题导致的通用漏洞披露 (CVE),将显示成千上万的条目。人类已实现载人登月,但却还没找到合适的方法来处理文本文件中的行结束符。这些微小的细节很容易被程序员忽略。

Log4j 固有的复杂性将这一挑战提升到全新的高度。Log4Shell 漏洞 (CVE-2021-44228) 自 2013 年以来一直存在,但没有引发关注。在 Log4Shell 漏洞作为零日漏洞暴露出来并震惊业界之前,可以用哪些工具来成功发现它呢?自动检测出这些未知安全漏洞,这样的想法现实吗?如果真能做到这一点,那么像 Log4j 这样经过严格测试的模块是如何逃过所有防线的呢?

请注意,这个问题比下列问题更难回答:“我要如何在代码库中检测并修补已知易受攻击的 Log4j 版本”(这个问题在过去几周已经被广泛讨论过,在本文中不再赘述)。

零日漏洞检测挑战

所有人都是事后诸葛,现在非常清楚的是,日志 API 函数最终将接收用户控制的数据,而JNDI 注入是一个主要问题。然而,在发现 Log4Shell 之前,这两种情况并非显而易见。对于前一种情况,应用程序和代码库之间验证输入的责任划分并不明确,攻击面的精确定义也不清晰。对后者而言,尽管 JNDI 注入在几年前便已为人所知¹˒²,但人们对其仍缺乏认识。

因此,检测 CVE-2021-44228(或其他类似漏洞)的最大障碍可能是模糊性。数值是否可信,是否对其执行操作,没有绝对完整的定义。实际上,任何分析都取决于对这些灰色地带的理解。

当需求模糊时,开发人员会发现自己处于不利地位。代码安全性审查旨在检查大型软件模块的行为是否有“任何错误”,这是一项艰巨的任务。审查人员倾向于寻找局部错误,因为通过几十个函数调用来掌握程序不同部分之间的关系并不可行。与之形成鲜明对比的是,有些攻击者试图利用代码中疑似存在的“特定”漏洞,对他们来说,这项任务轻而易举。

零日漏洞自动检测——是否可行?

模糊测试是一种有效的动态分析技术,通过对随机(或伪随机)输入执行程序,并查找程序崩溃或违反某些断言的实例,来识别未知漏洞。模糊测试适用于检测零日漏洞吗?

模糊测试通常意味着寻找具有内存损坏迹象的程序崩溃。在内存安全的 Java 环境中,程序崩溃通常不会带来严重的安全隐患。对于有意义的模糊测试,需要在特定的逻辑条件下自定义钩子,用于提示问题行为。此外,需要构建一个提供输入(在我们的例子中是 Log4j API 函数)的模糊工具。这两种构建都需要手动操作。完成此设置后,模糊测试可以用来检测零日漏洞,甚至是其他软件中的类似漏洞(假设所需的钩子和工具足够相似)。然而,与手动审查代码的情况一样,尝试不同假设相关的需求和手动操作的模糊性,很可能会导致这个问题在模糊测试中也被遗漏。

接来下是最后一个备选项:静态分析,它会检查程序可能出现的行为,但并不实际执行。静态分析的一个特别有趣的形式是据流分析——跟踪程序中数据的可能路径,从数据源开始,一直到数据接收器。在所讨论的情况下,从 Log4j API 函数参数开始到 JNDI 查找的数据路径,表明有可利用的漏洞存在。

在这篇文章的剩余部分,我们将举例说明在分析 Log4j 时,带有/未带一组预定接收器的现代过程间静态分析器所面临的困难。

通过静态分析检测零日漏洞——更深入的观察

查看下面的代码片段示例(摘自 log4j-2.14.1-core)。当 LogEvent e 的一个字段中包含用户控制的字符串时,它向静态分析器发出信号,表示应该进一步跟踪。检查虚拟调用 (appender.append(e)) 会发现 Appender 接口有二十多个实现。静态分析器如何确定使用了哪项实现?无法确定!也不能确定。根据为人所熟知的停机问题,静态确定在运行时实际采用哪条代码路径实际上是不可行的

那么分析器能做什么呢?可以进行过近似处理。只要代码中可能存在危险的代码路径,您的代码就会被标示为“危险代码”。就像整夜敞开大门也不见得会有人来偷智能电视一样,静态分析器会给出类似的提示:“最好大门紧锁”。

静态分析器固有的“过近似”处理使它们可以扩展到现实生活中的代码。试想一下循环。动态方法会不可避免地单独探索循环的后续迭代。实际上这意味着涉及的状态数量是无限的。简而言之,静态分析器将通过综合考虑 0、1、2……次迭代的效果来总结循环的效果。

那么静态分析器的缺点是什么呢?缺乏精确性,或者换句话说,误报。由于静态分析器将许多代码路径的影响结合在一起,包括不可行的路径,所以主要的问题是用户会在数据包中面对无数被标示为“危险”的虚假代码路径。与之形成对比,动态方法的输出极为精确且可重构。动态方法能够精确定位漏洞的确切位置。

越来越多的公司在开发周期中使用静态安全分析³。开发人员通过 IDE 集成进行编码时,静态分析工具可提供越来越多的指导。然而,由于上述挑战,在没有适当接收器定义的情况下,大多数现有工具会“拒绝”执行数据路径分析。在许多方面,这些工具是绝对正确的。即使一组明确的预定义接收器,也会有无数的误报,所以想象一下在没有接收器时,会出现什么情况。

我们建议整个行业都应该向更“交互”的静态分析方向发展。试想一下,开发人员编码时,有一个工具可以提供有关用户输入潜在风险的信息。例如,无论是否有预定义接收器,该工具均可标记用户控制的输入数据流,并提供感染病毒的用户控制输入的视觉引导,而不是确定的目的地。可以想象一种 IDE 用红色加粗字体显示来自攻击者的字符串,当程序员感到可疑时,他们可以选择检查这些“数据流提示”。进一步扩展这个想法,程序员可以选择放大某些代码区域,并定义自有(内部的,非 API)源代码。这种方法十分灵活,将为零日漏洞检测带来颠覆性转变。

结论

总体来说,数据流静态分析有助于开发人员能够尽早识别涉及受操纵用户输入的漏洞,如 Log4Shell。现如今,数据流分析是安全研究的一个活跃领域;将这项技术广泛应用于开发人员工具中并作为 DevSecOps 过程的一部分,应该是全行业的目标。

虽然通常来说,程序 API 可以对用户控制入口点进行定义,但定义使用用户控制输入的危险代码接收器就比较棘手了。对于零日漏洞检测来说更是如此。请记住这个重要的概念:开发人员有时无法判断要寻找什么样的安全警告信号。但是,使用自动“助手”管理传输的用户控制输入,显然会有所帮助。为此,我们提倡以“左移”静态分析插件的形式提供交互式 IDE 支持。

 

¹ https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdf
² https://www.veracode.com/blog/research/exploiting-jndi-injections-java
³ http://bodden.de/pubs/nal+17jit.pdf