|
某cms的任意文件读取和删除
原文作者:是juju呀原文地址:https://xz.aliyun.com/t/10932文末送书福利
前言
记录一下最近碰到的几处cms路径穿越的问题(均在后台),均已提交CNVD。
正文
1.某cms的任意文件读取和删除
这个cms目前还是处于更新的状态,来到更多功能-模板管理-新建文件:发现会报错:
 
根据链接:
往回看下源码:
- function addFile() {
- $root = app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR;
- if (!$this->request->isPost()) {
- $dir = rtrim(I('get.path'), '/');
- if (strpos($dir, 'template') === false) {
- exit('error|目录错误!');
- }
- $path = $root . str_replace('/', DIRECTORY_SEPARATOR, $dir);
- $info = [
- 'name' => 'newfile.html',
- 'content' => '',
- 'mode' => 'htmlmixed',
- 'isNew' => true,
- 'path' => $dir,
- ];
- if (is_file($path)) {
- $parts = pathinfo($path);
- $dir = str_replace('/' . $parts['basename'], '', $dir);
- $info['name'] = $parts['basename'];
- $info['content'] = file_get_contents($path);
- $info['isNew'] = false;
- if ($parts['extension'] == 'js') {
- $info['mode'] = 'text/javascript';
- } elseif ($parts['extension'] == 'css') {
- $info['mode'] = 'text/css';
- } elseif ($parts['extension'] == 'json') {
- $info['mode'] = 'application/json';
- }
- } elseif (is_dir($path)) {
- strpos($path, '.') !== false and exit('error|目录错误!');
- } else {
- exit('error|目录错误!');
- }
- $this->assign('dir', $dir);
- $this->assign('info', $info);
- return $this->fetch();
- }
- $dir = rtrim(I('post.path'), '/');
- $name = I('post.name');
- $content = I('post.content');
- if (strpos($dir, 'template') === false) $this->error('目录错误');
- $path = $root . str_replace('/', DIRECTORY_SEPARATOR, $dir);
- if (is_file($path)) {
- $parts = pathinfo($path);
- $dir = str_replace('/' . $parts['basename'], '', $dir);
- $new = str_replace($parts['basename'], $name, $path);
- $parts = pathinfo($new);
- if (!in_array($parts['extension'], ['js', 'html', 'css', 'txt', 'json'])) {
- $this->error('只允许操作文件类型如下:html|js|css|txt|json');
- }
- file_put_contents($path, $content);
- rename($path, $new) or $this->error('操作失败,请检查文件目录权限!');
- } elseif (is_dir($path)) {
- $path = $path . DIRECTORY_SEPARATOR . $name;
- $parts = pathinfo($path);
- if (!in_array($parts['extension'], ['js', 'html', 'css', 'txt', 'json'])) {
- $this->error('只允许操作文件类型如下:html|js|css|txt|json');
- }
- $rs = file_put_contents($path, $content);
- $rs === false and $this->error('操作失败,请检查文件目录权限!');
- } else {
- $this->error('目录错误!');
- }
- $this->success('操作成功!', U('Template/fileList') . '?path=' . urlencode($dir));
- }
- }
复制代码
get请求进入第一个if后,通过
- $dir = rtrim(I('get.path'), '/');
复制代码
获取path参数,经过一个判断
- if (strpos($dir, 'template') === false) {
- exit('error|目录错误!');
- }
复制代码
然后直接拼接到
- $path = $root . str_replace('/', DIRECTORY_SEPARATOR, $dir);
复制代码
然后通过下面代码返回结果:
- $info['content'] = file_get_contents($path);//读取文件
- //调用并显示
- $this->assign('info', $info);
- return $this->fetch();
复制代码
利用(为了绕过判断,请求路径用template/..实行):
原始包
 
修改包
响应
 
通篇来看该cms的文件操作通过数据库实现,因此一定程度上避免了该类问题的发生。
后台清除缓存:
 
源码:
- function clearCache() {
- if (!$this->request->isPost()) {
- return $this->fetch();
- }
- if (!function_exists('unlink')) {
- $this->error('php.ini未开启unlink函数,请联系空间商处理!');
- }
- $clear = I('post.clearCache', []);
- clearCache($clear);
- $this->success('操作成功');
- }
- //跟进clearCache
- function clearCache($clears = []) {
- if ($clears === true || $clears === 'all') {
- $clears = ['cache', 'log', 'temp'];
- }
- $apps = C('config.apps');//清除的应用
- $runtime = app()->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR;
- foreach ($clears as $item) {
- if ($item == 'cache') {
- delFile($runtime . $item, true);
- continue;
- }
- if ($item == 'log') {
- delFile($runtime . $item, true);
- }
- foreach ($apps as $app) {
- $path = $runtime . $app . DIRECTORY_SEPARATOR . $item;
- delFile($path, true);
- }
- }
- /*清除旧升级备份包,保留最后一个备份文件*/
- $backupArr = glob($runtime . 'data/backup/v*_www');
- for ($i = 0; $i < count($backupArr) - 1; $i++) {
- delFile($backupArr[$i], true);
- }
- delFile($runtime . 'schema');
- delFile($runtime . 'log');//调试时生成的日志文件
- return true;
- }
复制代码
原理跟上面类似,也是直接拼接:
 
2.某cms任意文件下载
该cms也是处于更新状态,系统管理-数据还原-备份下载
 
请求包
通过路由找到源码:
- // 下载
- public function downfile(string $name)
- {
- $file_name = $name; //得到文件名
- header("Content-type:text/html;charset=utf-8");
- $file_name = iconv("utf-8", "gb2312", $file_name); // 转换编码
- $file_sub_path = $this->config['path']; //确保文件在这个路径下面,换成你文件所在的路径
- $file_path = $file_sub_path . $file_name;
- # 将反斜杠 替换成正斜杠
- $file_path = str_replace('\\', '/', $file_path);
- if (!file_exists($file_path)) {
- $this->error($file_path);exit; //如果提示这个错误,很可能你的路径不对,可以打印$file_sub_path查看
- }
- $fp = fopen($file_path, "r"); // 以可读的方式打开这个文件
- # 如果出现图片无法打开,可以在这个位置添加函数
- ob_clean(); # 清空擦掉,输出缓冲区。
- $file_size = filesize($file_path);
- //下载文件需要用到的头
- Header("Content-type: application/octet-stream");
- Header("Accept-Ranges: bytes");
- Header("Accept-Length:" . $file_size);
- Header("Content-Disposition: attachment; filename = " . $file_name);
- $buffer = 1024000;
- $file_count = 0;
- while (!feof($fp) && $file_count < $file_size) {
- $file_con = fread($fp, $buffer);
- $file_count += $buffer;
- echo $file_con;
- }
- fclose($fp); //关闭这个打开的文件
- }
复制代码
这个$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
 
结尾
这东西就是图一乐啊,继续搬砖了。
|
|