变量覆盖(DynamicVariableEvaluation)是指变量还没有被初始化,我们自定义的参数值可以替换程序原来的变量值。
漏洞危害:
一般结合程序中的其他漏洞实现完整的攻击,例如文件上传页面覆盖原来的白名单列表,导致任意文件上传; 未初始化的变量未被用户注册页面控制覆盖导致SQL。
挖矿经验:
迭代已初始化的变量
常见遍历方法释放代码,可能导致变量覆盖漏洞
extract() 变量覆盖
intexttract($array, extract_rules, 前缀)
• 与$array 关联的字段受第二个和第三个参数的影响。
• extract_rules 如何处理非法/数字和冲突密钥将遵循提取标志
• 仅当第二个参数特殊时才需要前缀,添加前缀
危险函数
• EXTR_OVERWRITE - 默认值。 如果存在冲突php变量类型,现有变量将被覆盖。
• EXTR_SKIP - 如果存在冲突,请勿覆盖现有变量。 (忽略链表中同名元素)
• EXTR_PREFIX_SAME - 如果存在冲突,请在变量名称前添加前缀。从 PHP 4.0.5 开始,这还包括数字对
索引处理
• EXTR_PREFIX_ALL - 所有变量名称都添加前缀(第三个参数)
• EXTR_PREFIX_INVALID - 仅添加非法或数字变量名称的前缀。 该标签是PHP4.0.5中新添加的。
• EXTR_IF_EXISTS - 仅当当前符号表中已存在同名变量的值时才覆盖它们。 其他未处理,已可以使用
定义了一组合法的变量,然后需要从$_REQUEST这样的链表中取出值来覆盖这样的变量。 这个标记是PH
P4.2.0中新增。
• EXTR_PREFIX_IF_EXISTS - 仅当当前符号表中已存在同名变量时才构造附加前缀的变量名,除此之外别无其他
处理。 该标签是PHP4.2.0中新添加的。
• EXTR_REFS - 提取变量作为引用。 这强烈表明导出的变量始终引用 var_array 参数的值。可以
单独使用该标志或与 extract_type 中的任何其他标志或 OR 一起使用。该标签是 PHP4.3.0 中新添加的
的。
变量覆盖是指通过外部输入覆盖变量的值。
通常,将原始变量值可以替换为自定义参数值的情况称为变量覆盖漏洞。
0x02register_globals 全局变量覆盖
php.ini中有一项叫做register_globals,就是注册全局变量。 当register_globals=On时,传递的值将直接注册为全局变量并直接使用。 当register_globals=Off时,我们需要去特定的字段去获取。
注意:register_globals 自 PHP5.3.0 起已被弃用,自 PHP5.4.0 起将被删除。
当register_globals=On时,变量未初始化且可由用户控制,会存在变量覆盖漏洞:
1
2
3
4
5
6
7
<?php
echo "Register_globals: " . (int)ini_get("register_globals") . "
";
if ($a) {
echo "Hacked!";
}
?>
通过GET和POST方法输入变量a的值:
其实也可以从COOKIE中输入:
0x03extract() 变量覆盖率
extract() 函数将变量从链表导出到当前符号表。 该函数使用链表键值作为变量名,使用链表通配符作为变量值。 对于链表中的每个元素,都会在当前符号表中创建一个对应的变量。
函数定义如下:
1
int extract ( array $var_array [, int $extract_type [, string $prefix ]] )
其中,第二个参数指定函数将变量导出到符号表时的行为。 两个最常见的值是EXTR_OVERWRITE和EXTR_SKIP。
当值为EXTR_OVERWRITE时,在导出变量到符号表的过程中,如果变量名冲突,所有变量都会被覆盖; 值为EXTR_SKIP,表示跳过,不覆盖。 如果不指定第二个参数,则默认使用EXTR_OVERWRITE。
当extract()函数从用户控制的链表导入变量且第二个参数未设置或设置为EXTR_OVERWRITE时,存在变量覆盖漏洞:
1
2
3
4
5
6
7
8
9
上面的例子以GET为例:
防御方法:调用extract()时使用EXTR_SKIP,确保现有变量不会被覆盖
1
extract($_GET,EXTR_SKIP);
0x04parse_str()变量覆盖率
parse_str()函数一般用于解析URL中的查询字符串,并将查询字符串解析为变量。 如果没有数组参数,则该函数设置的变量将覆盖现有的同名变量。
函数定义如下:
1
void parse_str ( string $str [, array &$arr ])
当parse_str()函数的参数值可以由用户控制时,存在变量覆盖漏洞:
1
2
3
4
5
6
7
8
9
10
我们看一下子主题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
是弱类型和变量重写的组合:
0x05mb_parse_str() 变量覆盖
mb_parse_str()函数用于解析GET/POST/COOKIE数据并设置全局变量,与parse_str()类似:
1
2
3
4
5
6
7
8
9
10
0x06import_request_variables()变量覆盖率
支持版本:PHP4>=4.1.0、PHP5<5.4.0
import_request_variables()函数将GET、POST、Cookies中的变量导出到全局。
函数定义如下:
1
bool import_request_variables (string $types [, string $prefix])
$type代表要注册的变量,G代表GETphp变量类型,P代表POST,C代表COOKIE,第二个参数是要注册的变量的前缀。
使用该函数只需指定类型即可,这里G指定导出GET请求中的变量:
1
2
3
4
5
6
7
8
9
10
0x07$$引起的变量覆盖
$$ 是一个可变变量。 可变变量获取普通变量的值作为可变变量的变量名。
$ 和 $$ 之间的区别
$var 是普通变量,名称为:var,存储任意值,如:字符串、整数、浮点数等。
$$var 是一个引用变量,用于存储$var 的值。
看看演示:
1
2
3
4
5
6
7
8
9
10
11
<?php
$x = "mi1k7ea";
$$x = 666;
echo $x."
";
echo $$x."
";
echo $mi1k7ea;
//也可以写到双引号中解析,输出结果一样
// echo "$x
";
// echo "${$x}
";
// echo "$mi1k7ea";
?>
变量覆盖漏洞
$$导致的变量覆盖问题在CTF代码审核题目中经常出现在foreach中,比如下面的示例代码,使用foreach遍历链表中的值,然后将得到的链表键值作为变量,链表中的通配符作为变量的值。 因此,形成了可变覆盖漏洞。 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$_value)
{
$$_key= $_value;
}
}
$id = isset($id) ? $id : "test";
if($id === "mi1k7ea") {
echo "flag{xxxxxxxxxx}";
} else {
echo "Nothing...";
}
?>
这里可以通过GET、POST或者COOKIE来触发。 传入id=mi1k7ea后,foreach语句中$_key为id,$_value为mi1k7ea,因此$$_key为$id,实现了变量覆盖:
CTF主题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$value){
$$key = $$value;
}
foreach ($_POST as $key => $value){
$$key = $value;
}
if ( $_POST["flag"] !== $flag ) {
die($_403);
} else {
echo "This is your flag : ". $flag . "n";
die($_200);
}
?>
可以看到,有3个if语句和2个foreach语句。
if语句中,第一个要求你通过POST方法发出请求,第二个需要POST一个flag参数,第三个是比较flag参数是否等于包含的真实flag。
foreach语句中,第一个是用变量覆盖GET的参数,第二个是用变量覆盖POST的参数,不过这两个语句的处理有点不同,就是一个名为的key $$value 另一个是$value。
这里总结一下思路:由于POST的参数必须是flag,所以第二个foreach语句的$key就是flag,所以$$key就是$flag,所以$flag的值就是flag参数通过 POST 值传递; 这里因为第二个foreach语句将$flag的原始值改为POST传递的flag参数的值,所以最后一个if语句的条件始终不成立,在后续的else代码块逻辑中echo输出只能为改变后的$flag值,即POST传递过来的flag参数的值代替原来的$flag值,然后输出$_200变量的值; 如果要输出原来的$flag值,我们需要将原来的$flag覆盖到$_200变量中,所以在第一个foreach语句中,通过GET输入_200=flag,这样得到的$$key是 $_200,$$value 是 $flag,因此实现在更改 $flag 的值之前将其覆盖到 $_200 变量。
验证一下:
parse_str() 变量覆盖
voidparse_str(字符串$encoded_string[,数组&$结果])
• $encoded_string 输入的字符串
•$result 变量将作为链接列表元素存储在此字段中
导入请求变量()
(PHP4>=4.1.0,PHP5<5.4.0)
import_request_variables - 将 GET/POST/Cookie 变量导出到全局范围
boolimport_request_variables(字符串$类型[,字符串$前缀])
• $type 指定需要导出的变量。 字母“G”、“P”和“C”分别可以用来表示 GET、POST 和 Cookie
• $prefix 变量名前缀
使固定
• 在 php.ini 文件中设置 register_globals=OFF
• 使用原有变量字段,如$_POST、$_GET等字段变量进行操作
• 不要使用foreach 语句来遍历$_GET 变量,而是使用[(index)] 来指定
•验证变量是否存在,在注册变量之前判断变量是否存在
反序列化漏洞
序列化和反序列化
•序列化:将对象转换为字节序列的过程称为对象的序列化
• 反序列化:将字节序列恢复为对象的过程称为对象的反序列化
漏洞驱动:
反序列化对象有魔法,但是魔法里面的代码是可以控制的,
漏洞根据不同的代码可以引发多种攻击,如代码注入、SQL注入、目录遍历等
日历等
序列化的不同结果
• 民众
• 私人的
• 保护
漏洞性质:
• unserialize函数的变量是可控的
• php文件中有可用的类,并且类中有魔术
魔术技巧:
__construct(),__destruct()
__call(),__callStatic()
__get(),__set()
__isset(),__unset()
__睡眠(),__唤醒()
__toString()
__调用()
__set_state()
__克隆()
__调试信息()
漏洞案例
根据不同的代码可以造成多种类型的攻击,如代码注入、SQL注入、目录遍历等。