不可见的npm恶意软件-用精心制作的版本逃避安全检查

的npm CLI有一个非常方便和众所周知的安全特性-当安装一个npm包时,CLI检查包及其所有依赖项,以查找众所周知的漏洞-

检查在包安装(运行时)时触发npm安装),但也可以通过运行手动触发npm审计.
这是一个重要的安全措施,警告开发人员不要使用带有已知漏洞的包。
最近,我们遇到了npm工具的一个意想不到的行为,它可能具有安全隐患——两者都有npm安装而且npm审计无法显示特定版本格式的包的警告,使使用这些包的开发人员面临潜在的风险,可能会将关键漏洞或恶意软件引入他们的系统和/或间接依赖于他们的NPM包。
在这篇博客文章中,我们将进一步解释这个问题,攻击者如何利用它来逃避他们发布的恶意包的安全检查,并建议开发人员如何避免被它所迷惑。
意想不到的结果
在使用一些npm包时,我们注意到npm CLI报告的漏洞和JFrog Xray报告的漏洞之间有一个有趣的差异。
安装特定软件包时,即cruddl 2.0.0-update.2,我们从npm和Xray -收到了不同的漏洞指示


对于同一个包,npm发现0个漏洞,而Xray发现了一个漏洞(CVE-2022-36084)。我们开始发现这是一个可能的漏洞,还是别的什么……
分类问题
经过多次尝试和包安装后,我们意识到这种差异只发生在安装的包版本包含破折号/连字符的字符(交货。1.2.3-a).那么,为什么会这样呢?
当上传一个包时,NPM允许遵循严格的版本格式语义版本控制必须通过node-semver.以下是两个完全有效的例子:5.6.7,5.6.7-a.
npm-install/audit工具将所有依赖包及其版本收集到一个json字典中,并将其发送到名为The的npm API端点批量咨询端点.端点检查每个包和版本,试图通过将版本与警告的影响范围相匹配来找到相关的警告。它将所有这些相关的警告附加到API端点返回的列表中。
我们已经发现,对于版本包含连字符(-)后跟附加字符的包,批量咨询端点无法检索安全咨询。
这些连字符的版本到底是什么?
根据语义版本控制规范,版本的格式为MAJOR.MINOR.PATCH。例如5.6.7。然而,如条款9预发布版本可以通过“紧跟补丁版本后附加一个连字符和一系列点分隔标识符”来指定。如。5.6.7-a
猜测这种行为的来源
由于npm端点的代码是闭源的,我们只能猜测这个问题的根源。
每项谘询(看例子)有一个影响版本保存逻辑表达式的字段(例如:> 6.6.0),以描述受影响版本的范围。如果某个版本满足任何逻辑表达式,则认为它是脆弱的。
根据node-semver的文件(并且可能semver的实现),在比较具有预发布标记的版本时有一些特殊的规则(请参阅中的预发布标记部分文档).
例如,默认情况下:
semver. satisfy ("1.2.3-a", "> 0")
返回假,这可能是相当出乎意料的,因为该通知的作者的意图是覆盖包的所有版本(例如,在为恶意包创建通知时)。
文档还指出,可以通过设置属性来抑制这种行为(为了范围匹配,将所有预发布版本视为普通版本)includePrerelease选项对象上的标记。
因此,对标志集执行相同的检查:
semver.satisfies(“1.2.3-a”、“> 0 ",{“includePrerelease”:1})
返回真正的.
在Bulk Advisory端点中启用这个标志应该可以消除常规npm包版本和预发布版本之间的不一致。
根据JFrog的披露,我们从NPM维护者那里了解到这个功能是预期的行为,因此我们不希望这个行为发生改变。
概念证明
让我们来cruddl,一个存在严重漏洞的真实包(cve - 2022 - 36084).
在安装cruddl在2.0.0版本中,输出显示(正如预期的那样!)发现了一个严重的漏洞:

如果不是上面的,我们选择安装同一个包的预发布版本:

输出(错误!)显示没有发现任何漏洞,即使版本2.0.0-update.2也受CVE-2022-36084影响(固定版本为2.7.0).
攻击者如何滥用这种行为?
威胁行为者可以利用这种行为,故意在他们看似无害的包中植入易受攻击的或恶意的代码,这些代码将被其他开发人员包括在内,这是由于有价值的功能或由于错误的感染技术,如拼写错误或依赖关系混淆上一篇博文举个例子)。
如上所述,如果威胁行为者使用预发布版本格式的包版本,即使这样的代码被报告为恶意/易受攻击,并创建了一个警告,依赖恶意/脆弱包的开发人员将没有任何通知,因为npm CLI不会报告此类警告的存在。
开发者该如何避免这个问题?
我们对开发人员和DevOps工程师的建议是不要用预发布版本安装NPM包,除非他们100%确定包裹来自一个非常有信誉的来源。即使在这种情况下,我们也建议尽快回到包的非预发布版本。
您可以使用以下命令行来确定当前是否安装了预发布版本的npm包-
Linux:
npm list -a | grep - e @[0-9]+\.[0-9]+\.[0-9]+-
Windows:
NPM list -a | findstr -r @[0-9]*\.[0-9]*\.[0-9]*-
与JFrog安全研究保持最新
在我们的JFrog安全研究团队中跟踪最新的发现和技术更新安全研究博客文章并在推特上@JFrogSecurity.
