Sunday, July 23, 2017

True Tales of XSS: Function Hoisting

A simplified version of the code:
<script>
func().val('<?php echo htmlspecialchars($input, ENT_COMPAT, 'UTF-8'); ?>');
</script>
Overview: user-supplied $input is echoed into a JavaScript context after having HTML entities encoded. The func() JavaScript function is undefined, causing an exception and halting execution before any injected payload can be executed. We can't inject additional script tags due to the HTML encoding, so an XSS vector isn't immediately apparent.

But it's there.

To gain execution, we'll need to make sure func() is defined. We'll use a JavaScript feature called function hoisting. Hoisting allows a function to be defined after it's been used. The JavaScript interpreter will look ahead for an appropriate function definition and "hoist" it up in the code, so the function call can execute correctly.

We'll use the following payload:
'); function func() {payload}; //
In this payload, we'll finish the val() call, supplying an empty string. Next, we'll provide a definition of func() for the interpreter to hoist. We can insert our payload into func()'s definition and let the original call execute it. Lastly, we'll comment out the trailing "');" that's been left over from the original code.

Now the func() function is defined and will execute with our injected payload. You read a bit more about hoisting here and here.

Stay beautiful, XSS Rangers.