变量覆盖及危害
变量覆盖(Dynamic Variable Evaluation)是指变量未被初始化,我
们自定义的参数值可以替换程序原有的变量值。
通常结合程序的其它漏洞实现完整的攻击,比如文件上传页面,覆盖掉原来白名单的列表,导致任意文件上传;用户注册页面控制没覆盖的未初始化变量导致SQL。
常见的变量覆盖的特征
变量初始化变量
bianli.php
<?php
$a = 1;
foreach (array('_COOKIE','_POST','_GET') as $_request){
foreach ($$_request as $_key => $_value){
$$_key = addslashes($_value);
}
}
echo $a;
//echo $_key;
//echo $$_key;
这里我们先分析下代码原理,当我们在url中没有传入a参数时,它只是单纯输出a变量的值,当我们传入a参数时,这时执行循环,将$a
的变量的值赋值给$key
,此时$key=$a
,而$$key
的值就等于a传入的参数值,达到一个变量覆盖的作用。
将上面注释掉的两行代码取消
第一个123是原本$a
的值,a是指$key
的值,123是指$$key
的值。这里为了让小伙伴更好地理解代码,其实上面的代码意思就是将$a和对应的值存进一个数组中并取名为变量request,然后双变量request指向的就是变量a,在第二次每次循环中都把变量a赋给变量key,把变量a的值赋给value。
注:造成以上效果必须满足一个条件就是php.ini
文件中的register_globals
的值必须为Off
extract()变量覆盖
int extract ( $array , extract_rules,prefix )
$array 关联的数组,受第二个和第三个参数的影响。
extract_rules 对待非法/数字和冲突的键名的方法将根据取出标记
prefix 仅在第二个参数特殊时需要,添加前缀
第一种
<?php
//第一种情况
$password = 'pwd';
$arr = array(
'username' => 'username',
'password' => 'password',
'rand' => 'rand'
);
extract($arr,EXTR_PREFIX_SAME,'pwd');
echo "$username.$password.$rand";
echo "." . $pwd_password;
运行效果如下图所示
extract()
检查每个键名看是否可以作为一个合法的变量名,同时也检查和符号表中已有的变量名的冲突。对待非法/数字和冲突的键名的方法将根据 extract_type
参数决定。
这里关键是extract($arr,EXTR_PREFIX_SAME,'pwd');
这个代码,这个代码原本的意思是倘若变量名有冲突,在变量名前加上前缀 prefix。现在$pwd_password
是password
添加前缀后才能等于关联数组内password
对应的值,这样就达到了一个变量覆盖的作用。
第二种
<?php
$password = 'pwd';
$arr = array(
'username' => 'username',
'password' => 'password',
'rand' => 'rand'
);
extract($arr,EXTR_OVERWRITE);
echo "$username.$password.$rand";
运行效果如下
这里不同的是extract($arr,EXTR_OVERWRITE);
,如果发现有冲突,则直接覆盖并输出关联数组中的对应的键的值。
第三种
<?php
$password = 'pwd';
$arr = array(
'username' => 'username',
'password' => 'password',
'rand' => 'rand'
);
extract($arr,EXTR_IF_EXISTS);
echo $password;
运行效果图
常见的危险函数
参数 | 描述 |
---|---|
EXTR_OVERWRITE | 默认。如果有冲突,则覆盖已有的变量。 |
EXTR_SKIP | 如果有冲突,不覆盖已有的变量。(忽略数组中同名的元素) |
EXTR_PREFIX_SAME | 如果有冲突,在变量名前加上前缀 prefix。自 PHP 4.0.5 起,这也包括了对数字索引的处理 |
EXTR_PREFIX_ALL | 给所有变量名加上前缀 prefix(第三个参数) |
EXTR_PREFIX_INVALID | 仅在非法或数字变量名前加上前缀 prefix。本标记是 PHP 4.0.5 新加的。 |
EXTR_IF_EXISTS | 仅在当前符号表中已有同名变量时,覆盖它们的值。其它的都不处理。可以用在已经定义了一组合法的变量,然后要从一个数组例如 $_REQUEST 中提取值覆盖这些变量的场合。本标记是 PHP 4.2.0 新加的。 |
EXTR_PREFIX_IF_EXISTS | 仅在当前符号表中已有同名变量时,建立附加了前缀的变量名,其它的都不处理。本标记是 PHP 4.2.0 新加的。 |
EXTR_REFS | 将变量作为引用提取。这有力地表明了导入的变量仍然引用了 var_array 参数的值。可以单独使用这个标志或者在 extract_type 中用 OR 与其它任何标志结合使用。本标记是 PHP 4.3.0 新加的。 |
parse_str()变量覆盖
void parse_str ( string
$encoded_string
[, array &$result ] )
$encoded_string 输入的字符串
$result 变量将会以数组元素的形式存入到这个数组,作为替代
<?php
$a = 'a';
parse_str($a = 'b');
echo $a;
运行效果
这里这个函数和前一个不同,这个是直接在函数中对变量的值进行替换,它不会先去检测变量的值是否存在冲突
import_request_variables函数
bool import_request_variables ( string $types [, string $prefix ] )
$type 指定需要导入的变量。可以用字母‘G’、‘P’和‘C’分别表示 GET、POST 和 Cookie
$prefix 变量名前缀
<?php
$a = '0';
import_request_variables('G');
if($a != 0){
echo 'success';
}else{
echo 'fail';
}
这个函数将 GET/POST/Cookie
变量导入到全局作用域中。如果你禁止了 register_globals
,但又想用到一些全局变量,那么此函数就很有用。
这里给出一个别人大佬在CTF中见到的例子,以下是它的链接
https://www.cnblogs.com/xhds/p/12587249.html#_label3
修复方案
- 在php.ini文件中设置register_globals=OFF
- 使用原始变量数组,如
$_POST
,$_GET
等数组变量进行操作 - 不使用foreach语句来遍历$_GET变量,而改用[(index)]来指定
- 验证变量是否存在,注册变量前先判断变量是否存在