参考文章
PHP中的变量覆盖漏洞
简介
变量覆盖----自定义的参数值替换原有变量值的情况称为变量覆盖漏洞
经常导致变量覆盖漏洞场景有:开启了全局变量注册、$$ 使用不当、extract() 函数使用不当、parse_str() 函数使用不当、import_request_variables() 使用不当等。
具体
全局变量注册
- register_globals
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLwkzMyIzNwMTNx0SNwcDOygzM2ETMxATMwIDMy0iN3AzM5gTMvwFMxAjMwIzLcZzNwMTO4EzLcd2bsJ2Lc12bj5ycn9Gbi52YuAjMwIzZtl2Lc9CX6MHc0RHaiojIsJye.png)
在 PHP5.3 之前,默认开启;PHP5.3 默认关闭,PHP5.6 及 5.7 已经被移除
当
register_globals
全局变量设置开启时,传递过来的值(
POST/GET/Cookie
)会被直接注册为全局变量而使用,这会造成全局变量覆盖
小例子:
<?php
if ($_GET["username"] == "1ndex" and $_GET["password"] == "1ndex"){
$authorized = true;
}
if(!$authorized){
echo "Failed";
}
else{
echo "Success";
}
?>
register_globals=Off
时,只能通过提交正确的账号密码才可成功登陆,也就是
http://127.0.0.1/blfg.php?username=1ndex&password=1ndex
register_globals=On
时,可以无需账号密码通过验证,只需要将变量
authorized
设置为
真
即可,可为
http://127.0.0.1/blfg.php?authorized=1
或者
http://127.0.0.1/blfg.php?GLOBALS[authorized]=1
register_globals=Off
时,该方法无效
$$ -- 动态变量覆盖
首先看到这里:
<?php
$a = "test";
$test = "who am i";
echo($$a);
?>
最终输出结果是什么?? --
who am i
$$a
也可以写成
${$a}
,解析后就是
$test
也就是最终结果
who am i
看到这样一个题:
<?php
foreach (array('_POST','_GET') as $_request)
{
foreach ($$_request as $_key=>$_value)
{
$$_key = $_value;
}
}
$id = isset($id) ? $id : 2;
if($id == 1) {
include(./flag);
die();
}
?>
我们怎么样取得flag?很显然,需要包含
flag
文件
直接
get/post
传参
id=1
即可,当执行
$$_key = $_value;
时,
$_key="id"
,
$$_key
等价于
$id
,就终结果也就是
$id = 1
,定义了
id
参数,并赋值为
1
,也就导致了包含
flag
extract()
extract()
函数从数组中将变量导入到当前的变量表。该函数使用数组键名作为变量名,使用数组键值作为变量值。
<?php
$a = array('nickname' => '1ndex');
extract($a);
echo $nickname;
?>
//运行结果为输出 1ndex
extract(array[,flag][,prefix])
三个参数:
-
array
一个关联数组(必需)。此函数会将键名当作变量名,值作为变量的值。 对每个键/值对都会在当前的符号表中建立变量,并受到
和flags
prefix
参数的影响。
必须使用关联数组,数字索引的数组将不会产生结果,除非用了
EXTR_PREFIX_ALL
。EXTR_PREFIX_INVALID
-
flags
对待非法/数字和冲突的键名的方法将根据取出标记
参数决定。可以是以下值之一:flags
-
EXTR_OVERWRITE
如果有冲突,覆盖已有的变量。(默认)
-
EXTR_SKIP
如果有冲突,不覆盖已有的变量。
-
EXTR_PREFIX_SAME
如果有冲突,在变量名前加上前缀
prefix
-
EXTR_PREFIX_ALL
给所有变量名加上前缀 prefix。
-
EXTR_PREFIX_INVALID
仅在非法/数字的变量名前加上前缀
prefix
-
EXTR_IF_EXISTS
仅在当前符号表中已有同名变量时,覆盖它们的值。其它的都不处理。 举个例子,以下情况非常有用:定义一些有效变量,然后从
中仅导入这些已定义的变量。$_REQUEST
-
EXTR_PREFIX_IF_EXISTS
仅在当前符号表中已有同名变量时,建立附加了前缀的变量名,其它的都不处理。
-
EXTR_REFS
将变量作为引用提取。这有力地表明了导入的变量仍然引用了
参数的值。可以单独使用这个标志或者在array
中用flags
与其它任何标志结合使用。OR
-
-
prefix
注意 prefix 仅在 flags 的值是 EXTR_PREFIX_SAME,EXTR_PREFIX_ALL,EXTR_PREFIX_INVALID 或 EXTR_PREFIX_IF_EXISTS 时需要。 如果附加了前缀后的结果不是合法的变量名,将不会导入到符号表中。前缀和数组键名之间会自动加上一个下划线。
flags
参数默认为
EXTR_OVERWRITE
,即如果有冲突,覆盖已有的变量
小题目:
<?php
extract($_GET);
if(isset($mypwd))
{
if($mypwd==$pwd)
{
include("./flag");
}
}
?>
答案:
http://127.0.0.1/blfg.php?pwd=1&mypwd=1
parse_str()
parse_str()
函数把字符串解析成多个变量。其作用就是解析字符串并注册成变量,在注册变量之前不会验证当前变量是否存在,所以直接覆盖掉已有变量
注意:当
magic_quotes_gpc = On
,那么在
parse_str()
解析之前,变量会被
addslashes()
转换(也就是会被转义,加反斜线)。
parse_str(string[,array])
,两个参数分别为:
-
string
输入的字符串。如果
是str
传递入的查询字符串(URL
),则将它解析为变量并设置到当前作用域(如果提供了query string
array
则会设置到该数组里)
注意,
,如果不设置该参数,该函数将失效php >= 7.2
- 一个数组,如果设置该参数, 变量将会以数组元素的形式存入到这个数组,作为替代
<?php
$str = "first=value&arr[]=foo+bar&arr[]=baz";
parse_str($str, $output);
echo $output['first']; // value
echo $output['arr'][0]; // foo bar
echo $output['arr'][1]; // baz
?>
<?php
error_reporting(0);
if(
empty($_GET['id'])) {
show_source(__FILE__);
die();
} else {
include (‘./flag.php’);
$a = “https://www.cnblogs.com/wjrblogs/”;
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)) {
echo $flag;
} else {
exit(‘其实很简单其实并不难!’);
}
}
?>
利用
php
的
hash
比较缺陷(
PHP
在处理哈希字符串时,会利用
!=
或
==
来对哈希值进行比较,它把每一个以
0E
开头的哈希值都解释为
,所以如果两个不同的密码经过哈希以后,其哈希值都是以
0E
开头的,那么PHP将会认为他们相同,都是
),所以,只需要找一个字符串哈希后以
OE
开头即可。
最终答案:
127.0.0.1/blfg.php?id=a[0]=s878926199a
import_request_variables()
import_request_variables()
函数将
GET/POST/Cookie
变量导入到全局作用域中,当禁止了
register_globals
,但又想用到一些全局变量,那么此函数就很有用
import_request_variables(string[,prefix])
- 指定导入哪些变量为全局变量,可以用字母
、G
P
分别表示导入C
GET
POST
中的变量。这些字母不区分大小写,所以你可以使用Cookie
g
p
的任何组合。c
包含了通过POST
方法上传的文件信息。注意这些字母的顺序,当使用POST
gp
变量将使用相同的名字覆盖POST
变量。任何GET
以外的字母都将被忽略GPC
-
参数作为变量名的前缀,置于所有被导入到全局作用域的变量之前prefix
<?php
$auth='0';
import_request_variables('G');
if($auth== 1){
echo"登陆成功";
}else{
echo"登陆失败";
}
?>
127.0.0.1/blfg.php?auth=1