原始出处:
http://www.gnucitizen.org/blog/javascript-xss-scanner
On this page you will find a POC of a JavaScript XSS (Cross-site Scripting) Scanner which I promised to realize after I publish the Yahoo Site Explorer Spider discussed in detail over here. Before going any further, I must say a few words about the legal side of using this tool. Basically:
you should test only servers/applications that you are in charge of. I am not responsible for your own actions.
If you notice, the scanner is a bit restricted. It is not exhaustive and it can be definitely improved in a number of areas. However, all restrictions were introduced on purpose for a number of reasons. The first reason is that this tool is just a Proof of Concept - it is not intended to be part of any XSS/AJAX worm, attack toolkit or other type of malicious software. Also, the tool should only be used for educational purposes only. Learn from it and come up with creative solutions that solve interesting problems. Don’t be evil. Last but not least, this tools is written just to prove that AJAX worms can propagate across several domains by scanning for new vulnerabilities on their own. That used to be considered theoretical attack vector. With this example we put the theory into practice.
The XSS Scanner relays on an external proxy server to locate the XSS holes. The proxy in use is called Palary and you can find more information about it over here. Here is what the developers of the Palary have to say about their product:
The Palary Browser is a cutting-edge, web application that delivers a secure, personalized surfing experience. The main advantages of the Browser over classical technologies are as follows:
Security - The Palary Browser by default disables Javascript in webpages. Javascript is a useful technology in many circumstances, but is also insecure and opens your computer up to innumerable web based attacks. These attacks are impossible when using the Palary Browser.
Privacy - The Palary Browser increases your privacy on multiple fronts. On a wide front, the Browser prevents your ISP, your government, or another body from tapping your web-surfing. All data is routed through the Browser’s servers so that is impossible for these bodies to see what information you are accessing or sending. (The above assumes that you have media disabled on webpages.)
On a more local front, the Browser prevents history files, cache files, and cookie files from being saved to your computer. This means that no one with access to your computer will be able to see where you have been surfing.
Palary.org
Funny enough we can use Palary to detect XSS vectors although the authors have designed it to prevent them. I am not sure whether this is a bug or something else. I don’t know.
The url and query mutation engines that are part of this client-side XSS scanner are written on the top of AttackAPI v3, which is coming along quite nicely.
Once you open the POC application there are two options that are given to you. The first one is to use the XSS scanner together with the Yahoo Site Explorer Spider. The spider is restricted in terms of depth and number of results per page. You can spider only the top 50 results. Again, this is done on purpose. Concurrently with the spider, the scanner will test for the XSS issues and deliver via a callback.
The second option scans only one URL. The scanner will grab your input and mutate it into various XSS vectors. Then it will try each one of them. On success the scanner returns a callback and displays on the screen the results. If for any reasons there are no results on the page, this means that no XSS was found. Again, keep in mind that the scanner was crippled on purpose.
For the curious ones, here is the scanner source code:
复制内容到剪贴板
代码:
/** * @name scan * @desc scan url * @param {String} u url to scan * @param {Function} c callback to use */function scan(u, c) { /** * @cat Core * @name AttackAPI * @desc the library head */ var AttackAPI = { version: '3.0.0a', author: 'Petko Petkov | pdp (architect)', homepage: 'http://www.gnucitizen.org', projecthome: 'http://www.gnucitizen.org/projects/attackapi'}; /** * @cat Core * @name AttackAPI.clone * @desc clone object * @param {Object} o the object to clone * @return {Object} cloned object */ AttackAPI.clone = function (o) { function clone (o) { for (var i in o) { this[i] = o[i]; } } return new clone(o); }; /** * @cat Core * @name AttackAPI.expand * @desc expand obj with properties * @param {Object} o the object to extend * @param {Object} p the properties to use * @return {Object} the expand object (o) */ AttackAPI.expand = function (o, p) { var _o = this.clone(o); for (var i in p) { _o[i] = p[i]; } return _o; }; /** * @cat Base * @name AttackAPI.buildURL * @desc build url from url object * @param {Object} obj the object to be used * @return {String} url string */ AttackAPI.buildURL = function (obj) { var host = obj.host?obj.host:(obj.hostname?obj.hostname:null); if (!host) { return ''; } var hash = obj.hash?(obj.hash[0] == '#'?obj.hash:'#' + obj.hash):''; var password = obj.password?obj.password:''; var pathname = obj.pathname?(obj.pathname[0] == '/'?obj.pathname:'/' + obj.pathname):'/'; var port = obj.port?':' + obj.port:''; var protocol = obj.protocol?obj.protocol + '://':'http://'; var search = obj.search?(obj.search[0] == '?'?obj.search:'?' + obj.search):''; var username = obj.username?obj.username:''; var creds = (username || password)?username + ':' + password + '@':''; return protocol + creds + host + (port != ':80'?port:'') + pathname + (search != '?'?search:'') + (hash != '#'?hash:''); }; /** * @cat Base * @name AttackAPI.parseURL * @desc parse URL into object * @param {String} url the url to parse * @return {Object} parsed url object */ AttackAPI.parseURL = function (url) { var REGEX = /^((\w+):\/\/)?((\w+):?(\w+)?@)?([^\/\?:]+):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(\w*)/; var fields = {'href': 0, 'username' : 4, 'password' : 5, 'port' : 7, 'protocol' : 2, 'host' : 6, 'hostname' : 6, 'pathname' : 8, 'search' : 9, 'hash' : 10}; var result = new Object(); var r = REGEX.exec(url); for (var field in fields) { result[field] = r[fields[field]]; } result.hash = result.hash?'#' + result.hash:'#'; result.search = result.search?'?' + result.search:'?'; result.username = result.username?result.username:''; result.password = result.password?result.password:''; result.pathname = result.pathname?result.pathname:'/'; if (result.port == undefined) { switch (result.protocol) { case 'http': result.port = 80; break; case 'https': result.port = 443; break; case 'ftp': result.port = 21; break; default: result.port = ''; break; } } return result; }; /** * @cat DOM * @name AttackAPI.decodeURL * @desc decode URL * @param {String} url the url to decode * @return {String} URL decoded string */ AttackAPI.decodeURL = function (url) { return unescape(url); }; /** * @cat DOM * @name AttackAPI.encodeURL * @desc URL encode string * @param {String} url the url to encode * @return {String} url encoded string */ AttackAPI.encodeURL = function (url) { return escape(url); }; /** * @cat Base * @name AttackAPI.parseQuery * @desc parse Query into object * @param {String} query the query to parse * @return {Object} parsed string object */ AttackAPI.parseQuery = function (query) { var queryobj = new Object(); var tokens = query.split('&'); for (var index = 0; index < tokens.length; index++) { var pair = tokens[index].split('='); queryobj[this.decodeURL(pair[0])] = this.decodeURL(pair[1]); } return queryobj; }; /** * @cat Base * @name AttackAPI.buildQuery * @desc build query string from object * @param {Object} obj the object to be used * @return {String} query string */ AttackAPI.buildQuery = function (obj) { var tokens = []; for (var item in obj) { tokens.push(this.encodeURL(item) + '=' + ((obj[item] != undefined && obj[item] != null)?this.encodeURL(obj[item]):'')); } return tokens.join('&'); }; /** * @name scan >> mutate_query * @desc muate query * @param {String} q query to mutate * @return {Array} mutations */ function mutate_query(q) { var ret_query = []; var query_parts = AttackAPI.parseQuery(q.substr(1)); for (var name in query_parts) { var tmp = {}; tmp[name] = 'XSS_SCAN"><script>'; ret_query.push(AttackAPI.buildQuery(AttackAPI.expand(query_parts, tmp))); tmp[name] = 'XSS_SCAN\'><script>'; ret_query.push(AttackAPI.buildQuery(AttackAPI.expand(query_parts, tmp))); } return ret_query; } /** * @name scan >> mutate_url * @desc muate url * @param {String} u url to mutate * @return {Array} mutations */ function mutate_url(u) { var ret_urls = []; var url_parts = AttackAPI.parseURL(u); ret_urls.push(AttackAPI.buildURL(AttackAPI.expand(url_parts, {pathname: '/XSS_SCAN"><script>'}))); ret_urls.push(AttackAPI.buildURL(AttackAPI.expand(url_parts, {pathname: '/XSS_SCAN\'><script>'}))); ret_urls.push(AttackAPI.buildURL(AttackAPI.expand(url_parts, {pathname: url_parts.pathname + '/XSS_SCAN"><script>'}))); ret_urls.push(AttackAPI.buildURL(AttackAPI.expand(url_parts, {pathname: url_parts.pathname + '/XSS_SCAN\'><script>'}))); var q_mutations = mutate_query(url_parts.search); for (var i = 0; i < q_mutations.length; i++) { ret_urls.push(AttackAPI.buildURL(AttackAPI.expand(url_parts, {search: q_mutations[i]}))); } return ret_urls; } /** * @name scan >> queryPalary * @desc load remote script * @param {String} q the query */ function queryPalary(q) { var s = document.createElement('script'); s.type = 'text/javascript'; // Palary is stupid/smart so we need to escape each " and script var q = q.replace('"', '\\"'); var q = q.replace('script', 'scrip t'); var q = escape(q); s.src = 'http://palary.com/main/load_page?sfrurl=' + q; document.body.appendChild(s); } window['page_loaded'] = function (d, e, u) { if (d.match(/XSS_SCAN\\*"><scrip\s*t>/) || d.match(/XSS_SCAN\\*'><scrip\s*t>/)) { c.call(c, u, d); } }; window['Effect'] = {'Highlight': function () {}}; var scanned = {}; var url_mutations = mutate_url(u); for (var i = 0; i < url_mutations.length; i++) { if (scanned[url_mutations[i]] != undefined) { continue; } scanned[url_mutations[i]] = true; queryPalary(url_mutations[i]); }}and this is how I use it:
复制内容到剪贴板
代码:
$(document).ready(function () { function startSpider() { var target = $('[@name="domainXSSScanner"] [@name="target"]').val(); spider(target, function (u, cu, t) { scan(u, function (u) { $('#spidered-links').append(u + '<br/>'); }); }); } function startScanner() { var target = $('[@name="targetXSSScanner"] [@name="target"]').val(); scan(target, function (u) { $('#spidered-links').append(u + '<br/>'); }); } $('form[@name="domainXSSScanner"] input[@name="scan"]').click(function () { startSpider(); }); $('form[@name="domainXSSScanner"]').submit(function () { startSpider(); return false; }); $('form[@name="targetXSSScanner"] input[@name="scan"]').click(function () { startScanner(); }); $('form[@name="targetXSSScanner"]').submit(function () { startScanner(); return false; });});I hope that this was helpful and quite educational. If you are interested in this subject, I highly recommend to subscribe to this blog and check out some of the previous articles. You can also have a look on Billy Hoffman’s Jikto.
launch:
POC; download: