变量覆盖漏洞

来自:

[BJDCTF2020]Mark loves cat

开始打开就是一个js/css页面,源码看不到东西,没有其他入口,像这种题,基本上是源码泄露。

dirsearch一扫发现一堆/.git,应该就是git源码泄露,我们直接githack下载:

(注,此处我开始下不下来,搜了下问题应该在于扫的太快了导致给我拒了,把里面一个self.thread_count的值从10改到1,其实速度没有多慢,就能下载下来了。

 

打开flag.php,果不其然啥也没有,就告诉你一个flag的路径在根目录:

打开index.php,开始也没找到,翻到最后找到关键源码:

搜了一下,这里应该属于是变量覆盖漏洞,从这$$就可以看出来。

但令我震惊的是,这道题有四种解法,厚礼谢!!!

我们进行一下代码审计,第一个foreach就是POST传参可以给你整成变量,使得$键 = $值的值。
第二个foreach就是GET传参,并且键和值都引用了两次,相当于我们可以设计两层,第一层是内部的一个属性,第二层为flag,这样就把flag的值给赋到$$x上去了。

第三个foreach是新的过滤条件:不能flag的值等于某个键名,并且它还要求那个键值是flag,不然就exit了,乍一看挺矛盾,先搁着。

第四个是if判断条件,如果我们把GET/POST都传了flag的参,就会exit。

第五个也是if判断条件,如果我们GET或者POSY传的flag的参值就是flag字符串的话,就会exit。

看起来是要把这个东西都绕过才会执行到最后一行的echo出flag。

但你别忘了,exit本身就可以作为一种输出,所以可以直接利用这个exit把flag给套出来。

 

解法一

看到第三个foreach:

foreach($_GET as $x => $y){
    if($_GET['flag'] === $x && $x !== 'flag'){
        exit($handsome);
    }

如果我们不去绕过它,而是满足它的条件,然后触发这个exit($handsome),需要怎么做?

我们传的payload需要:

?flag=(变量a)&(变量a)=flag

看起来逻辑有点矛盾,但是传参的时候它不会考虑这些。

 

我们把$_GET['flag']替换一下,就发现逻辑通了:

a===a & a!=='flag',这不就是true了吗?!!

如果我们令?flag=a&a=flag,那么函数判定到这里时,第一个条件flag的值是a,但与此同时a的值不是'flag'字符串,有点指针的意思了。

所以就会执行到exit($handsome),我们就能知道最终目的是让$handsome=$flag。

哪里有赋值给$handsome的方法呢?

 

我们再看到第二个foreach:

foreach($_GET as $x => $y){
    $$x = $$y;
}

试想,如果我们让$x=handsome,$y=flag,这时就会出现$$x=$handsome,$$y=$flag,就达到了把flag爆出来的效果。

payload1:

<url>?handsome=flag&flag=a&a=flag

payload2(来自其他师傅,太高了):

<url>?handsome=flag&flag=handsome

可以看作等效精简了a变量。

 

输入payload后查看源码得到flag。

 

解法二

我们看到这个条件:

if(!isset($_GET['flag']) && !isset($_POST['flag'])){
    exit($yds);
}

意思就是只要我们GET/POST都不设置flag了,就直接进exit($yds),而属性里有个$handsome='yds'。

想通了就会发现,太简单了,不让我们设置那就不设置呗,目的是$yds=$flag,那就是yds=flag

那不就结束了吗,直接传:

<url>?yds=flag

成功。(不一定只有GET/POST这种才能传参哦,只要有这个属性都可以传这个属性的值)

 

解法三

看到第二个判断条件:

if($_POST['flag'] === 'flag'  || $_GET['flag'] === 'flag'){
    exit($is);
}

嗯?GET/POST只要传了flag的参=flag就直接exit($is)???

那不就直接出来了?

<url>?flag=flag&is=flag

简简单单~~

 

解法四

最难做的一个方法,就是真的把所有的条件都绕过,使用最后的echo给套出flag。

这里本人能力不足,原博客:https://blog.csdn.net/weixin_44895005/article/details/123721901

强类型比较绕过
我在尝试的时候发现有个问题:

正常情况

http://127.0.0.1/CTF/?a=flag&flag=a 这里触发了 if($_GET['flag'] === $x && $x !== 'flag'){ //不能同时 $_GET['flag'] 的值等于某个键名,那个键名又不是flag 这个条件,进入了 exit($handsome);里面了,这很好理解的。函数遍历 a=flag 的时候, $_GET['flag'] === $x && $x !== 'flag' --> a === a && a !== 'flag' 这就进来了 true && true 就进来了, 然后 exit($handsome);

 

不正常的情况

http://127.0.0.1/CTF/?1=flag&flag=1 这是啥情况呢,按道理来说跟上面一样啊,这里触发了 if($_GET['flag'] === $x && $x !== 'flag'){ //不能同时 $_GET['flag'] 的值等于某个键名,那个键名又不是flag 这个条件,进入了 exit($handsome);里面了,这很好理解的。函数遍历 1=flag 的时候, $_GET['flag'] === $x && $x !== 'flag' --> 1 === 1 && 1 !== 'flag' 这就进来了 true && true 就进来了, 然后 exit($handsome);但是事实不是这样的,我估计是涉及到 字符串和数字的问题,都打印出来看看。 

 

打印代码:

foreach($_GET as $x => $y){
 
echo "</br>foreach :".'</br>';
 
echo '$_GET[\'flag\'] :';
 
var_dump($_GET['flag']).'</br>';
 
echo '$x :';
 
var_dump($x).'</br>';
 
echo '$_GET[\'flag\'] === $x :';
 
var_dump($_GET['flag'] === $x).'</br>';
 
echo '$x !== \'flag\' :';
 
var_dump($x !== 'flag').'</br>';
 
if($_GET['flag'] === $x && $x !== 'flag'){ //不能同时 1 === 1 && 1 !== 'flag' flag的值等于某个键名,键名又不是flag
 
exit($handsome);
 
}
 
}

 

果然啊,get 传参数的时候,如果传入1,默认:做为键:类型是 int; 做为值,类型是 string

问题就这这里了:$_GET['flag'] === $x : 如果传入 a=flag&flag=a 那么 判断 a=flag 的时候 $_GET['flag']a, &xa,完全相等 进入if条件, 而如果传入 1=flag&flag=1 那么判断 1=flag的时候 $_GET['flag'](string)1&x(int)1 不完全相等,无法进入if条件,进而绕过

这样就绕过好了,满足了大佬说的几句话,不会改变 flag 而且不会提前结束,具体其他几个循环自己打印看看就行。

这个题我是没找到 绕过判断不提前结束 ,而又不改变flag值的情况下到达最后的echo。


还有一个可能的解法(没试,不知道),传入 $$$xx=$$xx,或者 更多的 $$$$$$$x=$xx, 来绕过 变量覆盖,这里记一下,变量覆盖可以多次递归(没实验过)

posted @ 2023-09-18 12:10  Eddie_Murphy  阅读(16)  评论(0编辑  收藏  举报