下载Install.php 安装上 这个cms竟然没有数据库
先去看了一下文件的功能
文件功能列表 https://www.4hou.com/technology/19138.html
│ build.php │ index.php 整个项目的入口,首先引入核心库mc-core.php,然后进行路由,对路由结果进行相应的渲染,相当于MVC中的C │ install.txt 复制为php文件后,用来安装MiniCMS │ README.md │ ├─mc-admin 管理功能的实现 │ conf.php 用户设置页面,包括接收和保存更改的设置 │ editor.php 编辑器的大小、样式调整的库 │ foot.php html<foot>标签构造 │ head.php token验证,html<head>标签构造;若验证失败,跳转至主页 │ index.php 后台登陆身份验证页面 │ page-edit.php 页面编写处理逻辑,包括显示编辑页面、接收提交的页面、页面序列化储存 │ page.php 管理页面的库,声明加载数据、删除页面、还原页面(从回收站还原) │ post-edit.php 文章编写处理逻辑,包括显示编辑页面、接收提交的页面、页面序列化储存 │ post.php 管理文章的库,声明加载数据、删除文章、还原文章(从回收站还原) │ style.css 后台用到的CSS │ └─mc-files │ markdown.php 一个开源的markdown解析库 │ mc-conf.php 配置文件,包含用户名和密码等敏感信息 │ mc-core.php 引入mc-tags、mc-conf,声明404函数 │ mc-rss.php 订购RSS的链接 │ mc-tags.php 相当于M,引入markdown、包括一些核心函数,包括了加载各种信息的函数(网站名、文章数、前进后退等,中间有各种过滤,可以重点分析) │ ├─pages │ └─index │ delete.php 使用数组储存了删除页面的信息(id、标题、标签等)与data文件夹内的文章数据一一对应 │ draft.php 使用数组储存了草稿页面的信息(id、标题、标签等)与data文件夹内的文章数据一一对应 │ publish.php 使用数组储存了已发布的页面的信息(id、标题、标签等)与data文件夹内的文章数据一一对应 │ ├─posts │ ├─data 储存了文章内容的反序列化数据(文章内容等) │ └─index │ delete.php 使用数组储存了删除的文章的信息(id、标题、标签等)与data文件夹内的文章数据一一对应 │ draft.php 使用数组储存了草稿文章的信息(id、标题、标签等)与data文件夹内的文章数据一一对应 │ publish.php 使用数组储存了已发布文章的信息(id、标题、标签等)与data文件夹内的文章数据一一对应 │ └─theme index.php 主题文件,决定了页面的风格,将C传入的信息显示出来,相当于V style.css 主题使用的CSS风格
安装后访问,首页就只显示了三个功能 还包括一个首页
大体看了一下index.php,这里的Index.php就相当于C(控制路由),大体的逻辑
先是根据url后面?后的内容,判断$mc_get_type
再根据$mc_get_type 返回指定的内容
登陆界面 这里面貌似有xss
mc-admin/index.php
<form action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="post">
直接输出,有点像xss
试了半天没试出来 我的火狐把<给urlencode postman也不行
看别的师傅说 用IE能弹窗 但是我经过测试也不能弹窗
去看了一下 登陆的验证过程
一种是通过cookie验证,
还有一种是账号密码验证,通过后设置cookie
刚开始以为越权漏洞是 把 普通用户的账号当作admin的账号,后来发现,普通用户执行admin的操作也是越权
post页面有编辑 删除 恢复等操作 这就是了解文件结构的作用 根据文件的作用 就能找到常见漏洞
在Post.php的52行 有一处删除文件
unlink('../mc-files/posts/data/'.$id.'.dat');
回溯变量
if ($state != 'delete') { $index_file2 = '../mc-files/posts/index/delete.php'; require $index_file2; $mc_posts[$id] = $post; file_put_contents($index_file2, "<?php\n\$mc_posts=".var_export($mc_posts, true)."\n?>"); } else { unlink('../mc-files/posts/data/'.$id.'.dat'); }
$state=delete就可以绕过if判断
$state变量在load_posts函数处被赋值
else if ($_GET['state'] == 'delete'){ $state = 'delete'; $index_file = '../mc-files/posts/index/delete.php'; }
赋值之后,在就没有变化
再去看$id变量
$id变量是从ids种哪来的,ids是GET传过来的 可控
if (isset($_GET['delete']) || (isset($_GET['apply']) && $_GET['apply'] == 'delete')) { if (isset($_GET['apply']) && $_GET['apply'] == 'delete') { $ids = explode(',', $_GET['ids']); foreach ($ids as $id) { if (trim($id) == '') continue; delete_post($id); load_posts(); } } else { delete_post($_GET['delete']); }
试一下 访问post.php 结果是302
很有可能是没登陆,所以重定向了,但是代码看到这里,没有发现有验证的地方,再往下看看
在188行处
<?php require 'head.php' ?> <script type="text/javascript"> function check_all(name) { var el = document.getElementsByTagName('input'); var len = el.length; for(var i=0; i<len; i++) { if((el[i].type=="checkbox") && (el[i].name==name)) { el[i].checked = true; } } }
在head.php中
if (isset($_COOKIE['mc_token'])) { $token = $_COOKIE['mc_token']; if ($token != md5($mc_config['user_name'].'_'.$mc_config['user_pass'])) { Header("Location:index.php"); exit; } } else { Header("Location:index.php"); exit; }
这里对cookie进行了检查
因为我没登陆,所以就302了
但是不影响我们删文件,因为验证在删除的后边
测试
data目录下的文件已经没了
xss大部分是在输入的地方产生的,根据功能来,不去看代码,找到可以输入的地方
统计一下
发布文章那里有三个 分别是 标题 内容 标签
设置 里面有评论代码
在管理页面 也就是post.php页面 同样会出现发布的内容,看一下
这里也被转义了
内容这里存在xss
弹窗
也转义了 不能打
同样设置这里 变量输出时都进行了转义不能弹窗
去源码里看一下后台是怎样处理数据的
编辑页面在post-edit.php
这里开头就包含了head.php,不存在越权
下面对content进行了处理
if (isset($_POST['_IS_POST_BACK_'])) { $post_id = $_POST['id']; $post_state = $_POST['state']; $post_title = trim($_POST['title']); $post_content = get_magic_quotes_gpc() ? stripslashes(trim($_POST['content'])) : trim($_POST['content']); $post_tags = explode(',', trim($_POST['tags'])); $post_date = date("Y-m-d"); $post_time = date("H:i:s"); $post_can_comment = $_POST['can_comment'];
get_magic_quotes_gpc 获得当前magic_quotes_gpc的配置选项设置
stripslashes函数 返回以剥离反斜杠\的字符串
在这里就等同于没处理 也没有过滤函数 对xss没防御措施
标题 在前端的输出哪里进行了一次转义操作 这是在草稿箱哪里进行的转义,但是在存储的过程中并没有进行转义操作
<input name="title" type="text" class="edit_textbox" placeholder="在此输入标题" value="<?php echo htmlspecialchars($page_title); ?>"/>
mc-admin/conf.php
同样这里的转义是在conf界面显示的时候进行的转义,存储时没有进行转义
<?php echo htmlspecialchars($comment_code); ?>
后来发现 数据在存储过程中经过了一步var_export() 这个函数会把单引号转义 但是并不会转义<>
所以我推测,这里的转义可能是浏览器转义的
评论代码这里只在设置界面这里会显示,其他地方不显示,也就没了输出点
既然可以输入内容,那么输入一点代码会是什么样,不要忘了这里的内容被存在数组里, 所以要先逃出数组
$mc_config = array ( 'version' => '1.10', 'site_link' => 'http://127.0.0.1/minicms', 'site_name' => '我的网站', 'site_desc' => '又一个MiniCMS网站', 'comment_code' => '<script>alert(1)</script>', )
单引号闭合再加上注释符
不行,前面说了comment_code中的单引号被var_export转义了,
只能利用其他的位置
就用网站标题吧
最后发现从conf.php传过去的mc_config变量,都经过了var_export
后来看了已有的CVE 是使用了install.php 在安装时 通过设置网站标题为
实现RCE 一般网站在安装后都会删除install.php
mc-admin/post.php
这里有一个tag参数,最后的输出没有经过转义
if (isset($_GET['tag'])) $filter_tag = $_GET['tag'];
288行有输出点
<a class="link_button" href="?state=<?php echo $state; ?>&date=<?php echo $filter_date;?>&tag=<?php echo $filter_tag; ?>">«</a> <a class="link_button" href="?state=<?php echo $state; ?>&date=<?php echo $filter_date;?>&tag=<?php echo $filter_tag; ?>&page=<?php echo $prev_page; ?>">‹</a>
下面还有一个data参数,同样的道理,也存在xss
利用过程和之前的基本一样
可能只在一个地方输入,但是会在多个地方显示,也就是说虽然只在编辑页面输入了xss语句,但是会在post.php 以及minicms/?post/4hctsa等多个页面显示
还有浏览器可能也会转义标签等xss
因为这个cms使用了stripslashes来处理输入数据,那么可以全局搜索一下stripslashes来快速找到,有哪些地方可以输入数据,也就找到了xss输入点
page-edit.php下面还有一处写入操作file_put_content
116行
$data['content'] = $post_content; file_put_contents($file_path, serialize($data));
上面还有一处文件包含109行
$index_file = '../mc-files/posts/index/'.$post_state.'.php'; require $index_file;
回溯变量
$post_content = $_POST['content']
$post_state = $_POST['state']
都可控
在测试的时候出现了两个问题
content写上<?php phpinfo();?>
第一个还好说 content是存放在data目录下 这里的路径是index 可以用../跳转一下
第二个捣鼓了半天 还没弄好 就是写入的文件是有后缀的,dat 而这里自动加上了一个后缀.php 试过%00 # ; ./长度截断 都不行
最后把php的版本改为5.2 使用%00截断 实现了文件包含+RCE
%00截断适用与php<5.3.29 并且GPC(magic_quotes_gpc)为off
这个cms是一个搭建博客使用的,普通用户能够执行的东西比较少,像文件上传这种漏洞根本就不存在,主要是一个xss
这里xss还用到了一个在action参数里输出,虽然没复现出来