Composer是PHP中管理和保护软件依赖关系的主要工具,在更新过程中被开发团队广泛使用。因此,Composer使用一个名为Packagist的在线服务来确定包下载供应链的正确性。Packagist的每月下载请求约为14亿次。在进行安全研究时,研究人员在Packagist使用的Composer源代码中发现了一个严重的安全漏洞,漏洞CVE编号为CVE-2021-29472。攻击者可利用此漏洞在Packagist.org服务器上执行任意系统命令。此外,攻击者可以进一步窃取维护者凭据,或将包下载重定向到传播后门依赖项的第三方服务器。漏洞分析当请求包下载时,Composer首先向Packagist查询元数据。元数据包含两个域source和dist以获取代码的来源。Source只想开发库,dist只想要预构建的库。Composer在从存储库下载代码时使用外部系统命令,以避免为每个版本控制软件重新实现逻辑。因此,这些调用是使用包装器ProcessExecutor执行的:$output=null,$cwd=null){if(func_num_args()>1){return$this->doExecute($command,$cwd,false,$output);}return$this->doExecute($command,$cwd,false);}//[...]privatefunctiondoExecute($command,$cwd,$tty,&$output=null){//[...]if(method_exists('Symfony\Component\Process\Process','fromShellCommandline')){//[1]$process=Process::fromShellCommandline($command,$cwd,null,null,static::getTimeout());}else{//[2]$process=newProcess($command,$cwd,null,null,static::getTimeout());}if(!Platform::isWindows()&&$tty){try{$process->setTty(true);}catch(RuntimeException$e){//ignoreTTYenablingerrors}}$callback=is_callable($output)?$output:array($this,'outputHandler');$process->run($callback);在[1]和[2]中,可以看到在shell中执行了参数$command。大多数ProcessExecutor调用都在版本控制软件驱动程序中执行,该驱动程序加载原始库和本机库的所有操作。例如Git驱动中:composer/src/Composer/Repository/Vcs/GitDriver.phppublicstaticfunctionsupports(IOInterface$io,Config$config,$url,$deep=false){if(preg_match('#(^git://|\.git/?$|git(?:olite)?@|//git\.|//github.com/)#i',$url)){returntrue;}//[...]尝试{$gitUtil->runCommand(function($url){return'gitls-remote--heads'.ProcessExecutor::escape($url);//[1]},$url,sys_get_temp_dir());}catch(\RuntimeException$e){returnfalse;}使用ProcessExecutor::escape()可以转义参数$url以防止子命令($(...),`...`),但不能阻止用户提供的(--)开头的值只要加上其他参数就可以成为最终的命令。这种类型的漏洞称为参数注入。类似的漏洞模式出现在其他驱动程序中,其中用户控制的数据可以成功绕过检查并连接到系统命令中:deep=false){$url=self::normalizeUrl($url);if(preg_match('#(^svn://|^svn\+ssh://|svn\.)#i',$url)){returntrue;}//[...]$process=newProcessExecutor($io);$exit=$process->execute("svninfo--non-interactive".ProcessExecutor::escape($url),$ignoredOutput);composer/src/Composer/Repository/Vcs/HgDriver.phppublicstaticfunctionsupports(IOInterface$io,Config$config,$url,$deep=false){if(preg_match('#(^(?:https?|ssh)://(?:[^@]+@)?bitbucket.org|https://(?:.*?)\.kilnhg.com)#i',$url)){returntrue;}//[...]$process=newProcessExecutor($io);$exit=$process->execute(sprintf('hgidentify%s',ProcessExecutor::escape($url)),$ignored);return$exit===0;}有关更多技术细节,请参阅:https://blog.sonarsource.com/php-supply-chain-attack-on-composerA补丁研究人员将漏洞提交给Packagist团队后,该团队迅速响应,在12小时内部署了安全补丁。本文翻译自:https://blog.sonarsource.com/php-supply-chain-attack-on-composer
