变量覆盖总结

April 1, 2019 WEB安全 访问: 25 次

常见类型

由$$变量赋值引发的覆盖

在php中,$$是一种可变变量的写法,他可以使一个普通变量的值作为可变变量的名字。如:

<?php
$a=0;
echo "第一次a:".$a;
echo "<br>";
$b=$_GET['key'];
$$b=$_GET['value'];
echo "第二次a:".$a;
?>

若不给参数,那么两次a都等于0,倘若$_GET['key']的值是a的话,那么$$b就等于$a,倒数第二句话会将$_GET['value']的值覆盖掉
看一道CTF题:

<?php
include "flag";
$fl4g = "flag{this_is_wrong_flag!}";
foreach ($_POST as $key => $value) {
    # code...
    $a = $value;
    $$$key=$value;
    $yum=$_GET['flag'];
    if($yum=="radish")
    {
        if($ccut=="flag")
        {
            echo $fl4g;
        }else
        {
            echo "你还年轻!";
        }
    }else
    {
        echo "nonono";
    }
}
highlight_file(__FILE__);
?>

这道题目的关键就是利用$$来变量覆盖掉$ccut的值,根据题意得出payload:

$$ 引起的任意命令执行

该题目是在2018年ISCC线上赛的一个web题

<?php
include "flag.php";
$a=@$_REQUEST['a'];
@eval("var_dump($$a);");
show_source(__FIlE__);
?>

我想比赛方原本是要考察双$变量覆盖的点

但是忽略了双$还可以引发任意命令执行,原因可以看官方手册
使用方法:

http://127.0.0.1/?a={php代码}

extract函数使用不当导致的变量覆盖

官方解释

extract
(PHP 4, PHP 5, PHP 7)
extract — 从数组中将变量导入到当前的符号表
说明
extract ( array &$array [, int $flags = EXTR_OVERWRITE [, string $prefix = NULL ]] ) : int
本函数用来将变量从数组中导入到当前的符号表中。
检查每个键名看是否可以作为一个合法的变量名,同时也检查和符号表中已有的变量名的冲突。
参数
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 与其它任何标志结合使用。
如果没有指定 flags,则被假定为 EXTR_OVERWRITE。
prefix
注意 prefix 仅在 flags 的值是 EXTR_PREFIX_SAME,EXTR_PREFIX_ALL,EXTR_PREFIX_INVALID 或 EXTR_PREFIX_IF_EXISTS 时需要。 如果附加了前缀后的结果不是合法的变量名,将不会导入到符号表中。前缀和数组键名之间会自动加上一个下划线。
返回值
返回成功导入到符号表中的变量数目。

看了官方对这个函数的解释就可以理解这个函数是干什么用的,该函数的第二个参数默认是EXTR_OVERWRITE(如果有冲突,覆盖已有的变量。),那么这就引起了变量覆盖的问题
CTF题:

<?php
include("fl4g.php");
show_source(__FILE__);
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    extract($_POST);
    if ($pass == $thepassword) {
        echo $flag;
     }
 }

首先看到这段代码中extract函数没有添加第二个参数,那么使用的就是默认参数EXTR_OVERWRITE,这就很容易的构造出使if语句满足条件的payload

全局变量的覆盖

在php.ini中registerglobals的值位ON的前提下,如果谋陷变量没有被初始化,并且可以被用户直接控制的话,就存在变量覆盖的漏洞。
测试代码

<?php
include "flag.php";
echo (int)ini_get("register_globals");
if($a=="radish")
{
    echo $flag;
}
show_source(__FIlE__);
?>

首先输出了register_globals的值,发现为1,说明可以对未初始化的变量进行赋值

那么根据if条件得出payload

在php.ini中registerglobals的值位ON的前提下,,也可以通过$GLOBALS获取的变量在使用不当时也会导致变量覆盖,测试代码还是上面的代码

parse_str函数使用不当导致的覆盖

官方解释:

parse_str
(PHP 4, PHP 5, PHP 7)
parse_str — 将字符串解析成多个变量
说明
parse_str ( string $encoded_string [, array &$result ] ) : void
如果 encoded_string 是 URL 传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域(如果提供了 result 则会设置到该数组里 )。
参数
encoded_string
输入的字符串。
result
如果设置了第二个变量 result, 变量将会以数组元素的形式存入到这个数组,作为替代。
返回值
没有返回值。

同样,若不指定第二个参数,则会导致任意变量覆盖
CTF题:

<?php
include "flag.php";
$radish="wxm";
parse_str($_GET["flag"]);
if($radish=="radish")
{
    echo $flag;
}
show_source(__FIlE__);
?>

看到parse_str函数没有第二个参数,那么就可以构造payload覆盖掉$radish的值为“radish”

importrequestvariables所导致的变量覆盖

官方解释:

import_request_variables
(PHP 4 >= 4.1.0, PHP 5 < 5.4.0)
import_request_variables — 将 GET/POST/Cookie 变量导入到全局作用域中
说明:
import_request_variables ( string $types [, string $prefix ] ) : bool
将 GET/POST/Cookie 变量导入到全局作用域中。如果你禁止了 register_globals,但又想用到一些全局变量,那么此函数就很有用。
你可以使用 types 参数指定需要导入的变量。可以用字母‘G’、‘P’和‘C’分别表示 GET、POST 和 Cookie。这些字母不区分大小写,所以你可以使用‘g’、‘p’和‘c’的任何组合。POST 包含了通过 POST 方法上传的文件信息。注意这些字母的顺序,当使用“gp”时,POST 变量将使用相同的名字覆盖 GET 变量。任何 GPC 以外的字母都将被忽略。
prefix 参数作为变量名的前缀,置于所有被导入到全局作用域的变量之前。所以如果你有个名为“userid”的 GET 变量,同时提供了“pref_”作为前缀,那么你将获得一个名为 $pref_userid 的全局变量。

同样第二个参数是非常重要的,第二个参数是将导入到变量的时候名字前都加上变量二的值作为变量名,但是如果指定第二个参数的话,那么就会造成对前面的变量任意的覆盖
CTF题:

 <?php
include "flag.php";
import_request_variables("g");
if($radish=="radish")
{
    echo $flag;
}
show_source(__FIlE__);
?>

可以看到该函数没有设置第二个参数,那么就可以构造payload了

漏洞防御

  • 对用户可控的变量进行严格的过滤
  • 使用函数的时候要严格安照函数的使用方法来使用

添加新评论