二、运行时 SCA - 供应链安全新视角
三、运行时 SCA 实践方案
四、结语
本文字数:5188 ,阅读时长:13分钟
SCA 工具为用户(主要是开发人员和安全运营人员)提供了两大核心能力,一是对软件组成成分进行梳理,形成软件物料清单(SBOM);二是基于 SBOM 内容,对其中涉及安全漏洞或许可合规风险的组件进行告警。
所谓“警报疲劳”是指用户被 SCA 工具的海量报告淹没,导致疲于对漏洞修复优先级排序或对误报进行处理。造成这一问题的原因首先是由于现代软件变得越来越复杂,一个应用程序通常会有数十或数百个直接依赖,其间接依赖数量更是直接依赖的两倍甚至更多;其次,SCA 工具难免会产生一些误报:报告存在已知风险但并未被真正引用或无法直接利用的组件。这种误报会导致开发人员对工具失去信心,即产生“警报疲劳”。
SCA 工具提供的上下文信息不足也是导致“警报疲劳”的原因之一。如果 SCA 报告中没有提供已识别风险的影响、严重程度、修复建议等信息,研发人员就需要花费更多的精力在了解漏洞细节上,增加了修复漏洞的平均时间。
为了减轻“警报疲劳”,需要提高漏洞的检测的准确性、减少误报、对海量结果修复的优先级进行排序,提供更详细的漏洞描述信息。然而,实现上述功能对静态 SCA 工具来说,仍有较长的距离。
可达性是一个用来确定应用程序中的任何调用路径是否可以到达某个脆弱函数的术语。这种分析有助于理解一个存在于间接依赖中的漏洞如何被到达和利用。直接依赖是与代码直接关联的,开发者通常清楚其作用;间接依赖是直接依赖本身所依赖的组件(一般在构建过程中间接导入所需的代码),可能开发者并未意识到。可达性分析不仅是评估软件漏洞风险的关键概念,也是轻松修复漏洞的关键。通过分析软件架构中的各种路径和依赖关系,可达性有助于识别潜在的攻击向量,以及修复的最快方式。理解可达性有助于优先考虑修复工作,引导开发者关注直接依赖可以提高修复的效率和速度。它还有助于通过确定哪些第三方包负责大部分漏洞并需要打补丁,来识别对组织的真正风险。
可达性分析主要有静态分析和运行时分析两种方式:
1. 静态分析会遍历源代码,试图绘制出所有函数调用路径,来检查是否可以到达脆弱函数。但静态分析通常不太容易绘制反射或者动态加载操作的调用路径。并且由于静态分析会穷举出所有可能的调用路径,其结果仍然需要花费精力去审计。
2. 运行时分析则是将一段代码(代理,库)植入到应用程序的运行时环境中,观察正在运行的应用程序,监控加载到运行时环境中的类、在运行时调用的函数,并检查是否实际使用了脆弱的函数/类。运行时使用分析可以帮助用户进一步梳理确定漏洞修复优先级列表。运行时 SCA 检查的是运行态的可达性,因此可以正确反映出漏洞真实利用路径,更适合在流水线中使用。
如《IAST 技术进阶系列(一):关键语言支持》中所介绍,运行时 SCA 需要根据不同编程语言特性单独编写“探针”,来获取加载到应用运行时环境的组件信息。通常来讲,按照是否需要编译可将编程语言分为三类:编译型、解释型和混合型。
- 编译型编程语言:C/C++、Go、Rust 等就是典型的编译型编程语言,这类型的运行时插桩探针通常需要通过类似“劫持”的方式,监控或接管编译器的行为,来达到获取编译后制品中包含的第三方组件信息的目的。
- 解释型编程语言:常见的解释型编程语言有 JavaScript、Python、PHP等,这类型的运行时插桩探针一般是通过对底层函数包装后,通过调用解释器内置模块,例如 python的 sys.modules;Node.js 的 require.cache 等方法获取已经加载到运行时环境的组件信息。
- 混合型编程语言:指的是例如 Java、.Net 这类需要编译生成中间文件,然后在 JVM、CLR 中运行的半编译半解释型编程语言。JVM和 CLR 都提供了可以获取运行时依赖信息的接口,例如 Instrumentation API 和 Reflection API 。
通过上述方式获取到运行时 SCA 的依赖信息后,就可以像静态 SCA 工具一样,通过知识库匹配得到更详细的组件信息和漏洞信息了。
...
java.util.concurrent.Executors
java.util.concurrent.Executors$DefaultThreadFactory
org.apache.logging.log4j.core.LifeCycle$State
org.apache.logging.log4j.internal.LogManagerStatus
org.apache.logging.log4j.core.LoggerContext
org.apache.logging.log4j.spi.Terminable
org.apache.logging.log4j.core.config.ConfigurationListener
org.apache.logging.log4j.spi.LoggerContextShutdownEnabled
org.apache.logging.log4j.core.AbstractLifeCycle
org.apache.logging.log4j.core.util.ExecutorServices
org.apache.logging.log4j.core.config.NullConfiguration
org.apache.logging.log4j.core.config.AbstractConfiguration
org.apache.logging.log4j.core.filter.AbstractFilterable
org.apache.logging.log4j.core.Filter
org.apache.logging.log4j.core.async.AsyncLoggerConfigDelegate
org.apache.logging.log4j.core.util.Watcher
org.apache.logging.log4j.core.lookup.StrLookup
org.apache.logging.log4j.core.Layout
org.apache.logging.log4j.core.layout.Encoder
org.apache.logging.log4j.core.Appender
org.apache.logging.log4j.core.net.Advertiser
org.apache.logging.log4j.core.util.NanoClock
org.apache.logging.log4j.core.config.ConfigurationSource
org.apache.logging.log4j.core.config.Property
org.apache.logging.log4j.core.config.DefaultAdvertiser
org.apache.logging.log4j.core.lookup.Interpolator
org.apache.logging.log4j.core.lookup.AbstractConfigurationAwareLookup
org.apache.logging.log4j.core.config.ConfigurationAware
org.apache.logging.log4j.core.lookup.AbstractLookup
org.apache.logging.log4j.core.lookup.MapLookup
org.apache.logging.log4j.core.lookup.Log4jLookup
org.apache.logging.log4j.core.lookup.SystemPropertiesLookup
org.apache.logging.log4j.core.lookup.EnvironmentLookup
org.apache.logging.log4j.core.lookup.MainMapLookup
org.apache.logging.log4j.core.lookup.MarkerLookup
org.apache.logging.log4j.core.lookup.JavaLookup
org.apache.logging.log4j.core.lookup.LowerLookup
org.apache.logging.log4j.core.lookup.UpperLookup
org.apache.logging.log4j.core.lookup.JndiLookup
org.apache.logging.log4j.core.lookup.JmxRuntimeInputArgumentsLookup
org.apache.logging.log4j.core.lookup.DateLookup
org.apache.logging.log4j.core.lookup.ContextMapLookup
...
此外,也可以反方向进行排查。例如,先圈定需验证可达性的漏洞范围,对这些漏洞涉及的类及方法进行埋点,然后通过 PoC 或者 DAST/Fuzzing 工具等方式进行测试,验证漏洞是否可达。这个方案适合周期性地对一批应用进行筛查。例如若要在一批应用中排查是否涉及 Fastjson远程代码执行漏洞(CNVD-2019-22238),可在
com.alibaba.fastjson.parser.DefaultJSONParser$parseObject
处埋点,通过构造利用 Payload 验证其是否可达。在灵脉 IAST 中验证情况如下:
1. 组件安全加固:对所有底层调用进行加固,例如命令执行、文件读写等,在执行敏感操作时进行过滤和拦截。
- 通过运行时SCA技术,RASP探针可以在应用程序运行过程中识别到其依赖的所有组件清单;
- 通过RASP云端组件漏洞数据库对比分析,可以自动下发组件漏洞修复热补丁,实现对组件风险的代码级加固防御,从根源上杜绝漏洞被利用的可能性。
以Java为例,当由用户代码、第三方组件等代码一起编译生成的混源应用制品与RASP探针共同加载到JVM虚拟机中时,在Instrumentation API的transform阶段,我们不仅可以动态地修改目标Hook方法,同时也可以获取到所有加载到JVM的第三方组件信息。此时,借助云端运行时SCA分析引擎,即可获得在运行阶段加载第三方组件的风险情况,借助热修复补丁库,云端可自动下发对应的热修复补丁,动态地将风险组件的风险方法屏蔽或注入额外的安全判断逻辑,在不直接修改源代码情况下实现对风险组件的代码级加固。
2. 应急风险响应:根据 CVE 针对性下发策略,动态地对具有风险的调用进行拦截。例如,阻断 Log4j2 的方法;或是在 fastjson执行前插入类型推断的逻辑。
关于运行时威胁免疫的更多原理介绍,也可参考RASP技术进阶系列(四):基于安全共生的供应链安全风险防御。
- 在开发阶段通过 CI/CD 流程接入源代码 SCA 工具,梳理识别项目中依赖的第三方组件信息。
- 在测试阶段,通过部署例如 IAST 工具、RASP工具的运行时插桩探针来获取运行时依赖的第三方组件信息。
- 在上述阶段的工具中,都接入开源组件威胁情报,持续获取最新的开源组件风险信息、漏洞利用函数调用链路(漏洞可达性信息)、漏洞验证信息(漏洞利用 Payload)。安全运营人员可参考这些数据,圈定出需要处置的风险范围。
- 当应用进入测试阶段,将基于开源组件威胁情报的“漏洞验证信息”和“漏洞可达性”录入到 IAST 或 RASP 工具中后,检查应用运行环境,判断运行环境是否具备漏洞利用条件(如:Spring4Shell漏洞利用需要运行在JDK9或更高版本),然后监控漏洞风险入口函数,判断组件漏洞风险是否可触达。在应用运行过程中,如果风险函数被调用,则认为风险可达。需注意以下情况:
- 部分组件漏洞可在应用正常功能触发时自动进行验证(如文件上传、反序列化、命令执行等通用类型漏洞)。
- 剩余部分组件漏洞,需要进行发送 Payload 请求验证:可以通过 IAST/DAST 工具的流量引擎自动发送,或手工构造 Payload发送进行验证。
- 将SCA工具的静态可达性分析和IAST工具的运行时可达性分析结果汇总合并后,由安全运营人员统一评估是否需要修复。
- 对于没有源代码的应用,可通过二进制SCA代替源代码SCA,配合运行时可达性分析进行漏洞修复优先级研判。
- 在应用上线后常态化安全运营中,可通过运行时SCA配合开源威胁情报持续对应用进行风险监控。相比静态SCA方案,运行时SCA可以在线下发风险函数监控或阻断规则,在不重启应用的情况下,监控风险函数是否可达、是否被触发、是否被利用。同时,运行时SCA利用DSDX SBOM能够实现对存量资产的开源治理和组件级资产测绘。
- 通过静态 SCA,梳理出资产清单,获得一个大致治理范围的漏洞治理清单;
- 通过开源组件威胁情报,持续更新各类组件的漏洞风险入口函数及其对应的验证 Payload;
- 通过在测试环境中部署运行时 SCA 监控探针,根据功能测试流量和验证 Payload 自动收敛漏洞治理清单。
收敛流程如下图所示:
原文始发于微信公众号(悬镜安全):SCA 技术进阶系列(五): 揭秘运行时SCA-新视角下的供应链安全革新
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论