最近CMS审了好几个,根据CNVD上公布的试图去审出1day,但是效果却不太好,自己觉得能找到的可能的漏洞点到最后都利用失败了,本想着先暂时放放,但还是不太甘心,就又看了看,重新找了个CMS,经过一番战斗,终于算是有结果了。
upload-getshell
根据CNVD披露的信息,确定该CMS后台是存在文件上传漏洞的,无非就是功能点上传,那就把后台能够进行文件上传的功能点对应的代码都审一遍。
用户管理->个人信息(fail)
在个人信息处能够上传个人头像,上传一张图片,同时抓包
admin/controller/Index.php#4032
首先会对请求方式做一个校验,之后调用request
方法来获取file
类的实例对象,可以看到这里写着上传的白名单;接着调用了file类
的validate
方法,跟进,代码就只有几行,发现只是设置了上传文件的规则;接着重点是调用的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;
}
从代码可以看到如若上传出错,会直接返回false
;接着会调用类中的isValid
方法对文件合法性进行检查,最主要的是调用的check方法,这里对文件后缀的校验白名单就来自前面$validate
数组,这里没有办法进行绕过
全局搜索了upload
相关的函数名,发现都做了白名单校验,直接上传行不通,那么就需要通过上传压缩包来达到getshell
的目的了,后面三个都是成功getshell的点,除了最后一个前面两个还挺简单的
关键搜索
在上传之前直接全局搜索和zip
相关的代码,看看存不存在对压缩包内容进行解压缩的方法,找到了三个函数,一处为uploadtheme
函数,刚好对应主题上传的功能点;另一处为upgrading
函数,最后一处为pluginlist
方法,先来看主题上传
系统设置->主题(success)
几个方法都是前面分析过的,所以上传压缩包肯定是没有问题的;之后会实例化ZipArchive
类,该类为PHP
的原生类,针对ZIP
压缩文件进行相关的操作;这里调用了ZipArchive
类中的open
方法,并且传递的参数为overwrit
e或者create
;之后会调用extractTo
方法,该方法将压缩文件解压缩到指定的目录,解压缩之后的路径为/runtime/transfer/theme/zip文件名
ZIPARCHIVE::CREATE (integer)
如果不存在则创建一个zip压缩包。
ZIPARCHIVE::OVERWRITE (integer)
总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖。
然后上传之后到指定的路径下去查看却没有发现解压缩之后的文件;结合前端代码,通过查看源代码定位原先默认的theme
路径,在同路径下找到了我们上传解压缩之后的文件夹,成功getshell
/public/theme/tt
网站相关->插件列表(success)
admin/controller/index.php#2649
首先调用checkUser
方法对用户的身份信息进行了验证, 只有管理员才能够进行相关操作
之后的代码逻辑跟上面getshell的差不多,就不多分析了,由于缺少插件存放的文件夹,所以会在根目录下自动创建存储的文件夹;上传之后的文件路径为/plugins/tt
,也是能够getshell的
系统设置->系统升级(success)
admin/controller/index.php#4140
,这几个上传的函数方法主主体部分都差不多,存储路径不太一样,都是遍历了上传的压缩包内容,之后调用file
类中的方法对文件后缀、大小等进行校验,校验符合白名单的就能够上传成功;这里上传成功之后,并没有解压缩操作,还是差了一步
经过全局搜索,定位到admin/controller/index.php#4168
,upgrading
方法, 猜测应该就是对上传的系统升级压缩包进行处理
首先会调用Catfish
类中的getPost
方法,跟进,由于传入的param
不为空,直接来看else
代码部分;由于$param=auto
,所以直接进入断点处的else,接着会调用Requsest
类中的has
方法对POST请求中是否有auto
参数进行判断,auto
参数可控,不传参直接返回false;这只会影响存储路径,继续往下
接着调用Catfish
类的get
方法获取更新文件的路径,跟进之后发现通过缓存来进行获取,这里猜测先通过上传压缩包,传递的post
数据包不变,直接调用upgrading
方法,就能够从上传缓存中获取到存储路径
下面就是调用ZipArchive
原生类对更新包进行解压缩操作了,那么这里也是能够利用成功的
先调用upgradepackage
方法上传,再调用upgrading
方法从缓存获取存储路径再进行解压缩
写在后面
经过一天的奋战,应该算是把文件上传getshell的点找齐了,还是得多审计呀,有些漏洞类型就审计的不是特别拿手,后续可能要去审计JAVA的CMS了。。。
对了,最后一处为什么shell会在根目录下,可以去下载官方的更新包,会发现更新包里的文件都是根目录下的关键代码文件夹,应该是替换掉进行升级操作,也就能解释我们上传的shell为什么会在根目录下存储了。
- 本文作者: joker
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/907
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!