前言
最近“深信服EDR控制端存在多个漏洞”事件让我知道了PHP变量覆盖漏洞,那就来学习总结一下。发现网上许多文章不给参考链接,这个习惯不好。
文献[1]给出了几种细分漏洞类型:
- $$变量赋值引发的覆盖
- extract函数导致的覆盖
- 全局变量的覆盖
- parse_str函数导致的覆盖
- import_request_variables函数导致的覆盖
下面来看一下吧。目前我并没有实践,只是简单了解。后文主要是由各博文拼接修改而来的,在此感谢。
$$变量赋值引发的覆盖
$$是一种可变变量的写法,它可以使一个普通变量的值作为可变变量的名字,这种类型常常会使用遍历的方式来释放变量的代码,最常见的就是foreach的遍历,示例代码如下:
<?php
$yml = 10;
echo $yml;
echo "<br>";
foreach ($_POST as $k => $v){
$$k = $v;
echo $yml;
}
?>
无任何操作时正常输出,当POST内容为 yml=1000
时, $yml
的值变为了1000,成功实现变量覆盖。
extract函数导致的覆盖
该函数可以将变量从数组中导入当前的符号表。参考某教程[2]有:
将键值 “Cat”、“Dog” 和 “Horse” 赋值给变量 $a、$b 和 $c:
<?php
$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\\$a = $a; \\$b = $b; \\$c = $c";
?>
明白了,也就是extract可能会从不可信输入中读入恶意payload,然后覆盖重要变量。比如 [[深信服EDR控制端存在多个漏洞]] 。
这里,在extract之后又 eval($code)
,那么攻击者当然可以去先去覆盖变量,再去eval。
全局变量的覆盖
如果某些变量没有被初始化,并且黑客可以控制,那将会是一件很危险的事情。在这种情况下,漏洞触发的前提是 register_globals
为 ON
( register_globals
的值可以在php.ini中修改,网友发现在phpStudy上的PHP 5.2版本后该值默认是 OFF
)。
例如:
<?php
echo (int)ini_get("register_globals");
echo '<br>';
echo "yml=".$yml;
?>
在 register_globals
打开的情况下,我们直接提交 yml=3
也会将其赋值。
还有一种情况: $GLOBALS
获取的变量在使用不当时也会导致变量覆盖,漏洞触发的前提同样是 register_globals
为 ON
。例如,我们可以直接提交 GLOBALS[yml]=3
实现覆盖。
parse_str函数导致的覆盖
该函数可以把查询的字符串解析到变量中。参考某教程[3]有:
<?php
parse_str("name=Peter&age=43");
echo $name."<br>";
echo $age;
?>
这个函数的语法如下:
parse_str(string,array)
如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。
另外,php.ini文件中的 magic_quotes_gpc
设置影响该函数的输出。如果已启用,那么在 parse_str()
解析之前,变量会被 addslashes()
转换。
所以当我们没有设置函数的第二个参数时,攻击者很可能通过特定的输入来覆盖代码中的已定义变量:
<?php
$yml = "cool";
echo "out0:".$yml;
echo "<br>";
$a = $_GET['a'];
parse_str($a);
echo "out1:".$yml;
?>
import_request_variables函数导致的覆盖
该函数将GET、POST、Cookie 变量导入到全局作用域中,如果你禁止了 register_globals
,但又想用到一些全局变量,那么此函数就很有用。但该函数在最新版本的 PHP 中已经不支持。继续参考某教程[4](感谢🙏):
这个函数的语法如下:
bool import_request_variables ( string $types [, string $prefix ] )
实例:
<?php
// 此处将导入 GET 和 POST 变量
// 使用 runoob_ 作为前缀
import_request_variables("gP", "runoob_");
echo $runoob_foo;
?>
该函数的第二个参数用于设置注册变量的前缀。漏洞点是当第二个参数未设置时,全局变量可能会被覆盖。
例如:
<?php
$yml = "happy";
echo "out0:".$yml;
echo "<br>";
import_request_variables('P');
echo "out1:".$yml;
?>
在POST提交了 yml=xxx
时, $yml
变量将被覆盖。