变量覆盖漏洞的概念
变量覆盖漏洞是指攻击者使用
自定义的变量去覆盖源代码中的变量
,从而改变代码逻辑,实现攻击目的的一种漏洞。通常来说,单独
的变量覆盖漏洞很难有利用价值,但是在与
其他应用代码或漏洞
结合后,可能会有很大的影响。
变量覆盖漏洞相关的函数
变量覆盖漏洞大多数由函数使用不当导致,经常引发变量覆盖漏洞的函数有:
extract()
parse_str()
import_request_variables()
$$(
可变变量
)
register_globals=On
(
PHP5.4
之后正式移除此功能)
变量覆盖漏洞原理和代码解析
extract()
extract()
函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。该函数返回成功设置的变量数目。
语法:
extract
(
array
,
extract_rules
,
prefix
)
参数解释:
array :
必需。规定要使用的数组。
extract_rules:
可选。
extract()
函数将检查每个键名是否为合法的变量名,同时也检查和符号表
中已存在的变量名是否冲突。对不合法和冲突的键名的处理将根据此参数决定。
可能的值:
EXTR_OVERWRITE -
默认。如果有冲突,则覆盖已有的变量。
EXTR_SKIP -
如果有冲突,不覆盖已有的变量。
EXTR_PREFIX_SAME -
如果有冲
突,在变量名前加上前缀
prefix
。
EXTR_PREFIX_ALL -
给所有变量名加上前缀
prefix
。
EXTR_PREFIX_INVALID -
仅在不合法或数字变量名前加上前缀
prefix
。
EXTR_IF_EXISTS -
仅在当前符号表中已有同名变量时,覆盖它们的值。其它的都
不处理。
EXTR_PREFIX_IF_EXISTS -
仅在当前符号表中已有同名变量时,建立附加
了前缀的变量名,其它的都不处理。
EXTR_REFS -
将变量作为引用提取。导入的
变量仍然引用了数组参数的值。
prefix :
可选。如果
extract_rules
参数的值是
EXTR_PREFIX_SAME
、
EXTR_PREFIX_ALL
、
EXTR_PREFIX_INVALID
或
EXTR_PREFIX_IF_EXISTS
,则
prefix
是必需的。 该参数规定了前缀。前缀和数组键名之间会自动加上一个下划
线。
代码示例:
<?php
// 声明变量
$name = "jack";
// 定义一个数组
$arr = array("name" => "rose");
// 将数组转变成变量
extract($arr);
// 输出
echo $name;
parse_str()
parse_str()
函数把查询字符串解析到变量中。
注释:
如果未设置
array
参数,由该函数设置的变量将覆盖已存在的同名变量。
注释:
php.ini
文件中的
magic_quotes_gpc
设置影响该函数的输出。如果已启用,那么在
parse_str()
解析之前,变量会被
addslashes()
转换。
语法:
parse_str
(
string
,
array
)
参数解释:
string:必需。规定要解析的字符串。
array:可选。规定存储变量的数组名称。该参数指示变量存储到数组中。
代码示例:
<?php
parse_str("name=HSJ&age=18");
echo $name."<br>";
echo $age;
?>
<?php
parse_str("name=HSJ&age=18",$arr);
print_r($arr);
?>
变量覆盖示例:
<?php
$name = "Jack";
parse_str("name=HSJ&age=18");
echo $name;
echo "<br/>";
echo $age;
?>
<?php
$arr = array("name" => "Jack");
parse_str("name=HSJ&age=18", $arr);
print_r($arr);
?>
import_request_variables()
import_request_variables()
函数将
GET
/
POST
/
Cookie
变量导入到全局作用域中。
该函数在最新
版本的
PHP
中已经不支持。
import_request_variables()
函数将
GET
/
POST
/
Cookie
变量导入到全局作用域中。如果你禁止了
register_globals
,但又想用到一些全局变量,那么此函数就很有用。
版本要求:
PHP 4 >= 4.1.0, PHP 5 < 5.4.0
语法:
bool
import_request_variables
(
string
$types
[,
string
$prefix
] )
参数说明:
$types
:指定需要导入的变量,可以用字母
G
、
P
和
C
分别表示
GET
、
POST
和
Cookie
,这些字
母不区分大小写,所以你可以使用
g
、
p
和
c
的任何组合。
POST
包含了通过
POST
方法上传的文
bool
import_request_variables
(
string
$types
[,
string
$prefix
] )
件信息。
注意这些字母的顺序,当使用
gp
时,
POST
变量将使用相同的名字覆盖
GET
变量。任何
GPC
以外的字母都将被忽略。
$prefix
: 变量名的前缀,置于所有被导入到全局作用域的变量之前。所以如果你有个名为
userid
的
GET
变量,同时提供了
pref_
作为前缀,那么你将获得一个名为
$pref_userid
的全局变量。虽
然
prefix
参数是可选的,但如果不指定前缀,或者指定一个空字符串作为前缀,你将获得一个
E_NOTICE
级别的错误。
示例代码:
<?php
header("Content-type:text/html;charset=utf-8");
$name = "jack";
// 导入GET和POST作为变量
import_request_variables("gp");
// 输出,如果此时GET/POST中有name变量,那么此时name就会被覆盖掉
echo $name;
$$
$$
是一种可变变量的写法,它可以使一个普通变量的值作为可变变量的名字,这种类型常常会使用遍历
的方式来释放变量的代码,最常见的就是
foreach
的遍历。
代码示例:
<?php
session_start();
$name = "rose";
// 准备全局变量名
$arr = array("_GET","_POST","_COOKIE","_SESSION");
// 遍历
foreach ($arr as $value){
foreach ($$value as $k=>$v){
$$k = $v;
}
}
echo $name;
如果
GET/POST/COOKIE/SESSION
中任意传递来
name
参数, 代码里面的
name
变量就会被覆盖。
变量覆盖防御
变量覆盖漏洞最常见的漏洞点是在做变量注册时没有验证变量是否存在,以及在赋值给变量的时候,所
以一般推荐使用原始的变量数组,如
$
GET
、
$
POST
,或者在注册变量前一定要验证变量是否存在。
1.
使用原始变量
建议不注册变量,直接用原生的
$
GET
、
$
POST
等数组变量进行操作,如果考虑程序可读性等原因,需要
注册个别变量,可以直接在代码中定义变量,然后再把请求中的值赋值给他们。
2.
验证变量存在
如果一定要使用前面几种方式注册变量,为了解决变量覆盖的问题,可以再注册变量前先判断变量是否
存在。如使用
extract( )
函数则可以配置第二个参数为
EXTR_SKIP
。使用
php parse_str
函数注册变量前需
要先自行通过代码判断变量是否存在。最重要的一点,自行申明的变量一定要初始化,不然即使注册变
量代码在执行流程最前面也能覆盖掉这些未初始化的变量。