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


发布人:admin分类:网络安全浏览量:88发布时间:2017-12-12

前言:

这一版的变化比上一版有点大,首先整个代码的构架被修改了,更加的直观,方便修改和定位。另外,在这一版里我修复了上一版两处BUG(后文会说),添加了伪静态检测XSS(URL处的路径及文件)。

0×01 网站结构:

我把之前写的xss.html修改为getXSS.html。增加了formXSS.html来接受form表单的xss(下一章会专门来说这个功能)。把日志放在了logs文件里。如下:

修改log日志文件的话,nginx的配置文件也要改的。下面是nginx配置后的nginx代码:

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

下面的ajax代码我就使用logs/xxx.html为接受地址了。

0×02 代码的构架:

因为在写的时候,想把这个插件不止光做成“网站XSS漏洞检测”,以后再添加一些其他的漏洞检测脚本。而且添加了判断,使得在应该的时候再运行脚本,比如当前的网页URL没有参数,那就不运行URL参数XSS漏洞检测脚本,也可以大大的提升脚本的运行效率。于是我把几个常用的变量写在外面,函数里面就可以直接调用,不用再声明(JavaScript变量声明提升),然后在声明的下面使用if判断使用哪些脚本。再把每一个功能都封装成一个函数。

构架如下:

变量声明
变量操作(在if前面,防止if直接跳到函数里,而没有运行)
if判断(跳到哪个函数)
功能函数

变量声明

var onlyString = 'woainixss<>'; //唯一标识符
var protocol = window.location.protocol;  //网站使用的网络协议(http、https等)
var host = window.location.host;   //网站的主域名(*.com、*.cn等,例:test.cn)
var href = window.location.href;   //网站的完整URL(协议+域名+参数+锚)
var hostPath; //用来存放网站除去参数的字符串(协议+域名+锚)
var urlPath;  //用来存放网站URL路径的数组(例:test.cn/test/xss/123  urlPath = ['test','xss’,'123'])

变量操作:

if(href.indexOf("?") != "-1"){  //如果url存在?字符串(存在?基本就存在参数了)
    hostPath = href.slice(0,href.indexOf("?"));      //去除参数,只留下“协议+域名+锚”
}else{
    hostPath = href;  //不存在?则把完整的url赋值给hostPath。
}
urlPath = hostPath.split("/").splice(3);      //以“/”为分隔符,把路径分割成数组。

因为协议后面会有两个//,而.com等域名后面还会有一个/,这些都是要抛弃的。只留下路径。反馈如下:

因为多余的“/”存在,会多出3个数组,使用splice(3)去除就行了。

If判断:

if(location.search != ""){  //当参数不为空时,跳转到parameter_Xss函数里
    parameter_Xss();
}
if(href.split("/")[3] != ""){      //当完整的URL里第三个/后存在字符串,则跳转到pseudoStatic_Xss函数里
    pseudoStatic_Xss();
}
if($("form").length > 0){       //当页面存在form表单,就跳转到form_Xss函数里
    form_Xss();
}

功能函数:

function parameter_Xss(){...}      //URL参数检测XSS
function pseudoStatic_Xss(){...}       //伪静态检测XSS
function form_Xss(){...}     //form表单检测XSS

0×03:URL参数检测XSS的BUG修改:

第一处BUG是我在测试的时候发现的:也是我的疏忽。我在调试代码的时候,没有把各个可能出现的环境考虑到。下面是问题代码:

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变量赋值的时候出现的。我没有考虑到路径的问题。就直接把域名加上参数加上唯一标识符。比如测试的网页是test.cn/test/xss/123?a=1&b=2,但是此时的url就为test.cn/?a=1&b=2。直接把路径给忽略了。这就导致待检测的网页无法正确的被正确的处理。

下面是我修改后的代码:

var url = protocol + "//" + host + "/" + urlPath.join("/") + "?";

至于怕没有路径而导致的报错,大可不必担心。如果不存在路径,urlPath.join("/")则会返回空字符串。

第二处是我第一节时提到的BUG:当网站存在两个参数时,代码会自动把每一个参数的值都加上“唯一标识符符”,然后再交给ajax发送数据。如果这时第一个参数存在XSS,但是第二个参数不能变,否则就无法触发XSS。这个问题在第一版的时候是无法解决的,在这一版就完美的解决了。下面是我重写的代码:

var i; //for循环里的i
var parameter = location.search.substring(1).split("&");    //把URL字符以&分割成字符串
var url = protocol + "//" + host + "/" + urlPath.join("/") + "?";     //拼接成新的URL字符。(例:http://test.cn/test/xss/123?)
for(i = 0;i < parameter.length;i++){      //for循环,有多少个参数就循环多少次
    var parameterData = parameter[i];
    parameter[i] = parameter[i].split("=")[0] + "=" + parameter[i].split("=")[1] + onlyString;
    $.ajax({
        url: url + parameter.join("&"),
        type: 'get',
        dataType: 'text',
        async:false,
    })
    .done(function(data) {
        if(data.indexOf(parameter[i].split("=")[1]) != "-1"){
            $("body").append("<img src='http://xss.cn/getXSS.html?host=$" + host + "&$xss=$" + parameter[i].split("=")[0] + "&$url=$" + window.location.href + "&$rand=$" + Date.parse(new Date()) + "' style='display:none;'>");
        }
    })
    parameter[i] = parameterData;
}

下面就来说说for语句里的代码的意思。

var parameterData = parameter[i];

parameter[i]代表的是当前参数的字符,如图:

因为下面的操作代码会使当前的parameter[i]值改变,而我们需要再最后再把他改回来,那就需要赋值了,最后的时候我们再输入

parameter[i] = parameterData;

反过来就可以搞定了。

parameter[i] = parameter[i].split("=")[0] + "=" + parameter[i].split("=")[1] + onlyString;

把当前的参数重新定义,parameter[i].split("=")[0]的是获取当的参数的字符串,如下:

parameter[i].split("=")[1]是获取当前参数的值,如下:

onlyString就是“唯一标识符”了,除去ajax,剩下的结果就是下面这样:

拼接URL参数的代码,我放到ajax里了,也就是url + parameter.join("&"),结果如下:

先是第一个参数与“唯一标识符”拼接,然后恢复原样。再把第二个参数与“唯一标识符”拼接。依次下去。

Ajax代码相比上次修改了url:url + parameter.join("&")。然后再加上async:false,让ajax为同步,如果为异步的话,只会取for循环最后一次结果,而改为同步的话,才会正常的循环ajax。其他的就没怎么变。

0×04 伪静态检测XSS:

一开始的时候,我想了很久“如何判断页面是否为伪静态”,网上也有说,是利用document.lastModifie来查看到网页的最后更新时间,如果网页采用的是静态,则日期时间和我们电脑的系统时间不一样。如果网页采用的是伪静态,则日期时间和我们电脑的系统时间完全一致。 但是需要发送两次ajax,来判断时间是否不同,我感觉太浪费效率了。后来我想了下,伪静态的规则一般都是把参数设置为路径,如下:

Test.cn/test.php?id=xss/123

修改后:

Test.cn/test/xss/123。

下面是nginx配置的规则代码:

location /test {
    rewrite ^(/.+)/ /test.php?id=$1 last;
}

那我只需要检测当前网页的路径及文件就行了。所以在if判断的时候,我写的是if(href.split("/")[3] != ""){…}就是来判断当前网页是否存在路径。

var fileURL;  //存放文件名字符串的变量
var fileUrlXss;   //把文件的与“唯一标识符”拼接后的字符
var url;   //ajax发送的url
var xss = "";     //存在xss的参数

我们需要先判断当前url是否存在文件,也就是xxx.html、xxx.php等。如果存在就把xxx与“唯一标识符”拼接,再发送。也就是原本是xxx.html文件,拼接后就成为xxxwoiainixss<>.html了,有人可能会问这有什么用,在伪静态看来,xxx.html只是一个参数,就像freebuf的某一个页面:http://www.freebuf.com/tools/74654.html tools、74654其实就是一个参数而已。并非真的存在tools这个目录和74654.html这个文件。

下面就开始说存在文件的情况下如何修改和发现是否存下XSS。

我们先判断是否存在文件,使用

if(urlPath[urlPath.length-1].indexOf(".") != "-1")

就行了。

urlPath是以/为分隔符而切割的数据,urlPath.length-1则是最后一个数组。indexOf(".")则是判断最后一个数组是否存在“.”字符串,因为如果是文件的话,肯定有“.”的。输出的结果如下:

然后当if为true时,也就是存在文件时,我们就要进行拼接发送数据了。

fileURL = urlPath.pop();    //删除并获取最后一个数组,并赋值给fileURL变量

(其实也就是文件的字符串,例:xxx.html、xxx.php)。

下面就是拼凑字符串了:

fileUrlXss = fileURL.split(".")[0] + onlyString + "." + fileURL.split(".")[1]

fileURL.split(".")[0]是获取文件名(除去后缀)

onlyString 则是“唯一字符串”

fileURL.split(".")[1]是获取文件的后缀字符串。

拼凑的结果就是:

然后交给ajax来发送数据就行了。

发送的url为:protocol + "//" + host + "/" + urlPath.join("/") + "/" + fileUrlXss

协议+域名+路径+文件。完整的代码如下:

$.ajax({
    url: protocol + "//" + host + "/" + urlPath.join("/") + "/" + fileUrlXss,
    type: 'get',
    dataType: 'text',
    async:false,
})
.done(function(data) {
    if(data.indexOf(fileUrlXss) != "-1"){
        xss += fileURL + "|";
    }
})

.done(function(data){…})是当ajax发送成功,目标网站返回200状态码时,执行的代码。Data就是发送数据后的网页HTML源码。然后配上if来判断发送数据后的网页源码里是否存在文件名与“唯一字符串”拼接后的结果。如果存在则把文件名字符串加到xss结尾。结果如下:

只所以使用+=是为了让后面的路径也添加到xss变量里。

下面就说说else里的代码。

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

先把文件的变量给清空,后面字符串拼接的话,也只会与空字符相拼接,并不会造成什么影响。而if里的代码是去掉“/”字符串的。

因为在之前的“变量操作”里,有这样一段代码:

urlPath = hostPath.split("/").splice(3);

把URL以“/”分割成数组,但是这里存在一个BUG,在伪静态里或者正常的链接,经常会有这样的URL:test.cn/test/xss/123/,没有文件,只有路径。导致的结果就是多出一个空数组,如下:

我们需要把最后一个空数组给去掉,但是有的URL是test.cn/test/xss/123这样的,而这样的URL是不存在BUG的,如下:

所以我们需要一个if判断,判断最后一个数组是否为空。如果为空,则剔除最后一个数据,也就是使用pop函数,完整代码为:

urlPath.pop();

OK,文件XSS检测依据OK了,下面就是对URL路径进行检测了。

for(var i = 0;i < urlPath.length;i++){...}

因为这时的urlPath存储的是当前网页URL路径的数组,他的长度就决定了有多少个路径。这里的意思就是循环的次数与路径多少 是一样的。

然后让当前的数组值与“唯一字符串”拼接。

urlPath[i] += onlyString;

拼接后,再与其他变量、字符串拼接:

url = protocol + "//" + host + "/" + urlPath.join("/") + "/" + fileURL;

至于无法复原的情况,加上下面的代码就可以解决了:

urlPath[i] = urlPath[i].substring(0,urlPath[i].length-11);

因为woainixss<>字符是11位,而且在原本字符串的后面,那我们只需要去掉字符串最后11位就可以恢复了。如图:

OK,下面就是在他们之间发送ajax请求了。

因为是在for循环里,所以我们要使用异步执行。也就是async:false。

然后使用if判断是否存在XSS漏洞:

if(data.indexOf(urlPath[i]) != "-1"){
    xss += urlPath[i].substring(0,urlPath[i].length-11) + "|";
}

上面说了,urlPath[i].substring(0,urlPath[i].length-11)是去掉“唯一标识符”后的字符串,把存在的漏洞加到xss变量的结尾,这个时候,最后面的字符一定是|,下面将会把它去掉。

For循环好后,我们在for循环的外面,使用if来判断是否存在XSS。

if(xss == ""){
    return false;
}else{ .... }

当xss为空(不存在XSS漏洞)时,则返回false,来跳出function。else里的代码则是存在XSS时,操作的代码。我们先把最后一个字符串|去掉。

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

去掉之后,就是发送字符串到我们的服务端了。

$("body").append("<img src='http://xss.cn/getXSS.html?host=$" + host + "&$xss=$" + xss + "&$url=$" + window.location.href + "&$rand=$" + Date.parse(new Date()) + "' style='display:none;'>");

这段代码和之前的“URL参数检测XSS”代码是一样的。无需修改,当然了,如果你不想发送,可以使用alert来把结果弹出来。alert(xss)。

完整的代码如下:

function pseudoStatic_Xss(){    //伪静态检测XSS
    var fileURL;
    var fileUrlXss;
    var url;
    var xss = "";
    if(urlPath[urlPath.length-1].indexOf(".") != "-1"){
        fileURL = urlPath.pop();
        fileUrlXss = fileURL.split(".")[0] + onlyString + "." + fileURL.split(".")[1]
        $.ajax({
            url: protocol + "//" + host + "/" + urlPath.join("/") + "/" + fileUrlXss,
            type: 'get',
            dataType: 'text',
            async:false,
        })
        .done(function(data) {
            if(data.indexOf(fileUrlXss) != "-1"){
                xss += fileURL + "|";
            }
        })
    }else{
        fileURL = "";
        console.log(urlPath)
        if(urlPath[urlPath.length-1] == ""){
            urlPath.pop();
        }
    }
    for(var i = 0;i < urlPath.length;i++){
        urlPath[i] += onlyString;
        url = protocol + "//" + host + "/" + urlPath.join("/") + "/" + fileURL;
        $.ajax({
            url: url,
            type: 'post',
            dataType: 'text',
            async:false,
        })
        .done(function(data){
            if(data.indexOf(urlPath[i]) != "-1"){
                xss += urlPath[i].substring(0,urlPath[i].length-11) + "|";
            }
        })
        urlPath[i] = urlPath[i].substring(0,urlPath[i].length-11);
    }
    if(xss == ""){
        return false;
    }else{
        xss = xss.substring(0,xss.length-1);
        $("body").append("<img src='http://xss.cn/getXSS.html?host=$" + host + "&$xss=$" + xss + "&$url=$" + window.location.href + "&$rand=$" + Date.parse(new Date()) + "' style='display:none;'>");
    }
}

结尾:

form表单XSS查询,将放到下一章来说,因为这个功能代码比上两个功能的代码总和还多,逻辑也有点复杂,用到了JavaScript里的“闭包”和“filter函数”,再加上for循环里再套一个for循环,还需要重写xss.cn(xss结果反馈网页)。这个功能值得重新开一章节来说。

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





被黑站点统计 - 文章版权1、本主题所有言论和图片纯属会员个人意见,与本文章立场无关
2、本站所有主题由该文章作者发表,该文章作者与被黑站点统计享有文章相关版权
3、其他单位或个人使用、转载或引用本文时必须同时征得该文章作者和被黑站点统计的同意
4、文章作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
7、被黑站点统计管理员有权不事先通知发贴者而删除本文

免责声明

本站主要通过网络搜集国内被黑网站信息,统计分析数据,为部署安全型网络提供强有力的依据.本站所有工作人员均不参与黑站,挂马或赢利性行为,所有数据均为网民提供,提交者不一定是黑站人,所有提交采取不记名,先提交先审核的方式,如有任何疑问请及时与我们联系.

admin  的文章


微信公众号

微信公众号


Copyright © 2012-2022被黑网站统计系统All Rights Reserved
页面总访问量:21214350(PV) 页面执行时间:115.057(MS)
  • xml
  • 网站地图