变量覆盖$$ extract()parse_str()mb_parse_str()import_request_variables()register_globals=On实例-MetInfo v5.1.4 ($$+foreach)
变量覆盖
变量覆盖指的是用我们自定义的参数值替换程序原有的变量值,一般变量覆盖漏洞需要结合程序的其它功能来实现完 整的攻击。经常导致变量覆盖漏洞场景有:
-
$$
-
extract()
-
parse_str()
-
import_request_variables()
-
全局变量注册
$$
demo.php
<?php
$var='hello';
//$$代表可变变量
$$var='world'; //$hello='world'
echo $var.'</br>';
echo $$var.'</br>';
echo $hello;
?>
运行结果:
extract()
(PHP 4, PHP 5, PHP 7, PHP 8)
extract — 从数组中将变量导入到当前的符号表
语法:extract(array,extract_rules,prefix)
-
array 必需
-
extract_rules 可选
可选的值有:
-
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 可选
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。该函数返回成功设置的变量数目。
demo.php
<?php
$var="don9";
extract($_GET);
if($var == "sec"){
echo $var;
}
else echo $var;
?>
结果:
覆盖前.png
覆盖后.png
demo.php (设置extract_rules)
<?php
$var="don9";
extract($_GET,EXTR_SKIP);
if($var == "sec"){
echo $var;
}
else echo $var;
?>
结果:
覆盖失败, 因为设置extract_rules为 EXTR_SKIP后,如果变量名有冲突,不会覆盖已有的变量。
parse_str()
把查询字符串解析到变量中。语法:parse_str(string,array)
-
string为必选
-
array为可选
-
如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。
demo.php(无参数array)
<?php
parse_str("ID=don9sec&AGE=2");
echo $ID.'</br>';
echo $AGE;
?>
结果:
该函数将ID和AGE初始化成了变量,且值为等号右边的值。
demo.php (有参数array)
<?php
parse_str("ID=don9sec&AGE=2",$secArray);
echo $ID.'</br>';
echo $AGE;
print_r($secArray)
?>
结果:
可以看到,有参数array时,ID和AGE并没有被初始化为全局变量。
mb_parse_str()
(PHP 4 >= 4.0.6, PHP 5, PHP 7, PHP 8)
解析 GET/POST/COOKIE 数据并设置全局变量。
语法:mb_parse_str ( string , array)
-
string为必选
-
array为可选
-
如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。
demo.php(无参数array)
<?php
parse_str("ID=don9sec&AGE=2");
echo $ID.'</br>';
echo $AGE;
?>
结果:
可以看见,函数在没有设置参数array时,将id和age初始化为变量并赋值。
demo.php (有参数array)
<?php
$str = 'id=don9sec&age=2';
mb_parse_str($str,$result);
echo $id.'</br>';
echo $age.'</br>';
print_r($result);
结果:
如报错信息所示,设置参数array后,id和age并没有被初始化为全局变量。
import_request_variables()
版本要求:PHP 4 >= 4.1.0, PHP 5 < 5.4.0
将 GET/POST/Cookie 变量导入到全局作用域中(相当于开启全局变量注册), 所以如果禁止了 register_globals,但又想用到一些全局变量,则可以使用该函数。语法:import_request_variables(string $types ,string $prefix )
-
types为必选
指定需要导入的变量,可以用字母 G、P 和 C 分别表示 GET、POST 和 Cookie,这些字母不区分大小写,所以你可以使用 g 、 p 和 c 的任何组合。POST 包含了通过 POST 方法上传的文件信息。注意这些字母的顺序,当使用 gp 时,POST 变量将使用相同的名字覆盖 GET 变量。任何 GPC 以外的字母都将被忽略。
-
prefix为可选
demo.php
<?php
$var = "don9";
// 此处将导入 GET 变量
import_request_variables("G");
if($var == "sec"){
echo $var;
}
else echo $var;
?>
结果:
覆盖前.png
覆盖后.png
register_globals=On
全局变量注册,当在php.ini开启register_globals= On时,代码中的参数会被用户提交的参数覆盖掉。
本特性已自 PHP 5.3.0 起废弃并将自 PHP 5.4.0 起移除。
实例-MetInfo v5.1.4 ($$+foreach)
漏洞入口
/about/index.php
/case/index.php
/download/index.php
/job/index.php
/product/index.php
/news/index.php
这里以/news/index.php为例
<?php
$filpy = basename(dirname(__FILE__));
$fmodule=2;
require_once '../include/module.php';
require_once $module;
?>
跟进/include/module.php
<?php
require_once 'common.inc.php';
跟进/common.inc.php
使用foreach来遍历数组中的值作为变量,经典的$$变量覆盖案例代码, 利用示例
确定漏洞点后开始回溯,继续跟进/include/module.php
首先$fmodule和$module在/about/index.php获得一个初始值
当fmodule不等于7时,满足条件,$module会被初始化(/include/module.php 78行);当fmodule等于7时,module就不会被初始化,且module又是可控的,此时便可以通过
require_once '../include/module.php';
require_once 'common.inc.php';
两次文件包含使用$_request来获取GET传递的新fmodule值实现变量覆盖。
如:
为了方便演示该漏洞
设置php.ini
-
allow_url_fopen = On //允许打开远程文件
-
allow_url_include = On //允许include/require远程文件
参考:
https://y4er.com/post/variable-coverage/
----------分割线----------
近期状态:
前俩月:不是在hw就是在hw的路上
近一月:期末考试/减肥/秋招简历
9月前:
- web/内网填坑(准备秋招)
- 把最近的笔记贴上(总结+经验)
- 代码审计(多屯几个0day)
- 公众号回到正轨
- ...
ps:
欢迎一'点'三连(点分享+点赞+点在看)
本文始发于微信公众号(don9sec):PHP代码审计-变量覆盖
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论