安全矩阵

 找回密码
 立即注册
搜索
查看: 2673|回复: 0

几处cms路径穿越

[复制链接]

189

主题

191

帖子

903

积分

高级会员

Rank: 4

积分
903
发表于 2022-3-20 00:13:11 | 显示全部楼层 |阅读模式
原文链接:​几处cms路径穿越 (qq.com)

前言
记录一下最近碰到的几处cms路径穿越的问题(均在后台),均已提交CNVD。
正文
1.某cms的任意文件读取和删除
这个cms目前还是处于更新的状态,来到更多功能-模板管理-新建文件:发现会报错:

根据链接:




往回看下源码:

  1. function addFile() {
  2.         $root = app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR;
  3.         if (!$this->request->isPost()) {
  4.             $dir = rtrim(I('get.path'), '/');
  5.             if (strpos($dir, 'template') === false) {
  6.                 exit('error|目录错误!');
  7.             }
  8.             $path = $root . str_replace('/', DIRECTORY_SEPARATOR, $dir);
  9.             $info = [
  10.                 'name' => 'newfile.html',
  11.                 'content' => '',
  12.                 'mode' => 'htmlmixed',
  13.                 'isNew' => true,
  14.                 'path' => $dir,
  15.             ];
  16.             if (is_file($path)) {
  17.                 $parts = pathinfo($path);
  18.                 $dir = str_replace('/' . $parts['basename'], '', $dir);
  19.                 $info['name'] = $parts['basename'];
  20.                 $info['content'] = file_get_contents($path);
  21.                 $info['isNew'] = false;
  22.                 if ($parts['extension'] == 'js') {
  23.                     $info['mode'] = 'text/javascript';
  24.                 } elseif ($parts['extension'] == 'css') {
  25.                     $info['mode'] = 'text/css';
  26.                 } elseif ($parts['extension'] == 'json') {
  27.                     $info['mode'] = 'application/json';
  28.                 }
  29.             } elseif (is_dir($path)) {
  30.                 strpos($path, '.') !== false and exit('error|目录错误!');
  31.             } else {
  32.                 exit('error|目录错误!');
  33.             }
  34.             $this->assign('dir', $dir);
  35.             $this->assign('info', $info);
  36.             return $this->fetch();
  37.         }
  38.         $dir = rtrim(I('post.path'), '/');
  39.         $name = I('post.name');
  40.         $content = I('post.content');

  41.         if (strpos($dir, 'template') === false) $this->error('目录错误');
  42.         $path = $root . str_replace('/', DIRECTORY_SEPARATOR, $dir);
  43.         if (is_file($path)) {
  44.             $parts = pathinfo($path);
  45.             $dir = str_replace('/' . $parts['basename'], '', $dir);
  46.             $new = str_replace($parts['basename'], $name, $path);
  47.             $parts = pathinfo($new);
  48.             if (!in_array($parts['extension'], ['js', 'html', 'css', 'txt', 'json'])) {
  49.                 $this->error('只允许操作文件类型如下:html|js|css|txt|json');
  50.             }
  51.             file_put_contents($path, $content);
  52.             rename($path, $new) or $this->error('操作失败,请检查文件目录权限!');
  53.         } elseif (is_dir($path)) {
  54.             $path = $path . DIRECTORY_SEPARATOR . $name;
  55.             $parts = pathinfo($path);
  56.             if (!in_array($parts['extension'], ['js', 'html', 'css', 'txt', 'json'])) {
  57.                 $this->error('只允许操作文件类型如下:html|js|css|txt|json');
  58.             }
  59.             $rs = file_put_contents($path, $content);
  60.             $rs === false and $this->error('操作失败,请检查文件目录权限!');
  61.         } else {
  62.             $this->error('目录错误!');
  63.         }
  64.         $this->success('操作成功!', U('Template/fileList') . '?path=' . urlencode($dir));
  65.     }

  66. }
复制代码

get请求进入第一个if后,通过
  1. <pre>$dir = rtrim(I('get.path'), '/');
  2. </pre>

  3. <p></p>
复制代码

获取path参数,经过一个判断
  1. if (strpos($dir, 'template') === false) {
  2.                 exit('error|目录错误!');
  3.             }
复制代码

然后直接拼接到
  1. <pre>$path = $root . str_replace('/', DIRECTORY_SEPARATOR, $dir);
  2. </pre>

  3. <p></p>
复制代码

然后通过下面代码返回结果:
  1. $info['content'] = file_get_contents($path);//读取文件
  2. //调用并显示
  3. $this->assign('info', $info);
  4. return $this->fetch();
复制代码

利用(为了绕过判断,请求路径用template/..实行):
原始包




修改包





响应





通篇来看该cms的文件操作通过数据库实现,因此一定程度上避免了该类问题的发生。
后台清除缓存:





源码:
  1. function clearCache() {
  2.         if (!$this->request->isPost()) {
  3.             return $this->fetch();
  4.         }
  5.         if (!function_exists('unlink')) {
  6.             $this->error('php.ini未开启unlink函数,请联系空间商处理!');
  7.         }

  8.         $clear = I('post.clearCache', []);
  9.         clearCache($clear);

  10.         $this->success('操作成功');

  11.     }
  12. //跟进clearCache
  13. function clearCache($clears = []) {
  14.     if ($clears === true || $clears === 'all') {
  15.         $clears = ['cache', 'log', 'temp'];
  16.     }

  17.     $apps = C('config.apps');//清除的应用
  18.     $runtime = app()->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR;
  19.     foreach ($clears as $item) {
  20.         if ($item == 'cache') {
  21.             delFile($runtime . $item, true);
  22.             continue;
  23.         }
  24.         if ($item == 'log') {
  25.             delFile($runtime . $item, true);
  26.         }
  27.         foreach ($apps as $app) {
  28.             $path = $runtime . $app . DIRECTORY_SEPARATOR . $item;
  29.             delFile($path, true);
  30.         }
  31.     }

  32.     /*清除旧升级备份包,保留最后一个备份文件*/
  33.     $backupArr = glob($runtime . 'data/backup/v*_www');
  34.     for ($i = 0; $i < count($backupArr) - 1; $i++) {
  35.         delFile($backupArr[$i], true);
  36.     }

  37.     delFile($runtime . 'schema');
  38.     delFile($runtime . 'log');//调试时生成的日志文件

  39.     return true;
  40. }
复制代码


原理跟上面类似,也是直接拼接:

2.某cms任意文件下载
该cms也是处于更新状态,系统管理-数据还原-备份下载





请求包





通过路由找到源码:

  1. / 下载
  2.     public function downfile(string $name)
  3.     {
  4.         $file_name = $name; //得到文件名
  5.         header("Content-type:text/html;charset=utf-8");
  6.         $file_name = iconv("utf-8", "gb2312", $file_name); // 转换编码
  7.         $file_sub_path = $this->config['path']; //确保文件在这个路径下面,换成你文件所在的路径
  8.         $file_path = $file_sub_path . $file_name;
  9.         # 将反斜杠 替换成正斜杠
  10.         $file_path = str_replace('\\', '/', $file_path);
  11.         if (!file_exists($file_path)) {
  12.             $this->error($file_path);exit; //如果提示这个错误,很可能你的路径不对,可以打印$file_sub_path查看
  13.         }
  14.         $fp = fopen($file_path, "r"); // 以可读的方式打开这个文件
  15.         # 如果出现图片无法打开,可以在这个位置添加函数
  16.         ob_clean(); # 清空擦掉,输出缓冲区。
  17.         $file_size = filesize($file_path);
  18.         //下载文件需要用到的头
  19.         Header("Content-type: application/octet-stream");
  20.         Header("Accept-Ranges: bytes");
  21.         Header("Accept-Length:" . $file_size);
  22.         Header("Content-Disposition: attachment; filename = " . $file_name);
  23.         $buffer = 1024000;
  24.         $file_count = 0;
  25.         while (!feof($fp) && $file_count < $file_size) {
  26.             $file_con = fread($fp, $buffer);
  27.             $file_count += $buffer;
  28.             echo $file_con;
  29.         }
  30.         fclose($fp); //关闭这个打开的文件
  31.     }
复制代码



这个$file_name直接拼接,没有经过过滤,利用




3.某cms两处任意文件删除
该cms较老,且处于停更状态,一次偶然机会碰到的。
一、后台附件管理-图片管理可删除图片,




得到删除包:





ids和path可控,找到对应方法:admincms\contraller\upfile.contraller.php





传入的ids 用 “,” 进行分割,然后直接赋值没有过滤,跟进$this->pagebls['path']





发现也是直接赋值,没有过滤,修改ids 为 ,../s,txt, 的形式




二、admincms\contraller\template.contraller.php
找到模板管理,选中一个文件点击删除:





得到删除包





发现id[]是可控的,对应到源码:





发现$idd可控,跟进$this->pagebls['path']




发现$this->pagebls['path']也可控,修改id为id[]=../s.txt




结尾
这东西就是图一乐啊,继续搬砖了。


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-24 15:11 , Processed in 0.014318 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表