【WEB】代码审计|变量覆盖漏洞

0x00 背景

近期在研究学习变量覆盖漏洞的问题,于是就把之前学习的和近期看到的CTF题目中有关变量覆盖的题目结合下进一步研究。

通常将可以用自定义的参数值替换原有变量值的情况称为变量覆盖漏洞。经常导致变量覆盖漏洞场景有:$$使用不当,extract()函数使用不当,parse_str()函数使用不当,import_request_variables()使用不当,开启了全局变量注册等。

本篇收集了几个CTF中的题目作为例子,对$$,extract(),parse_str()的问题进行总结。

0x01 $$导致的变量覆盖问题

$$ 导致的变量覆盖问题在CTF代码审计题目中经常在foreach中出现,如以下的示例代码,使用foreach来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的键值作为变量的值。因此就产生了变量覆盖漏洞。请求?name=test 会将$name的值覆盖,变为test。

<?php
//?name=test
//output:string(4) "name" string(4) "test" string(4) "test" test
$name='thinking';
foreach ($_GET as $key => $value)
    $$key = $value;
    var_dump($key);
    var_dump($value);
    var_dump($$key);
echo $name;
?>

CTF中$$导致的变量覆盖问题的例题1

题目源码:

<?php
include "flag.php";
$_403 = "Access Denied";
$_200 = "Welcome Admin";
if ($_SERVER["REQUEST_METHOD"] != "POST")
	//这里限制了一定要用post方法传递
    die("BugsBunnyCTF is here :p...");
if ( !isset($_POST["flag"]) )
	//这里限制了post方法传递的值一定要有flag这个变量
    die($_403);
foreach ($_GET as $key => $value)
	//这里的理解应该是:$_GET返回的是一个数组
	//数组中的每一个变量名作为$key(键),每一个变量对应的值作为$value(值)
	//此处$key=(get方法传进来的参数名),$value=(get方法传进来的参数值)
	//这里如果传入?_200=flag,那么$key=_200,$$key=$_200
	//$value=flag,$$value=$flag(这里就把flag变量的值传递给了_200)
    $$key = $$value;
foreach ($_POST as $key => $value)
	//把post的值赋值给以post提交的变量名
    $$key = $value;
if ( $_POST["flag"] !== $flag )
	//这里要是post提交的变量名是flag就可以绕过
    die($_403);
echo "This is your flag : ". $flag . "\n";
die($_200);//把flag的值传递给了_200,这相当于打印出了flag 
?>

题目分析:

源码包含了flag.php文件,并且需要满足3个if里的条件才能获取flag,题目中使用了两个foreach并且也使用了$ $ .两个foreach中对 $ $ key的处理是不一样的,满足条件后会将$ flag里面的值打印出来,所以$flag是在flag.php文件文件中的。

但是由于第7,11-14行间的代码会将$ flag的值给覆盖掉了,所以需要先将$ flag的值赋给$ _200或$ _403变量,然后利用die($ _200)或 die($_403)将flag打印出来。

解题方法:

由于第7,11-14行间的代码会将$ `flag的值给覆盖掉,所以只能利用第一个foreach先将$ flag的值赋给$ _200,然后利用die($ _200)将原本的flag值打印出来。

最终PAYLOAD:

本地复现,所以flag与原题不一样

GET DATA:?_200=flag

POST DATA:flag=aaaaaaaaaaaaaaaaaaaaa
在这里插入图片描述

0x02 extract()函数导致的变量覆盖问题

extract() 该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。

extract()的用法参考:http://www.runoob.com/php/func-array-extract.html
(简单来说就是用数组中的键创建变量)
语法: extract(array,extract_rules,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 与其它任何标志结合使用。

如果没有指定 flags,则被假定为 EXTR_OVERWRITE。

prefix
注意 prefix 仅在 flags 的值是 EXTR_PREFIX_SAME,EXTR_PREFIX_ALL,EXTR_PREFIX_INVALID 或 EXTR_PREFIX_IF_EXISTS 时需要。如果附加了前缀后的结果不是合法的变量名,将不会导入到符号表中。前缀和数组键名之间会自动加上一个下划线。

CTF中extract()导致的变量覆盖问题的例题1

题目源码:

<?php
$flag = 'xxx';
extract($_GET);
if (isset($gift)) {
    $content = trim(file_get_contents($flag));
    if ($gift == $content) {
        echo 'hctf{...}';
    } else {
        echo 'Oh..';
    }
} 
?>

题目分析:

题目使用了extract($_GET)接收了GET请求中的数据,并将键名和键值转换为变量名和变量的值,然后再进行两个if 的条件判断,所以可以使用GET提交参数和值,利用extract()对变量进行覆盖,从而满足各个条件。

解题方法:

GET请求 ?flag=&gift=,extract()会将$ flag和$ gift的值覆盖了,将变量的值设置为空或者不存在的文件就满足$gift == $content。

最终PAYLOAD:

GET DATA:?flag=&gift=

CTF中extract()导致的变量覆盖问题的例题2

题目源码:

<?php if ($_SERVER["REQUEST_METHOD"] == "POST") { ?>
      <?php
        extract($_POST);
          if ($pass == $thepassword_123) { ?>
           <div class="alert alert-success">
                <code><?php echo $theflag; ?></code>
            </div>

        <?php } ?>
    <?php } ?>

题目分析:

题目要求使用POST提交数据,extract($ _POST)会将POST的数据中的键名和键值转换为相应的变量名和变量值,利用这个覆盖$ pass和$ thepassword_123变量的值,从而满足$ pass == $ thepassword_123这个条件。

解题方法:

使用POST请求提交pass=&thepassword_123=, 然后extract()会将接收到的数据将$ pass和$ thepassword_123变量的值覆盖为空,便满足条件了。

最终PAYLOAD:

POST DATA:pass=&thepassword_123=

0x03 parse_str函数导致的变量覆盖问题

parse_str() 函数用于把查询字符串解析到变量中,如果没有array 参数,则由该函数设置的变量将覆盖已存在的同名变量。

语法:parse_str(string,array)

003.png

parse_str() 用法参考:http://php.net/parse_str

CTF中parse_str()导致的变量覆盖问题的例题1

题目源码:

<?php
error_reporting(0);
if (empty($_GET['id'])) {
    show_source(__FILE__);
    die();
} else {
    include ('flag.php');
    $a = "www.OPENCTF.com";
    $id = $_GET['id'];
    @parse_str($id);
    if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
       echo $flag;
    } else {
        exit('其实很简单其实并不难!');
   }
}
?> 

题目分析:

首先要求使用GET提交id参数,然后parse_str($ id)对id参数的数据进行处理,再使用判断$ a[0] != ‘QNKCDZO’ && md5($ a[0]) == md5(‘QNKCDZO’)的结果是否为真,为真就返回flag,md5(‘QNKCDZO’)的结果是0e830400451993494058024219903391由于此次要满足$ a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)所以要利用php弱语言特性,0e123会被当做科学计数法,0 * 10 x 123。所以需要找到一个字符串md5后的结果是0e开头后面都是数字的,如,240610708,s878926199a

PHP处理0e开头md5哈希字符串缺陷/bug 参考:https://blog.csdn.net/weixin_45844670/article/details/108115517#PHP_1

解题方法:

使用GET请求id=a[0]=240610708,这样会将a[0]的值覆盖为240610708,然后经过md5后得到0e462097431906509019562988736854与md5(‘QNKCDZO’)的结果0e830400451993494058024219903391比较都是0 所以相等,满足条件,得flag。

最终PAYLOAD:

GET DATA:

?id=a[0]=s878926199a

or

?id=a[0]=240610708

0x04 小总结

变量覆盖漏洞在PHP代码审计中会以比较隐晦的方式存在,所以需要更加仔细的阅读源码找出漏洞的点,在CTF里面经常是以比较直接方式展示,所以可以先通过学习CTF各种变量覆盖的题目,然后掌握后再去审计cms,这样可以更加通透的理解掌握和挖掘变量覆盖漏洞。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值