此漏洞基于 CVE-2024-50340 漏洞
必须在 PHP 配置文件(php.ini)中启用 register_argc_argv 选项。此设置会将命令行参数和查询参数加载到 $_SERVER['argv'] 中。
https://www.php.net/manual/en/ini.core.php#ini.register-argc-argv
//Illuminate\Foundation\Application
public function detectEnvironment (Closure $callback)
{
$args = $_SERVER['argv'] ?? null;
return $this['env'] = (new EnvironmentDetector)->detect($callback, $args);
}
目的:此方法通过访问 $_SERVER['argv'](该变量保存命令行参数)来检测环境,然后将检测任务委托给 EnvironmentDetector 类。
漏洞成因:由于依赖 $_SERVER['argv'],任何注入的 URL 参数(例如 ?--env=dev)都可能操纵 $_SERVER['argv'],使其包含 ["--env=dev"]。该数组会被直接传递给 detect,从而可能允许攻击者控制环境。
//Illuminate\Foundation\EnvironmentDetector
public function detect(Closure $callback, $consoleArgs = null)
{
if ($consoleArgs) {
return $this->detectConsoleEnvironment($callback, $consoleArgs);
}
return $this->detectWebEnvironment($callback);
}
目的:detect 方法确定是否提供了 $consoleArgs(argv 数组)。如果存在,则调用 detectConsoleEnvironment 来处理基于控制台的环境检测;否则,对 HTTP 请求调用 detectWebEnvironment。
漏洞触发路径:如果 $_SERVER['argv'] 包含像 --env=dev 这样的注入值,则会进入 detectConsoleEnvironment,该方法会处理这些参数,并可能将应用程序切换到不同的环境。
//Illuminate\Foundation\EnvironmentDetector
protected function detectConsoleEnvironment(Closure $callback, array $args)
{
// First we will check if an environment argument was passed via console arguments
// and if it was that automatically overrides as the environment. Otherwise, we
// will check the environment as a "web" request like a typical HTTP request.
if (! is_null($value = $this->getEnvironmentArgument($args))) {
return $value;
}
return $this->detectWebEnvironment($callback);
}
目的:此方法检查控制台参数中是否存在 --env 参数。
机制:它调用 getEnvironmentArgument 来查看 argv 参数中是否有指定环境的参数。如果提供了 --env,此方法会返回指定的环境值,从而绕过 detectWebEnvironment 的调用。
//Illuminate\Foundation\EnvironmentDetector
protected function getEnvironmentArgument(array $args)
{
foreach ($args as $i => $value) {
if ($value === '--env') {
return $args[$i + 1] ?? null;
}
if (str_starts_with($value, '--env=')) {
return head(array_slice(explode('=', $value), 1));
}
}
}
目的:此方法解析 argv 以查找 --env 参数。
机制:
如果将 --env 作为独立标志找到,则返回下一个参数作为环境值。
如果使用了 --env= 语法(例如 --env=dev),则会提取并返回环境(本例中为 dev)。
环境配置:
.env 文件中设置了 APP_ENV=development,因此默认情况下应用程序处于 development 环境。
@production
<p>Production environment</p>
@endproduction
@env ('local')
<p>Local environment</p>
@endenv
默认访问(http://localhost):
由于 APP_ENV 设置为 development,且未注入 --env 参数,因此 @production 和 @env('local') 指令均不匹配。
结果:输出为空。
为生产环境注入参数(http://localhost?--env=production):
当 URL 中包含 ?--env=production 时,$_SERVER['argv'] 会被操纵,使其包含 ["--env=production"]。
这会触发 Laravel 的环境检测机制,将环境设置为 production。
结果:@production 指令会输出 <p>Production environment</p>。
为本地环境注入参数(http://localhost?--env=local):
当 URL 中包含 ?--env=local 时,$_SERVER['argv'] 会包含 ["--env=local"],从而将环境更改为 local。
结果:@env('local') 指令输出 <p>Local environment</p>
此漏洞无法更改 config 函数中的环境。相反,它只会影响 Application 类中的环境设置。
因此,尽管 Blade 指令和代码的其他部分依赖 Application 类来检测环境,但使用 config 函数的代码不受此漏洞影响。