文章归档

置顶文章

Web安全

Web安全基础

PHP相关

Writeups

靶机系列

HackTheBox

VulnHub

代码审计

PHP代码审计

流量分析

机器学习

基础学习

Python

Python编程

Java

Java编程

算法

Leetcode

随笔

经验

技术

 2019-11-18   1.6k

Web安全学习之变量覆盖漏洞利用

这一篇主要通过几道CTF题讲解一下PHP变量覆盖漏洞利用,主要涉及到extract()函数,parse_str()函数,import_request_variables()函数,$$运算符。

全局变量覆盖

PHP 5.3.0被废弃,PHP5.4.0被移除

PHP中使用变量并不需要初始化,因此register_globals=On时,变量来源可能是各个不同的地方,比如页面的表单、Cookie等。例如:

1
2
3
4
5
6
<?php
echo "Register_gl`obals: " . (int)ini_get("register_globals") . "<br/>";
if ($auth) {
echo "private";
}
?>

当register_globals=Off时,这段代码不会有问题:

但是当register_globals=On时,提交http://localhost/variableCover/index.php/?auth=1,变量$auth会自动得到赋值:

如果在代码中已经初始化了$auth变量,也不会有变量覆盖漏洞。

另外通过$GLOBALS获取的变量,也可能导致变量覆盖。

extract()变量覆盖

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
10
<?php
$auth = 1;
extract($_GET);

if($auth == 1){
echo "private";
}else{
echo "public";
}
?>

在这个例子里,extract()从$_GET中导出变量,从而可以导致任意变量被覆盖,即:

parse_str()变量覆盖

函数定义:

1
void parse_str (string $str [, array $arr])

当parse_str()函数的参数值可以被用户控制时,则存在变量覆盖漏洞:

1
2
3
4
5
<?php 
$a = 'init';
parse_str($SERVER['QUERY_STRING']);
print $a;
?>

再来看一道CTF的题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
if(empty($_GET['id'])) {
show_source(__FILE__);
die();
} else {
include ('flag.php');
$a = "www.ca0y1h.top";
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
echo $flag;
} else {
exit('so easy!');
}
}
?>

也就是PHP弱类型和变量覆盖结合的题目:

mb_parse_str()变量覆盖

mb_parse_str()函数用于解析GET/POST/COOKIE数据并设置全局变量,和parse_str()类似。

import_request_variables()变量覆盖

支持版本:PHP 4 >= 4.1.0, PHP 5 < 5.4.0

import_request_variables()函数将GET、POST、Cookies中的变量导入到全局。

函数定义如下:

1
bool import_request_variables (string $types [, string $prefix])

$type代表要注册的变量,G代表GET,P代表POST,C代表COOKIE,第二个参数为要注册变量的前缀,如果没有指定,则将覆盖全局变量。例如:

1
2
3
4
5
6
7
8
9
10
<?php
$auth = '0';
import_request_variables('P');

if ($auth === '0') {
echo "private";
}else{
echo "public";
}
?>

$$导致的变量覆盖

$$即可变变量,一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。例如:

1
2
3
4
5
6
7
<?php  
$x = "ca01h";
$$x = 666;
echo $x."<br/>";
echo $$x."<br/>";
echo $ca01h;
?>

其实也就等价于$($x),输出为:

1
2
3
ca01h
666
666

$$导致的变量覆盖问题在CTF代码审计题目中经常在foreach中出现,如以下的示例代码,使用foreach来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的键值作为变量的值。因此就产生了变量覆盖漏洞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
foreach (array('_COOKIE','_POST','_GET') as $_request)
{
foreach ($$_request as $_key=>$_value)
{
$$_key= $_value;
}
}
$id = isset($id) ? $id : "test";
if($id === "ca01h") {
echo "flag{xxxxxxxxxx}";
} else {
echo "Nothing...";
}
?>

这里以GET、POST或COOKIE都能触发变量覆盖漏洞,传入id=ca01h后,在foreach语句中,$_key为id,$_value为ca01h,进而$$_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
24
<?php
include "flag.php";
$_403 = "Access Denied";
$_200 = "Welcome Admin";
if ($_SERVER["REQUEST_METHOD"] != "POST"){
die("BugsBunnyCTF is here :p…");
}
if ( !isset($_POST["flag"]) ){
die($_403);
}
foreach ($_GET as $key => $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的参数进行变量覆盖,但两个语句的处理是有点区别的即一个键值为$$value另一个为$value。

这里整理一下思路:

  1. 因为POST的参数必须为flag,则第二个foreach语句的$key为flag,进而$$key$flag,从而得到$flag的值为POST传递的flag参数的值。
  2. 又因为第二个foreach语句修改了$flag原来的值为POST传递的flag参数的值,因而最后一个if语句的条件是恒不成立的,在其后的else代码块逻辑中echo输出出来的只能是修改了的$flag的值即POST传递的flag参数的值而非原本的$flag的值、接着输出$_200变量的值。
  3. 要想输出原本的$flag的值,我们需要将原本的$flag覆盖$_200变量,因此在第一个foreach语句中通过GET输入_200=flag,从而得到的$$key$_200 以及$$value$flag,从而实现在修改$flag的值之前将其覆盖到$_200变量中。

验证结果:

Reference

Copyright © ca01h 2019-2020 | 本站总访问量