Should you find yourself sitting in a tin can, far above the world, it’s reasonable to feel like there’s nothing you can do. Just stare out the window and remark that planet earth is blue.

Bowie Is Ticket

Should you find yourself writing a web app, with security out of this world, then it’s reasonable to feel like there’s something you forgot to do.

Here’s a web app that, at first glance, seems secure against HTML injection. However, all you have to do is tell the browser what it wants to know. Kind of like our floating Major Tom – the papers want to know whose shirts you wear.

Every countdown to an HTML injection exploit begins with a probe. Here’s a simple one:"autofocus/onfocus=alert(9);//&search-alias=something

The site responds with a classic reflection inside an <input> field. However, it foils the attack by HTML encoding the quotation mark. After several attempts, we have to admit there’s no way to escape the quoted string:

<input type="hidden" name="url" value=";autofocus/onfocus=alert(9);//&amp;search-alias=something">

Time to move on. But we’re only moving on from that particular payload. A diligent hacker pays attention to detail because, sometimes, that persistence pays off. (Regular readers might find this situation strangely familiar…)

Before we started mutating URL parameters, the link looked more like this:

One behavior that stood out for this page was the reflection of several URL parameters within a JavaScript block. In the original page, the JavaScript was minified and condensed to a single line. We’ll introduce the script block in a more readable composition that includes some indentation and line feeds in order to more clearly convey its semantics. The following script shows up further down the page; the key point to notice is the appearance of the number 412603031 from the node parameter:

(function(w,d,e,o){ var i='DAaba0'; if(w.uDA=w.ues&&w.uet&&w.uex){ues('wb',i,1);uet('bb',i)} siteJQ.available('search-js-general', function(){ SPUtils.afterEvent('spATFEvent', function(){ o=w.DA; if(!o){o=w.DA=[];e=d.createElement('script');e.src='';d.getElementsByTagName('head')[0].appendChild(e)} o.push({c:904,a:'site=redacted;pt=Search;pid=412603031',w:728,h:90,d:768,f:1,g:''}) }) }) })(window,document)

Essentially, it’s an anonymous function that takes four parameters, two of which are evidently the window and document objects since those show up in the calling arguments. If you’re having trouble conceptualizing the previous JavaScript, consider this reduced version:

(function(w,d,e,o){ var i='DAaba0'; o=w.DA;if(!o){o=w.DA=[]} o.push({c:904,a:'site=redacted;pid=XSS'}) })(window,document)

So, our goal must be to refine what gets delivered in place of the XSS characters in order to successfully execute arbitrary JavaScript.

The first step is to insert sufficient syntax to terminate the preceding tokens (e.g. function declaration, methods). This is as straightforward as counting parentheses and such. For example, the following gets us to a point where the JavaScript engine parses correctly up to the XSS.

(function(w,d,e,o){ var i='DAaba0'; o=w.DA;if(!o){o=w.DA=[]} o.push({c:904,a:'site=redacted;pid='})});XSS'}) })(window,document)

Notice in the previous example that we’ve closed the anonymous function, but there’s no need to execute it. This is the difference between (function(){})() and (function(){}) – we omitted the final () since we’re trying to avoid introducing parsing or execution errors preceding our payload.

Next, we find a payload that’s appropriate for the injection context. The reflection point is already within a JavaScript execution block. Hence, there’s no need to use a payload with <script> tags or other elements, nor do we need to rely on an intrinsic event like onfocus().

The simplest payload in this case would be alert(9). However, it appears the site might be rejecting any payload with the word “alert” in it. No problem, we’ll turn to a trivial obfuscation method:


Since we’re trying to cram several concepts into this tutorial, we’ll wrap the payload inside its own anonymous function. Incidentally, this kind of syntax has the potential to horribly confuse regular expressions with which a developer intended to match balanced parentheses.


Recall that in the original site all of the JavaScript was condensed to a single line. This makes it easy for us to clean up the remaining tokens to ensure the browser doesn’t complain about any subsequent parsing errors. Otherwise, the contents of the JavaScript block may not be executed. Therefore, we’ll try throwing in an opening comment delimiter, like this:


Oops. The payload fails. In fact, this was where one review of the vuln stopped. The payload never got so complicated as using the obfuscated alert, but it did include the trailing comment delimiter. Since the browser never executed any pop-ups, everyone gave up and called this a false positive.

Oh dear, it seems hackers can be as fallible as the developers that give us these nice vulns to chew on.

Take a look at the browser’s ever-informative error console. It tells us exactly what went wrong:

SyntaxError: Multiline comment was not closed properly

Everything following the payload falls on a single line. So, we really should have just used the single line comment delimiter:


And we’re done! (For extra points, try figuring out what the syntax might need to be if the JavaScript spanned multiple lines. Hint: This all started with an anonymous function.)

Here’s the whole payload inside the URL. Make sure to encode the plus operator as %2b – otherwise it’ll be misinterpreted as a space.'})});(function(){window['a'%2b'lert'](9)})()//&search-alias=something

And here’s the result within the script block. (WordPress’ syntax highlighting displays it accurately, which is another hint that we’ve modified the JavaScript context correctly.)


There are a few points to review in this example. Here’s a few hints for discovering and exploiting HTML injection:

  • Inspect the entire page for areas where a URL parameter name or value is reflected. Don’t stop at the first instance.
  • Use a payload appropriate for the reflection context. In this case, we could use raw JavaScript because the reflection appeared within a <script> element.
  • Write clean payloads. Terminate preceding tokens, comment out (or correctly open) following tokens. Pay attention to messages reported in the browser’s error console.
  • Don’t be foiled by sites that put “alert” on a deny list. Effective attacks don’t even need to use an alert() function. Know simple obfuscation techniques to bypass deny lists. (Obfuscation really just means an awareness of JavaScript’s objects, methods, semantics, and creativity.)
  • Use the JavaScript that’s already present. Most sites already have a library like jQuery loaded. Take advantage of $() to create new and exciting elements within the page.

And here’s a few hints for preventing it:

  • Use an encoding mechanism appropriate to the context where data from the client will be displayed. The site correctly used HTML encoding for “ characters within the value attribute of an <input> tag, but forgot about dealing with the same value when it was inserted into a JavaScript context.
  • Use string concatenation at your peril. Create helper functions that are harder to misuse.
  • When you find one instance of a programming mistake or a bad programming pattern, search the entire code base for other instances – it’s quicker than waiting for another exploit to appear.
  • Realize that a deny list with “alert” won’t get you anywhere. Have an idea of how diverse HTML injection payloads can be.

There’s nothing really odd about JavaScript syntax. It’s a flexible language with several ways of concatenating strings, casting types, and executing methods. We know developers can build sophisticated libraries with JavaScript. We know hackers can build sophisticated exploits with it.

We know Major Tom’s a junkie, strung out in Heaven’s high, hitting an all-time low. I have my own addiction, but the little green wheels following me are just so many HTML injection vulns, waiting to be discovered.