1. 程式人生 > 實用技巧 >wordpress外掛wpdiscuz任意檔案上傳漏洞分析

wordpress外掛wpdiscuz任意檔案上傳漏洞分析

下載連結

https://downloads.wordpress.org/plugin/wpdiscuz.7.0.3.zip

測試

環境

  • windows phpstudy
  • phpstorm

解壓外掛到wp-content/plugins目錄下

登入管理員啟用外掛,這時文章下面評論區

上傳帶GIF頭的php檔案

可以看到上傳成功,圖片地址也在回顯中

http://127.0.0.1/wordpress/wp-content/uploads/2020/08/1-1598075857.9448.php

分析

wp-setting.php 將wp_ajax_nopriv_wmuUploadFiles寫入了$wp_filter

foreach ( wp_get_active_and_valid_plugins() as $plugin ) {
   wp_register_plugin_realpath( $plugin );
   include_once $plugin;
   /**
    * Fires once a single activated plugin has loaded.
    *
    * @since 5.1.0
    *
    * @param string $plugin Full path to the plugin's main file.
    */
   do_action( 'plugin_loaded', $plugin );
}

通過表單action引數外掛載入

$action = ( isset( $_REQUEST['action'] ) ) ? $_REQUEST['action'] : '';

wordpress先載入外掛

入口檔案

上傳的關鍵程式碼在wp-content\plugins\wpdiscuz\utils\class.WpdiscuzHelperUpload.php376行

public function uploadFiles() {
……
require_once(ABSPATH . "wp-admin/includes/image.php");
foreach ($files as $file) {
$error = false;
$extension = pathinfo($file["name"], PATHINFO_EXTENSION); //$extension="php"
$mimeType = $this->getMimeType($file, $extension);

檢測檔案mime型別

private function getMimeType($file, $extension) {
    $mimeType = "";
    if (function_exists("mime_content_type")) {
        $mimeType = mime_content_type($file["tmp_name"]); //image/gif
    } elseif (function_exists("finfo_open") && function_exists("finfo_file")) {
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mimeType = finfo_file($finfo, $file["tmp_name"]);
    } elseif ($extension) {
        $matches = wp_check_filetype($file["name"], $this->options->content["wmuMimeTypes"]);
        $mimeType = empty($matches["type"]) ? "" : $matches["type"];
    }
    return $mimeType;
}

繼續跟進

……
if ($this->isAllowedFileType($mimeType)) {
    if (empty($extension)) {   //$extension="php" 沒進入
        ……
     }
    $file["type"] = $mimeType;
} else {
    ……
}
do_action("wpdiscuz_mu_preupload", $file);  //沒什麼用
if (!$error) {
    $attachmentData = $this->uploadSingleFile($file);

isAllowedFileType

private function isAllowedFileType($mimeType) {
    $isAllowed = false;
    if (!empty($this->options->content["wmuMimeTypes"]) && is_array($this->options->content["wmuMimeTypes"])) {
        $isAllowed = in_array($mimeType, $this->options->content["wmuMimeTypes"]);
    }
    return $isAllowed;
}

$this->options->content["wmuMimeTypes"] 內容

在陣列內

$isAllowed=true

上傳操作在

340行 $attachmentData = $this->uploadSingleFile($file);

uploadSingleFile

private function uploadSingleFile($file) {
$currentTime = WpdiscuzHelper::getMicrotime();
$attachmentData = [];
$path = $this->wpUploadsPath . "/";
$fName = $file["name"];
$pathInfo = pathinfo($fName);
$realFileName = $pathInfo["filename"];
$ext = empty($pathInfo["extension"]) ? "" : strtolower($pathInfo["extension"]);
$sanitizedName = sanitize_file_name($realFileName); 
$cleanFileName = $sanitizedName . "-" . $currentTime . "." . $ext;
$cleanRealFileName = $sanitizedName . "." . $ext;
$fileName = $path . $cleanFileName;
if (in_array($ext, ["jpeg", "jpg"])) {
$this->imageFixOrientation($file["tmp_name"]);
}
$success = apply_filters("wpdiscuz_mu_compress_image", false, $file["tmp_name"], $fileName, $q = 60);
if ($success || @move_uploaded_file($file["tmp_name"], $fileName)) {

到這裡已經把檔案上傳了
上傳檔名為原檔名+時間戳+字尾

v7.0.5的改進

isAllowedFileType 對mime的字尾和檔案字尾進行比較

private function isAllowedFileType($mimeType, $extension) {
    $isAllowed = false;
    if (!empty($this->mimeTypes) && is_array($this->mimeTypes)) {
        foreach ($this->mimeTypes as $ext => $mimes) {
            if ($ext === $extension) {
                if ($isAllowed = in_array($mimeType, explode("|", $mimes))) {
                    break;
                }
            }
        }
    }
    return $isAllowed;
}

總結

整個上傳過程只對檔案頭進行檢測,並沒有對檔案字尾進行檢測,導致可以設定一句話木馬內容為

圖片檔案頭+php程式碼

,進行getshell