概述
漏洞挖掘
研究人员首先检查了一个用于管理Definitely Typed文件的GitHub存储库(https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/README.cn.md)。然后,研究人员注意到对DefinitelyTyped存储库不具有任何权限的普通GitHub用户,可以使用@typescript bot命令合并拉取请求。根据以往的经验,研究人员认为该bot中可能存在漏洞,因此其决定对dt-mergebot源代码进行审计(https://github.com/definitelytyped/dt-mergebot)。
在开始调查dt-mergebot之前,研究人员在项目概述中找到以下图片:
看起来Definitely Typed具有包维护者的概念,不同于存储库维护者。包维护者是指只有管理单个类型定义包(如@types/node)权限的用户,并且在每个包中指定。如果用户具有目标包的包维护者权限,则他们可以批准仅包含对目标包的更改的拉取请求,而无需使用Definitely Typed维护者权限。
正确解析拉取请求并不容易,并且可能会引发错误。相比较下,通过添加类型定义就可以轻松获得包维护者权限,因此研究人员决定看看是否可以通过使用特制的拉取请求来绕过基于包的权限管理。
在阅读了dt-mergebot的源代码后,研究人员发现它的行为如下:
-
为拉取请求获取更改的文件
-
检查哪个类型定义包有更改的文件
-
向类型定义包中指定的包维护者发送审查请求
-
如果包维护者批准了更改,则向拉取请求的发送者授予合并权限
-
当拉取请求发送者发送评论Ready to merge时,typescript-bot合并拉取请求
虽然它从API获取更改的文件时看起来并没有什么问题,但研究人员注意到以下代码。
const { nodes, totalCount } = prInfo.files!;
if (nodes!.length < totalCount) console.warn(` *** Note: ${totalCount - nodes!.length} files were not seen by this query!`);
该代码会检查API返回的totalCount参数,并将其与实际返回的文件总数进行比较。如果返回的文件数小于totalCount,则执行console.warn。
检查dt-mergebot如何获取文件后,研究人员发现了以下GraphQL查询:
query PR($pr_number: Int!) {
repository(owner: "DefinitelyTyped", name: "DefinitelyTyped") {
id
pullRequest(number: $pr_number) {
[...]
files(first: 100) {
totalCount
nodes {
path
additions
deletions
}
}
[...]
}
}
}
从该查询中可以看出,dt-mergebot在计算合并所需的权限时,只获取了前100个文件。
如上所述,如果获取的文件数小于totalCount,则执行console.warn并在控制台上显示警告。
但是,console.warn不会停止后续处理。因此,如果有人发出更改101个或更多文件的拉取请求,则第101个和后续文件将不会受到权限检查。因此,权限检查将被破坏。所以,利用该漏洞即可以绕过基于包的权限管理。
漏洞利用
-
在npm上找到一个没有类型定义的库。
-
为库创建类型定义
-
将拉取请求发送到DefinitelyTyped存储库以添加其类型定义,并等待拉取请求被合并
-
合并拉取请求后,创建一个新拉取请求,将100个文件添加到包含在步骤3中添加的类型定义的目录中
-
在拉取请求中对要篡改的文件进行更改
-
在typescript-bot检查拉取请求后,将授予用户批准该拉取请求的权限
-
添加Ready to merge作为评论
-
拉取请求将被合并,并且目标文件将被篡改
END
本文始发于微信公众号(SecTr安全团队):篡改npm@types范围内的任意包
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论