某CMS快乐审计

最近CMS审了好几个,根据CNVD上公布的试图去审出1day,但是效果却不太好,自己觉得能找到的可能的漏洞点到最后都利用失败了,本想着先暂时放放,但还是不太甘心,就又看了看,重新找了个CMS,经过一番战斗,终于算是有结果了。

upload-getshell

根据CNVD披露的信息,确定该CMS后台是存在文件上传漏洞的,无非就是功能点上传,那就把后台能够进行文件上传的功能点对应的代码都审一遍。

用户管理->个人信息(fail)

在个人信息处能够上传个人头像,上传一张图片,同时抓包


根据上传的路径定位到代码位置

admin/controller/Index.php#4032

首先会对请求方式做一个校验,之后调用request方法来获取file类的实例对象,可以看到这里写着上传的白名单;接着调用了file类validate方法,跟进,代码就只有几行,发现只是设置了上传文件的规则;接着重点是调用的move方法,来看一下代码,有点长不好截图

  1. public function move($path, $savename = true, $replace = true)
  2. {
  3. // 文件上传失败,捕获错误代码
  4. if (!empty($this->info['error'])) {
  5. $this->error($this->info['error']);
  6. return false;
  7. }
  8. // 检测合法性
  9. if (!$this->isValid()) {
  10. $this->error = 'upload illegal files';
  11. return false;
  12. }
  13. // 验证上传
  14. if (!$this->check()) {
  15. return false;
  16. }
  17. $path = rtrim($path, DS) . DS;
  18. // 文件保存命名规则
  19. $saveName = $this->buildSaveName($savename);
  20. $filename = $path . $saveName;
  21. // 检测目录
  22. if (false === $this->checkPath(dirname($filename))) {
  23. return false;
  24. }
  25. // 不覆盖同名文件
  26. if (!$replace && is_file($filename)) {
  27. $this->error = ['has the same filename: {:filename}', ['filename' => $filename]];
  28. return false;
  29. }
  30. /* 移动文件 */
  31. if ($this->isTest) {
  32. rename($this->filename, $filename);
  33. } elseif (!move_uploaded_file($this->filename, $filename)) {
  34. $this->error = 'upload write error';
  35. return false;
  36. }
  37. // 返回 File 对象实例
  38. $file = new self($filename);
  39. $file->setSaveName($saveName)->setUploadInfo($this->info);
  40. return $file;
  41. }

从代码可以看到如若上传出错,会直接返回false;接着会调用类中的isValid方法对文件合法性进行检查,最主要的是调用的check方法,这里对文件后缀的校验白名单就来自前面$validate数组,这里没有办法进行绕过
全局搜索了upload相关的函数名,发现都做了白名单校验,直接上传行不通,那么就需要通过上传压缩包来达到getshell的目的了,后面三个都是成功getshell的点,除了最后一个前面两个还挺简单的

关键搜索

在上传之前直接全局搜索和zip相关的代码,看看存不存在对压缩包内容进行解压缩的方法,找到了三个函数,一处为uploadtheme函数,刚好对应主题上传的功能点;另一处为upgrading函数,最后一处为pluginlist方法,先来看主题上传

系统设置->主题(success)

几个方法都是前面分析过的,所以上传压缩包肯定是没有问题的;之后会实例化ZipArchive类,该类为PHP的原生类,针对ZIP压缩文件进行相关的操作;这里调用了ZipArchive类中的open方法,并且传递的参数为overwrite或者create;之后会调用extractTo方法,该方法将压缩文件解压缩到指定的目录,解压缩之后的路径为/runtime/transfer/theme/zip文件名

  1. ZIPARCHIVE::CREATE (integer)
  2. 如果不存在则创建一个zip压缩包。
  3. ZIPARCHIVE::OVERWRITE (integer)
  4. 总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖。

然后上传之后到指定的路径下去查看却没有发现解压缩之后的文件;结合前端代码,通过查看源代码定位原先默认的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#4168upgrading方法, 猜测应该就是对上传的系统升级压缩包进行处理

首先会调用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为什么会在根目录下存储了。

  • 发表于 2021-11-24 09:37:33
  • 阅读 ( 6929 )
  • 分类:漏洞分析

0 条评论

joker
joker

19 篇文章

站长统计