IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    shentou发表于 2017-01-25 04:48:20
    love 0

    前言:

    ‍‍‍‍还记得刚玩Web安全时,就想着要是能有一个自动挖掘XSS漏洞的软件就好了。然后我发现了Safe3、JSky、AWVS、Netsparker等等,但是误报太多,而且特别占内存。后来发现了fiddler的一个插件也可以检测当前浏览的页面是否存在XSS,但是不利于查看。于是就有了本文。‍‍

    ‍‍本文将从每段代码,每个步骤讲解“如何自己写一个自动检测网页是否存在XSS”的插件。‍‍‍‍

    0×01 构思很重要:

    我现在是在做前端,学了那么久。教会了我一件事,就是干活之前先构思好、规划好。只需要大体的说下就下,比如这个插件,我先是在本子上写了下面的计划:

    (一)获取当前网站的URL

    (二)获取参数,并对其格式化为二维数组,有助于后面的获取,格式如下:

    [        [参数,值], [参数,值] ]

    (后来因发现会使代码臃肿,就放弃了这个想法)

    (三)对“值”与“唯一标识符”拼接。

    (四)Ajax对当前网页的URL发送数据。

    (五)返回200状态码后,进行查找“唯一标识符”。

    (六)查找成功后,发送到服务器端。

    构思,并不是后面代码就必须遵守。只是给出一个可行的方案,然后在这个方案上优化一下。

    我现在的网页是http://test.cn/?a=1&b=2&c=3,后面就根据这个URL来进行操作。

    0×02先从JavaScript开始:

    为了以后添加新功能,我们就把每一个功能封装成一个函数。防止代码混乱不堪。

    一、这个功能是检测当前网页是否存在XSS的,我们就起一个function xssCheck(){}函数。

    二、我们需要判断当前这个url是否存在参数,如果是http://freebuf.com/这样的url,没有参数就不就运行下面的代码,这里就需要一个if判断。代码如下:

    if(location.search == ""){        return false; }

    三、我们需要设置五个变量。如下:

    var onlyString = 'woainixss';       //设置“唯一标识符” var protocol = window.location.protocol;      //获取当前URL的协议,如http:或者https: var hrefHost = window.location.host;    //获取当前URL的域名,如www.freebuf.com var parameter = location.search.substring(1).split("&");     //去掉前面的"?",再把参数以&为分隔符存为数组。 var url = protocol + "//" + hrefHost + "/?";        //拼接一个新的URL。如http://www.freebuf.com/?

    下面是chrome返回的结构,方便理解:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    四、接下来就是比较重要的一步了,把“唯一标识符”与“参数的值”进行拼接,因为不确定参数的个数,我采用for循环:

    for(var i = 0;i < parameter.length;i++){        url += parameter[i].split("=")[0] + "=" + onlyString + parameter[i].split("=")[1] + '"&'; }

    parameter.length的长度是为2(包括0),从上图可以看到。这段代码有点难理解,我们直接输出,看下:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    说的简单一点就是对parameter里的数组值以=为分隔符重新化为数组。parameter[0].split("=")[0]就是代表就是当前网页URL第一个参数的属性,parameter[0].split("=")[1]就是代表就是当前网页URL第一个参数的值。

    然后parameter后面的[i]循环三次(有多少参数,就循环多少次)。

    现在的url变量为http://test.cn/?a=woainixss1"&b=woainixss2"&c=woainixss3"&

    至于为什么每个参数的值后面都有"一个双引号,是为了确保XSS的准确性,这个小方法,至少能保证检测XSS时的误报率减少到5%。剩下的5%是网站除了没有对"进行过滤,其他都做了过滤。当然这种情况很少很少。如果想把误报率减小到0%的话,可以把

    var onlyString = 'woainixss';

    改成:

    var onlyString = 'onclick/'///&#alert()<>"';

    这个唯一标识符为的结果为:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    你以为这样就OK了么?不。上面的代码存在一个bug,那就是url后面会多出一个"&",从而导致ajax请求要分割时出现一个空数组。如何解决呢?加上下面的代码就可以解决了:

    url = url.substring(0,url.length-1);

    五、 都OK之后,就到了最重要的地方。发送请求及回馈查找。这个时候就要用到ajax了,有些人会问ajax不是会遇到跨域问题么,这点无须担心,因为你使用ajax请求的url和原本的url都是一样的,所以无需担心跨域问题。那为什么你要写成插件呢?因为你总不能每打开一个请求都输入下代码,有插件的话,就好办多了。这里说下插件的特性:

    不受跨域影响、每打开一次页面都会加载你所写的JavaScript代码。

    先写出ajax的框架:

    $.ajax({     url: url,     type: 'get',     dataType: 'text', }) .done(function(data) {     /* Code */ })

    上面的ajax代码里,url就是我们构造好的url。type是选择发送数据时采用的方法。dataType是选择返回的数据以什么样的方式回馈,这里的text就是html代码。也就是网站的源码。

    .done是当发送数据请求成功时执行的代码。function(data)里的data就是ajax请求成功后的源码。

    为什么不写.fail(function(data) {….})呢?因为我们这个是检测XSS的,如果返回失败,就说明修改参数后,会导致网站出现非200的状态码,也就是404状态码或者其他的状态码。这个时候就说明参数无XSS。(其实这里还有一个bug,就是假设网站有两个参数,你修改第一个参数的时候会返回404状态码,修改第二个参数的时候会触发XSS。但是我我这个只有一个ajax,导致两个参数乧做了修改,这样的话及时第二个有XSS漏洞,也无法检测到,之所以没写,是不想把代码搞得太复杂,这个月应该会写第二版,把这个bug补上,再加上检测POST XSS漏洞检测。因为有人JavaScript并不是很熟,所以没有写的多么复杂,在此说声抱歉,以后会补上。)

    现在就开始在done里写上处理代码:

    我们先新建一个变量,来储存出现XSS漏洞的参数var xss = "";为什么要在后面加上=""呢?因为下面会使用+=运算符,不加的话会返回NaN(非数字)。

    然后就是for循环用indexOf函数查找源码里是否存在“唯一标识符”。代码如下:

    for(i = 0;i < parameter.length;i++){ /*indexOf函数*/ }

    parameter.length就是当前参数的数量。

    然后就是在for里写上indexOf代码了。这里使用if来判断当前“唯一标识符”是否存在当前源码里:

    if(data.indexOf(onlyString + parameter[i].split("=")[1] + '"') != "-1"){     xss += parameter[i].split("=")[0] + "|"; }

    onlyString + parameter[0].split("=")[1] + '"'是“唯一标识符”与第一个参数的值拼接的结果

    data.indexOf(xxx) != "-1" 使用indexOf搜索字符串的时候,如果没搜索到,会返回-1,这里就的意思是,当网站存在“唯一标识符”时运行if里的代码。

    xss += parameter[i].split("=")[0] + "|";把出现XSS的参数以|为分割(这里后面会多处一个"|"字符,后面会处理。)假设网站的b和c参数存在XSS,那么会返回"b|c|"。

    这里存在一个bug,那就是如果没有XSS漏洞怎么办?下面的代码会解决:

    if(xss == ""){     return false; }else{ /*处理代码*/ }

    当没有变量不存在XSS漏洞,返回false。

    下面就开始处理上面的bug,和发送代码了:

    xss = xss.substring(0,xss.length-1); //img标签里的src属性,为你的服务器地址。 //如果不想加上远程地址,可以吧下面的代码修改为alert(xss) $("body").append("<img src='http://xss.cn/xss.html?host=$" + hrefHost + "&$xss=$" + xss + "&$url=" + window.location.href + "&$rand=$" + Date.parse(new Date()) + "' style='display:none;'>")

    ‍‍第一段是去掉xss变量后的最后一个字符串,也就是"|"。

    ‍‍第二段就是‍‍发送数‍‍据到远程服务器。当然你可以修改为alert(xss),但是如果网站存在XSS,就是弹,太烦人了。

    ‍‍为什么要加$呢,为了让split正确的分割。因为window.location.href里也有参数是&和=,导致split分割时,多分割几个数组。

    ‍‍后面要加随机数,加随机数,加随机数,重要的事情说三遍。调试的时候被这个坑惨了。 刷新页面或者打开页面时,log会无变化,因为这时候的图片已经被浏览器缓存了。‍‍‍‍

    OK,代码已经全部OK,下面的完整的代码:

    function xssCheck(){     if(location.search == ""){         return false;     }     var onlyString = 'woainixss';     var protocol = window.location.protocol;     var hrefHost = window.location.host;     var parameter = location.search.substring(1).split("&");     var url = protocol + "//" + hrefHost + "/?";     for(var i = 0;i < parameter.length;i++){         url += parameter[i].split("=")[0] + "=" + onlyString + parameter[i].split("=")[1] + '"&';     }     url = url.substring(0,url.length-1);     $.ajax({         url: url,         type: 'get',         dataType: 'text',     })     .done(function(data) {         var xss = "";         for(i = 0;i < parameter.length;i++){             if(data.indexOf(onlyString + parameter[i].split("=")[1] + '"') != "-1"){                 xss += parameter[i].split("=")[0] + "|";             }         }         if(xss == ""){             return false;         }else{             xss = xss.substring(0,xss.length-1);             //img标签里的src属性,为你的服务器地址。             //如果不想加上远程地址,可以吧下面的代码修改为alert(xss)             $("body").append("<img src='http://xss.cn/xss.html?host=$" + hrefHost + "&$xss=$" + xss + "&$url=$" + window.location.href + "&$rand=$" + Date.parse(new Date()) + "' style='display:none;'>");         }     }) } xssCheck();

    ‍‍0×03配置远程服务器:‍‍

    因为我已经一年没有碰PHP和MySql了,所以这里就是用JavaScript来完成服务器端的配置。这里使用win7+phpstudy来配置。

    打开phpstudy配置“站点域名管理”,添加一个xss.cn域名。

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    记得重启nginx。

    然后打开host文件,把xss.cn指向本地:

    127.0.0.1 xss.cn 127.0.0.1 www.xss.cn

    然后在在网站目录下,新建一个xss.html。里面内容可以为空。

    修改nginx.conf和vhosts.conf文件。

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    增加一个日志模块,名为xss。

    然后在vhosts.conf文件,配置xss文件的动作及添加

    access_log E:/WWW/xss/xss.log xss;

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    加上这段代码:

    location /xss.html{      access_log E:/WWW/xss/xss.log xss; }

    然后重启下nginx服务器。就行了,这里我们在index.php里面加上

    <?php     echo $_GET['b']; echo $_GET['c']; ?>

    来实验下:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    可以看到代码被成功渲染了,这个时候你的xss网站根目录会生成一个xss.log日志文件,我们来看下:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    可以看到host是出现XSS漏洞的域名,xss是出现漏洞的XSS参数。url是完整的原始URL。

    这个只是我们测试的网页,不来点实战有点过不去。当当网的搜索页面的key存在XSS。URL如下:

    http://daphne.dangdang.com/list.html?key=hello&inner_cat=all&sort_type=sort_xlowprice_asc

    打开后,在控制台输入我们的代码,然后打开xss.log文件:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    成功了。

    ‍‍0×04对日志格式化:‍‍

    现在的日志不是人看的。简直侮辱了使用者的智商。

    下面就是对日志进行格式化的操作。因为一年没碰php了,下面还是使用JavaScript来进行格式化。

    在xss.cn的根目录下创建一个index.html文件

    在开头我们需要远程调用jquery和bootstrap:

    <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js"></script> <link rel="stylesheet" type="text/css"href="http://apps.bdimg.com/libs/bootstrap/3.3.4/css/bootstrap.css">

    JavaScript读取xss.log日志文件,再对每条格式化,显现:

    使用JavaScript读取本地文件我还真没有做过,网上百度都是再说使用new ActiveXObject来读取,但是ActiveXObject的局限性很大,总不能在IE下打开反馈页面。这里介绍一个我自己想到的一个小技巧,使用ajax。之前生成xss.log文件的时候,我们就把xss.log放到了xss.cn的根目录下,这样一样就可以使用ajax来获取了(只读)。

    先创建一个ajax模块:

    $.ajax({     url: '/xss.log',     type: 'get',     dataType: 'text', }) .done(function(data){/* 对xss.log的操作 */})

    这里的data就是xss.log的本文。

    先创建四个变量,分别是储存域名的host、和储存变量的xss、储蓄完整的原URL的url、储蓄html代码的htmlText:

    var host = ""; var xss = ""; var url = ""; var htmlText = '<div class="panel panel-default"><div>网站存在XSS漏洞结果</div><table><thead><tr><th>序列</th><th>网站域名</th><th>存在漏洞参数</th><th>完整的原URL</th></tr></thead><tbody>';

    现在需要对日志进行初步的排版,先让我们看下ajax取到xss.log文件的内容:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    现在我们要求吧本文转成数组,那么下面一段代码就行:

    data = data.split("/n");

    现在再来看看:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    因为nginx生成日志的时候对对日志重开一个头,也就是xss.log文件的结尾会多出一个空格,防止下次写数据时,写到一行。这里我们就需要对最后一个空数组删除:

    if(data[data.length-1] == ""){     data.pop(); }

    之所以不能直接用data.pop();,是因为不确定后面是否有回车,因为人工有的时候会不小心删除。

    去掉干扰符。因为日志里有着很多不需要的字符。比如GET /和HTTP/1.1,接下来就是把他们删除,因为他们的位置是固定的,我们就不需要用正则:

    for(var i = 0;i < data.length;i++){        data[i] = data[i].substring(15); //删除GET /字符        data[i] = data[i].substring(0,data[i].length-11); //删除HTTP/1.1字符 }

    data.length是当前xss.log文章的行数。也就是数组里的个数。也是出线XSS漏洞的网站的个数。

    里面的两个处理代码不能写到一起,会照成错误,具体原因我也不太清楚。

    提取信息,一开始的时候我一直想着是横向取数据,然后一直出错。想了很久,换了纵向取数据就好了,但是代码还是有点难看。大伙别介意:

    for(i = 0;i<data.length;i++){     data[i] = data[i].split("&$");     host += data[i][0].split("=$")[1] + " ";     xss += data[i][1].split("=$")[1] + " ";     url += data[i][2].split("=$")[1] + " "; }

    先是对每一行的数据以&$进行分割,分割的结果如下:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    然后就是对里面的数据以=$进行二次分割,分割代码如下:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    前面的数据不要管,就管最后三行(最后两行是一起的,算作一行),也就是说当前的:

    host="test.cn test.cn daphne.dangdang.com test.cn "; xss="b|c b key b|c "; Url="http://test.cn/?a=1&b=2&c=3 http://test.cn/?a=1&b=2&c=3 http://daphne.dangdang.com/list.html?key=hello&inner_cat=all&sort_type=sort_xlowprice_asc http://test.cn/?a=1&b=2&c=3 ";

    因为我们在处理时,再后面都加了空格,现在就是把他们分割成数组。这里还有一个需要注意,就是最后面也都有一个空格,转化成数组后,最后面会多出一个空数组,我们只需要把它删除即可:

    host = host.split(" "); host.pop(); xss = xss.split(" "); xss.pop(); url = url.split(" "); url.pop();

    这个时候我们再输出看下:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    接下来就是见证奇迹的时刻。现在我们要把提取出来的数据显示到页面里:

    for(i = 0;i < data.length;i++){        htmlText += "<tr><td>"+ (i+1) +"</td><td>" + host[i] + "</td><td>" + xss[i] + "</td><td>" + url[i] + "</td></tr>"; } htmlText += "</tbody></table></div></div>"; $("body").append(htmlText);

    第一个for呢,是按照当前有多少存在XSS漏洞网站的个数循环的。 (i+1) 是序号,让它从1开始。host[i]、xss[i]、url[i]是当前数据的域名、参数、原URL。

    for下面的代码是为了闭合上面的tbody、table、div等标签。

    最下面的一行是为了把内容添加到body里。完整代码如下:

    <!DOCTYPE html> <html> <head>        <meta charset="utf-8">        <meta http-equiv="X-UA-Compatible" content="IE=edge">        <title>XSS反馈</title>        <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js"></script>        <link rel="stylesheet" type="text/css"href="http://apps.bdimg.com/libs/bootstrap/3.3.4/css/bootstrap.css"> </head> <body> <script>        $.ajax({               url: '/xss.log',               type: 'get',               dataType: 'text',        })        .done(function(data) {               var host = "";               var xss = "";               var url = "";               var htmlText = '<div class="panel panel-default"><div>网站存在XSS漏洞结果</div><table><thead><tr><th>序列</th><th>网站域名</th><th>存在漏洞参数</th><th>完整的原URL</th></tr></thead><tbody>';               data = data.split("/n");               if(data[data.length-1] == ""){                      data.pop();               }               for(var i = 0;i < data.length;i++){                      data[i] = data[i].substring(15);                      data[i] = data[i].substring(0,data[i].length-11);               }               for(i = 0;i<data.length;i++){                      data[i] = data[i].split("&$");                      host += data[i][0].split("=$")[1] + " ";                      xss += data[i][1].split("=$")[1] + " ";                      url += data[i][2].split("=$")[1] + " ";               }               host = host.split(" ");               host.pop();               xss = xss.split(" ");               xss.pop();               url = url.split(" ");               url.pop();               for(i = 0;i < data.length;i++){                      htmlText += "<tr><td>"+ (i+1) +"</td><td>" + host[i] + "</td><td>" + xss[i] + "</td><td>" + url[i] + "</td></tr>";               }               htmlText += "</tbody></table></div></div>";               $("body").append(htmlText);        }) </script> </body> </html>

    现在让我们看看网站是什么样子吧:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    ‍‍0×05 巴拉巴拉小魔仙,变变变:‍‍

    接下来就是让它成为一个插件,安装之后,我们只需要偶尔打开下xss.cn网站,就可以看到哪些网站存在XSS漏洞了。不用每打开一个网站就输入一次。

    这个Maxthon插件需要4个文件,1个目录。结构如下:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    Icons是插件上网logo

    Base.js是检测代码,‍‍也就是0×02节所说的JavaScript代码,直接复制过去就行了(注意配置服务器,如不想配置,把利用img发包那段代码修改为alert(xss))‍‍

    Jquery.js是jquery代码,大家都知道

    Def.json是插件的配置代码,代码如下:

    打造一个自动检测页面是否存在XSS的小插件Ⅰ

    然后使用maxthon官网提供的MxPacker软件进行压缩,压缩后打开就可以使用。

    MxPacker:http://bbs.maxthon.cn/forum.php?mod=viewthread&tid=611580

    插件下载地址(没有发送到远程地址,我吧发送数据包的代码修改为了alert(xss)):http://pan.baidu.com/s/1jG4EUJ8

    因为鄙人只学了maxthon的插件开发,chrome、火狐等浏览器的插件开发,本人不会。如果有会的,可以参考下我的代码。写出来一个,当然 共享了更好。

    ‍‍题外话‍‍

    这篇文章发布的日期正好是8月12号,去年的8月12号,正好是我第一次在freebuf发文章,那篇文章是XSS的原理分析与解剖:http://www.freebuf.com/articles/web/40520.html

    一年过去了,我从xss开始,从xss结束。从最基础的xss开始分享经验,到现在的全自动化检测XSS并前台显示,大家和我都成长了太多太多,回过头看,曾近的激情和梦想造就了今天的你我,挺美好的一件事。

    最后祝Freebuf越办越好!

    *本文来自FreeBuf特约作者Black-Hole投稿,属FreeBuf黑客与极客(Freebuf.COM)独家发布,未经允许禁止转载



沪ICP备19023445号-2号
友情链接