PHP代码审计-变量覆盖

网络安全·代码审计 · 2023-09-10 · 610 人浏览

前置知识

可变变量

这是PHP的一个特性,允许一个变量中的内容作为变量名使用,作用上类似于其他的指针。

代码示例

$a = "Cat";
$b = "a";
$$b = "Dog";

echo $a;

运行结果:Dog

extract()函数

extract()函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
该函数返回成功设置的变量数目。
用PHP代码进行实现的话:

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

参考: 菜鸟教程

代码示例

$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";

运行结果:$a = Cat; $b = Dog; $c = Horse

场景

选取自BJDCTF2020-Mark loves cat

<?php
$flag = file_get_contents('/flag');

$yds = "dog";
$is = "cat";
$handsome = 'yds';

extract($_POST);

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

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

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

if($_POST['flag'] === 'flag'  || $_GET['flag'] === 'flag')
{
    exit($is);
}
echo "the flag is: ".$flag;
?>

解法一:绕过所有if

分析

我们阅读代码,发现最后输出了变量flag中的内容,要到达输出变量flag的语句,则需要绕过所有if。
观察每个if的条件,发现我们需要:

  1. GET传参中flag的值不能与其他键名称相同
  2. GET传参与POST传参中必须有一个flag键
  3. GET传参与POST传参中flag键的的值不能为flag
    这条件看起来很宽松啊,好像随便传个?flag=1就能拿到flag。但真的是这样吗?别忘记前面还有一段代码:

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

    这段代码对变量进行了覆盖,如果我们在POST传参里面传递了flag=1,那么就会执行:$flag = '1'
    如果在GET传参里面传递了flag=1,那么就会执行:$flag = $1,而$1为空,所以$flag会被置空。
    这可有点棘手了,POST传递flag的话$flag都会被覆盖,GET传递flag=flag的话,违反了第三个if的条件。
    那么我通过GET传递flag=a&a=flag,在处理GET参数时不就相当于执行了$flag = ${$a},也就是$flag = $flag,这样就不会影响到$flag中的值了,但是第一个if限制了我们这样操作。
    似乎现有的思路都被堵住了,这该怎么办呢?

破局的方法:强比较

我们可以利用PHP强比较会进行类型比较的性质来绕过第一个if。
在解析GET与POST的参数中的键时,如果键为数字,那么PHP会将其识别为整型而不是字符串,而数字作为参数的值时,会将其识别为字符串。
利用这个特性就可以绕过第一个if。
Payload: ?1=flag&flag=1
注意这里的顺序不能乱,必须要先执行1=flag,然后执行flag=1,这样才不会导致flag被空变量1覆盖。
成功拿到flag。

解法二(最简):覆盖变量yds

通过将$yds覆盖为$flag的值,在执行第二个if的同时将$flag的值带出来,那么首先要做到的就是改变$yds的值为$flag的值。
那么只能通过GET传参进行变量覆盖。
Payload: ?yds=flag
这条Payload使得程序在对GET参数进行处理时将$yds覆盖为$flag的值,同时这条Payload能不触发第一条if,但是会触发第二条if,从而将$yds中的内容进行输出,成功拿到flag。

其他解法

当然也存在其他解法,也就是通过覆盖$handsome$is,但是这些方法就留给读者们去自行尝试。

绕过 PHP 代码审计 变量覆盖 610 Views
本站已在互联网运行了 Theme Jasmine by Kent Liao