title: 9.16cms审计
date: 2022-09-16 20:28:36
tags:
这里面学到了一点
之前一直以为图片码的利用方式只有包含图片码才可以利用 (因为include函数会自动的把文件当作php文件来解析) 这里重点是当作php文件来解析 所以 只要能是图片被解析成php文件格式 就可以利用图片码
通过_empty函数
public function _empty($name)
{
$data = Hook::listen("user_request_empty", $name);
foreach ($data as $index => $datum) {
$this->view->assign($datum);
}
return $this->view->fetch('user/' . $name);
}
这里参数为name
这里进入 Hook::listen函数看一看
public static function listen($tag, &$params = null, $extra = null, $once = false)
{
$results = [];
//user_request_empty
foreach (static::get($tag) as $key => $name) {
//name为键值 tag为user_request_empty null null
$results[$key] = self::exec($name, $tag, $params, $extra);
// 如果返回 false,或者仅获取一个有效返回则中断行为执行
if (false === $results[$key] || (!is_null($results[$key]) && $once)) {
break;
}
}
return $once ? end($results) : $results;
}
经过分析返回为空、
但是无所谓
这里取模板渲染user/$name
$name是我们可控的所以我们可以包含一个图片把它当成php文件来执行
这里上传一个图片码
直接渲染这个图片码即可
这里有文件路径
看 api应用 common控制器
有个uplaod函数是上传功能的函数
public function upload()
{
Config::set('default_return_type', 'json');
//必须设定cdnurl为空,否则cdnurl函数计算错误
Config::set('upload.cdnurl', '');
$chunkid = $this->request->post("chunkid");
if ($chunkid) {
if (!Config::get('upload.chunking')) {
$this->error(__('Chunk file disabled'));
}
$action = $this->request->post("action");
$chunkindex = $this->request->post("chunkindex/d");
$chunkcount = $this->request->post("chunkcount/d");
$filename = $this->request->post("filename");
$method = $this->request->method(true);
// var_dump($action);
//var_dump($method);
if ($action == 'merge') {
$attachment = null;
//合并分片文件
try {
$upload = new Upload();
//进入merge函数 并且参数是可控的
$attachment = $upload->merge($chunkid, $chunkcount, $filename);
} catch (UploadException $e) {
$this->error($e->getMessage());
}
$this->success(__('Uploaded successful'), ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]);
} elseif ($method == 'clean') {
//删除冗余的分片文件
try {
$upload = new Upload();
$upload->clean($chunkid);
} catch (UploadException $e) {
$this->error($e->getMessage());
}
$this->success();
} else {
//phpinfo();
//上传分片文件
//默认普通上传文件
//上传文件
$file = $this->request->file('file');
try {
//实例化upload类
$upload = new Upload($file);
//进入uplaod chunk方法 参数都是可控的
$upload->chunk($chunkid, $chunkindex, $chunkcount);
} catch (UploadException $e) {
$this->error($e->getMessage());
}
$this->success();
}
} else {
$attachment = null;
//默认普通上传文件
$file = $this->request->file('file');
try {
$upload = new Upload($file);
$attachment = $upload->upload();
} catch (UploadException $e) {
$this->error($e->getMessage());
}
$this->success(__('Uploaded successful'), ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]);
}
}
}
$action = $this->request->post("action");
$chunkindex = $this->request->post("chunkindex/d");
$chunkcount = $this->request->post("chunkcount/d");
$filename = $this->request->post("filename");
这些参数都是可控的
先进入分片模式上传文件
进入chunk方法 这里面支队文件mime类型进行了判断 所以我们可以轻松绕过
然后进入
合并分片方法中合并图片 最后返回路径
因为这里对分片模式的检查不够严格所以我们可以绕过
dedecms
后台rce
漏洞代码在
dede/sys_verifies.php
这里面的
else if ($action == 'getfiles')
{
//phpinfo();
if(!isset($refiles))
{
ShowMsg("你没进行任何操作!","sys_verifies.php");
exit();
}
/*var_dump($action);
var_dump($refiles);*/
//var_dump($refiles);
$cacheFiles = DEDEDATA.'/modifytmp.inc';
var_dump($cacheFiles);
//"ac501d8e0834f8e5
// var_dump($tmpdir);
$fp = fopen($cacheFiles, 'w');
//<?php
//$tmpdir=""ac501d8e0834f8e5";
//$files['1']="<";
fwrite($fp, '<'.'?php'."\r\n");
fwrite($fp, '$tmpdir = "'.$tmpdir.'";'."\r\n");
$dirs = array();
$i = -1;
$adminDir = preg_replace("#(.*)[\/\\\\]#", "", dirname(__FILE__));
foreach($refiles as $filename)
{
//
//从第四个字符开始,返回直到倒数第三个
$filename = substr($filename,3,strlen($filename)-3);
if(preg_match("#^dede/#i", $filename))
{
$curdir = GetDirName( preg_replace("#^dede/#i", $adminDir.'/', $filename) );
} else {
$curdir = GetDirName($filename);
}
if( !isset($dirs[$curdir]) )
{
$dirs[$curdir] = TestIsFileDir($curdir);
}
$i++;
//$files['1']=""";
//
var_dump($filename);
fwrite($fp, '$files['.$i.'] = "'.$filename.'";'."\r\n");
}
fwrite($fp, '$fileConut = '.$i.';'."\r\n");
fwrite($fp, '?'.'>');
fclose($fp);
$dirinfos = '';
if($i > -1)
{
$dirinfos = '<tr bgcolor="#ffffff"><td colspan="2">';
$dirinfos .= "本次升级需要在下面文件夹写入更新文件,请注意文件夹<font color='red'>是否有写入权限:</font><br />\r\n";
foreach($dirs as $curdir)
{
$dirinfos .= $curdir['name']." 状态:".($curdir['writeable'] ? "[√正常]" : "<font color='red'>[×不可写]:请创建该目录</font>")."<br />\r\n";
}
$dirinfos .= "</td></tr>\r\n";
}
$doneStr = "<iframe name='stafrm' src='sys_verifies.php?action=down&curfile=0' frameborder='0' id='stafrm' width='100%' height='100%'></iframe>\r\n";
include(DEDEADMIN.'/templets/sys_verifies_getfiles.htm');
exit();
}
这个分支里面、
这个如果action为空的话
就包含这个html文件
进入这个html文件
这个可以控制action和filename
继续回到dede/sys_verifies.php这个页面
我们控制action为getfiles
然后由于我们是由源码的 不用具体的看
随便写个东西 只要他不回显 就没有进入这个if分支
还是这样var_dump直接显示出文件的路径
然后可以向文件中写入内容
其实我注释里面页写入了一些东西(分析的过程)、
这里它自动给写了
<?php
$tmpdir=""ac501d8e0834f8e5";
php文件头 我们只需要在里面写入shell就可以了
但是
这里框架做了全局过滤
然后我们看一下是怎么进项过滤的
我们写shell的时候就这个addslashes进行了过滤 因为我们要闭合上面的引号
但是这个函数对"进行了转义
但是 这里又有一行代码
$filename = substr($filename,3,strlen($filename)-3);
它竟然把前三个字符给截取掉了
不知道开发者写这个功能是干什么用的
是想截取掉我们上传的<?php ?
我想不通 不过不重要 这里我们可以写入
/"
然后经过addslashes函数过滤之后就会形成这个样子
它里面有把前三个字符截取掉了
所以这样经过截取前三个字符之后就会只剩下一个"这样我们就可以闭合双引号 拼接php代码了
直接拼接shell就可以了 因为这个不是php文件所以我们可以找到利用包含 让他当作php文件来解析就可
这里面就有包含这个文件
所以就可rce了
标签:审计,文件,name,request,16cms,upload,action,php From: https://www.cnblogs.com/kkkkl/p/16748377.html