概述
cdnjs库更新服务器中存在任意代码执行漏洞,可导致cdnjs被完全破坏。一旦缓存过期,攻击者就可以利用该漏洞篡改互联网上所有使用cdnjs的网站。
CDNJS是Cloudflare旗下的JavaScript/CSS资料库,为数百万网站提供超过4000个JavaScript和CSS文件,在互联网上有12.7%的网站使用了CDNJS,是继Google Hosted Libraries之后第二个使用最广泛的JavaScript CDN。
初始调查
-
cdnjs/packages: 存储cdnjs支持的库信息
-
cdnjs/cdnjs: 存储库文件
-
cdnjs/logs: 存储库的更新日志
-
cdnjs/SRIs: 存储库的SRI
-
cdnjs/static-website:cdnjs.com 的源代码
-
cdnjs/origin-worker: 初始cdnjs.cloudflare.com的Cloudflare Worker
-
cdnjs/tools: cdnjs 管理工具
-
cdnjs/bot-ansible: cdnjs库更新服务器的Ansible存储库
从这些存储库中可以看出,大多数cdnjs的基础设施都集中在这个GitHub Organization中。其中,研究人员对cdnjs/bot-ansible和cdnjs/tools比较感兴趣,因为它可以自动更新库。
在浏览了这两个存储库的代码后,研究人员发现cdnjs/bot-ansible会定期执行cdnjs库更新服务器中cdnjs/tools的autoupdate命令,通过下载npm包/Git存储库来检查cdnjs/packages中库的更新。
自动更新调查
路径遍历
研究人员开始阅读autoupdate命令的主要函数,以寻找路径遍历:
func main() {
[ ]
switch *pckg.Autoupdate.Source {
case "npm":
{
util.Debugf(ctx, "running npm update")
newVersionsToCommit, allVersions = updateNpm(ctx, pckg)
}
case "git":
{
util.Debugf(ctx, "running git update")
newVersionsToCommit, allVersions = updateGit(ctx, pckg)
}
[ ]
}
从以上代码片段可以看出,如果将npm指定为自动更新的源,它会将包信息传递给 updateNpm函数。
func updateNpm(ctx context.Context, pckg *packages.Package) ([]newVersionToCommit, []version) {
[...]
newVersionsToCommit = doUpdateNpm(ctx, pckg, newNpmVersions)
[...]
}
然后,updateNpm将有关新库版本的信息传递给doUpdateNpm函数。
func doUpdateNpm(ctx context.Context, pckg *packages.Package, versions []npm.Version) []newVersionToCommit {
[...]
for _, version := range versions {
[...]
tarballDir := npm.DownloadTar(ctx, version.Tarball)
filesToCopy := pckg.NpmFilesFrom(tarballDir)
[...]
}
并且,doUpdateNpm将新版本的.tgz文件的URL传递给npm.DownloadTar函数。
func DownloadTar(ctx context.Context, url string) string {
dest, err := ioutil.TempDir("", "npmtarball")
util.Check(err)
util.Debugf(ctx, "download %s in %s", url, dest)
resp, err := http.Get(url)
util.Check(err)
defer resp.Body.Close()
util.Check(Untar(dest, resp.Body))
return dest
}
最后,将使用http.Get获取的.tgz文件传递给Untar函数。
func Untar(dst string, r io.Reader) error {
gzr, err := gzip.NewReader(r)
if err != nil {
return err
}
defer gzr.Close()
tr := tar.NewReader(gzr)
for {
header, err := tr.Next()
[...]
// the target location where the dir/file should be created
target := filepath.Join(dst, removePackageDir(header.Name))
[...]
// check the file type
switch header.Typeflag {
[...]
// if it's a file create it
case tar.TypeReg:
{
[...]
f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
[...]
// copy over contents
if _, err := io.Copy(f, tr); err != nil {
return err
}
}
}
}
}
和研究人员预期的一样,解压缩函数中使用了compress/gzip和archive/tar来提取 .tgz 文件。起初,研究人员以为它正在清理removePackageDir函数中的路径,但是检查函数内容后,研究人员注意到它只是从路径中删除了package/。
从这些代码片段中,研究人员确认从发布到npm的.tgz文件执行路径遍历并覆盖服务器上定期执行的脚本后,可以执行任意代码。攻击过程如下所示:
-
将包含特制文件名的.tgz文件发布到npm
-
等待cdnjs库更新服务器处理特制的.tgz文件
-
特制的 .tgz 文件内容被写入定期执行的脚本文件,并执行任意代码。
END
本文始发于微信公众号(SecTr安全团队):cloudflare cdnjs中的任意代码执行漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论