PHP代码审计:变量覆盖
0x00 前提
本文是给作为给小伙伴分享教学的一篇文章,给讲一下,所以写的比较简陋。。。见谅而且都比较基础,只是简单讲课提纲吧
0x01 变量覆盖审计
0x00 简介
变量覆盖,顾名思义就是可以覆盖已有变量值,导致变量覆盖的漏洞
常见的造成的代码审计的情景是代码中出现以下关键词:
- register_globals=on
- extract()函数
- parse_str()函数
- import_request_variables()函数
- $$
0x01 变量覆盖演示
extract()
extract(array,extract_rules,prefix)
函数
https://www.runoob.com/php/func-array-extract.html
该函数可以从数组中将变量导入到当前的符号表,即将数组中的键值对注册成函数,使用数组键名作为变量名,使用数组键值作为变量值。
这里我们要注意一下该函数的第二个参数
- EXTR_OVERWRITE - 默认。如果有冲突,则覆盖已有的变量。
- EXTR_SKIP - 如果有冲突,不覆盖已有的变量。
这就为我们提供了覆盖的可能。
<?php
$a = 'a';
echo $a.'</br>';
extract($_GET);
echo $a
?>
可以看到我们初始变量值为a但是覆盖之后就变成了我们输入的值。
http://127.0.0.1/test/extract.php?a=123
修复:
在使用extract()函数时,可以指定将第二个参数设置为EXTRS_KIP
parse_str()
parse_str()
函数用于把查询字符串解析到变量中,如果没有array参数,则由该函数设置的变量将覆盖已存在的同名变量。
在没有array参数的情况下使用此函数,
并且在PHP 7.2中将废弃不设置参数的行为,此函数没有返回值。
<?php
$a = "giao";
echo "a:" . $a;
echo "<br>";
$b = $_GET['b'];
parse_str($b);
echo "a_2:" . $a;
?>
http://127.0.0.1/test/parse_str.php?b=a=zeo
$$
典型的例子就是foreach来遍历数组中的值作为变量。
$$是一种可变变量的写法,它可以使一个普通变量的值作为可变变量的名字,这种类型常常会使用遍历的方式来释放变量的代码
<?php
$a = 10;
echo $a;
echo "<br>";
foreach ($_POST as $k => $v){
$$k = $v;
echo $a;
}
?>
import_request_variables()
import_request_variables ( string $types , string $prefix )
https://www.runoob.com/php/php-import_request_variables-function.html
import_request_variables() 函数将 GET/POST/Cookie 变量导入到全局作用域中。该函数在最新版本的 PHP 中已经不支持。
import_request_variables() 函数将 GET/POST/Cookie 变量导入到全局作用域中。如果你禁止了 register_globals,但又想用到一些全局变量,那么此函数就很有用。
版本要求:PHP 4 >= 4.1.0, PHP 5 < 5.4.0
第二个参数$types:指定需要导入的变量,可以用字母 G、P 和 C 分别表示 GET、POST 和 Cookie,这些字母不区分大小写,所以你可以使用 g 、 p 和 c 的任何组合。POST 包含了通过 POST 方法上传的文件信息。注意这些字母的顺序,当使用 gp 时,POST 变量将使用相同的名字覆盖 GET 变量。任何 GPC 以外的字母都将被忽略。
全局变量
当你在升级PHP到PHP5.4及之后的版本的时候,是否发现register_global配置指令不再生效了呢
因为从PHP5.4开始register_global配置指令被移除了。
<?php
echo "register_globals: " . (int)ini_get("register_globals");
echo '<br>';
echo "a=" . $a;
?>
0x02 深x服edr实例
简化后的代码
<?php
#var_dump($_REQUEST);
$show_form = function($params) use(&$strip_slashes, &$show_input) {
extract($params);
$host = isset($host) ? $strip_slashes($host) : "127.0.0.1";
$path = isset($path) ? $strip_slashes($path) : "";
$row = isset($row) ? $strip_slashes($row) : "";
$limit = isset($limit) ? $strip_slashes($limit) : 1000;
// 绘制表单
echo "<pre>";
echo '<form id="studio" name="studio" method="post" action="">';
$show_input(array("title" => "Host ", "name" => "host", "value" => $host, "note" => " - host, e.g. 127.0.0.1"));
$show_input(array("title" => "Path ", "name" => "path", "value" => $path, "note" => " - path regex, e.g. mapreduce"));
$show_input(array("title" => "Row ", "name" => "row", "value" => $row, "note" => " - row regex, e.g. \s[w|e]\s"));
$show_input(array("title" => "Limit", "name" => "limit", "value" => $limit, "note" => " - top n, e.g. 100"));
echo '<input type="submit" id="button">';
echo '</form>';
echo "</pre>";
};
$show_form($_REQUEST);
?>
变量匿名函数 $show_form
具有一个形式参数 $params
在这里也就是array(“strip_slashes”=>“system”,“host”=>“id”);
接下来执行extract($params);,后进入如下代码:
$host = isset($host) ? $strip_slashes($host) : "127.0.0.1";
在这个过程中就产生了漏洞,想要了解具体原因:
首先函数传入参数值为array("strip_slashes"=>"system","host"=>"id");
经过extract()
函数后,赋值了2个变量:
$strip_slashes = 'system';
$host = 'id';
变量$host
利用三元运算重新赋值$strip_slashes($host)
而实际上其赋值内容是函数system('id')
的返回结果,这也就造成了命令执行漏洞。
0x03MetInfo实例
/include/common.inc.php
传入的cookie、get、post参数进行变量赋值
foreach(array('_COOKIE', '_POST', '_GET') as $_request) {
foreach($$_request as $_key => $_value) {
$_key{0} != '_' && $$_key = daddslashes($_value);
}
}
daddslashes()
防注入,不过并不影响
随便来到一个子文件看看他的加载方式\about\index.php
<?php
# MetInfo Enterprise Content Management System
# Copyright (C) MetInfo Co.,Ltd (http://www.metinfo.cn). All rights reserved.
$filpy = basename(dirname(__FILE__));
$fmodule=1;
require_once '../include/module.php';
require_once $module;
# This program is an open source system, commercial use, please consciously to purchase commercial license.
# Copyright (C) MetInfo Co., Ltd. (http://www.metinfo.cn). All rights reserved.
?>
这里使用了require_once函数包含了/include/module.php文件,可以发现这个文件又包含了common.inc.php文件
出现了两个未知变量:$module
,$fmodule
。我们可以用$fmodule
变量通过两次文件包含,使用$_request
来获取GET传递的新$fmodule
值实现变量覆盖。
上一篇: dataBinding无法生成binding类的原因
下一篇: 记录DataBinding使用