Quoteless Javascript Injections

In multi reflection scenarios, like we already have seen here, it’s possible to use payloads in such a way that avoid filters and WAFs (Web Application Firewalls) due to the change in the order of its elements.

But in source-based JS injections (those which happen in script blocks) there’s another interesting consequence of having more than one reflection point: the possibility to break out from delimited strings (single or double quotes) with another character besides the quotes themselves.

The backslash character will escape the native quote, making room for the next reflection point to get full control over the code. See the example below, with 2 variables.

var a = ‘$INPUT’; var b = ‘$INPUT’;

If $INPUT is something like “any\”, value of variable “a” becomes what is highlighted in red.

var a = ‘any\’; var b = ‘any\’;

Because the backslash escapes the native quote of variable “a” and makes the JS parser finding the end of the value in the next quote, the one used to start the value of the next variable “b”. But in the example above it will throw a syntax error: we need to add some code to be executed after the new value of “a” and fix the remaining code.

var a = ‘-alert(1)//\’; var b = ‘-alert(1)//\’;

So a less sign (-) is used to perform an arithmetic operation with our alert function to make it be evaluated/executed and after it a pair of slashes to comment the rest to avoid the syntax error. Any other arithmetic operator or “;” or even a new line character also would make it work as well as the HTML comment (<!–).

By extending the concept to some simple possible scenarios, we have the following XSS cases currently covered by the online XSS PoC tool KNOXSS.


Quoteless Inline Double Injection in JS variables
Quoteless Inline Double Injection in JS object
Quoteless Inline Double Injection in JS object with Nested Array
Quoteless Inline Double Injection in JS object with Nested Function

#hack2learn => Take some minutes to figure out how to alert there!


All cases deal with the same entry point (same GET or POST variable) reflected twice but it’s also possible to find different entry points and/or more than two reflections in other scenarios. One important thing to remember is those reflections must be in same line.

Another rare but interesting case of quoteless JS injection happens when input lands in backticks delimited variable value:

var a = `$INPUT`;

Which gives room for execution by having ${alert(1)} as $INPUT.

var a = `$alert(1)`;

If the dollar sign ($) is being correctly sanitized we can still use the previous trick with backticks also, this time even in multiple lines:

var a = `-alert(1)//\`;
var b =



DOM-based XSS – The 3 Sinks

The most common type of XSS (Cross-Site Scripting) is source-based. It means that injected JavaScript code comes from server side to execute in client side.

But there’s another main type, the DOM-based one, where injected malicious input does not come from server via reflected or stored means: XSS is generated in client side by native JavaScript code.

DOM-based XSS also has reflected and stored sub-types, pretty much like it’s counterpart, the source-based XSS. They are exactly the same, with reflected/stored being provided by browser itself via its JS engine and client side storage.

However, unlike in the simple request/response model, DOM-based XSS might involve a lot of analysis of code flow in order to catch it. It’s hard to spot this flaw for static and even dynamic analysis tools.

Sources and Sinks

The DOM (Document Object Model) is the resulting program built over the HTML document received from server (source code) with JavaScript code. As you might know, that code comes embedded into the HTML document via script blocks and/or imported libraries. JavaScript code turns all HTML elements into objects and can manipulate them, altering the document in any possible way at run time.

We have almost the same XSS logic in client side as we have in server side: we need something responsible for the input and another for the output. In server side we have things like $_GET[“var”] or $_POST[“var”] for the input and echo or print commands for the output in PHP language to make our XSS situation arise.

In client side code we have sources and sinks. Think about sources as our inputs and sinks as our outputs. Sources are object properties like document.location, document.referrer, window.name and even the parameters of the URL (after being handled) like in server side XSS. The list is huge because everything can be manipulated by JavaScript and become a potential source of a sink.

So in order to get our study of DOM-based XSS easier let’s look first at the sinks because there are only 3 main cases. Below is a page to demonstrate them. If you are experienced enough with XSS or just want to train yourself, stop this reading right now and XSS them following our #hack2learn principle.


The 3 Sinks

In our demonstration page, we have the following source code:

It has only one element, a paragraph which is dynamically updated by the native JavaScript code in the only script block of the page. There are no imports, no libraries. All 3 cases are highlighted by means of 3 lines of comments to define where the snippet of code responsible for each one of them starts.

Before the first case, Document Sink, our code starts with the API to make possible to handle the value of URL parameters: after taking the search property of document’s location (which itself is property of document) we then create a new object implementing URLSearchParams.

Document Sink

In the Document Sink, the native JavaScript code updates the current document with data controlled or provided by attacker. It makes possible the insertion in the DOM of a whole new HTML tag with javascript code or a new attribute which might be an event handler with JS code.

The value of URL parameter “name” gets stored in “username” variable for later use in an update of the document. If that variable is not null, the paragraph element is updated with the content “Hello” plus the content provided in the URL via “name” parameter. That makes possible for an attacker to insert HTML code with:


Important to note here is the fact that classic “<script>alert(1)</script>” won’t work. Also several other XSS vectors because it’s a DOM insertion not a simple source reflection. Just a few will work like the one provided above.

Document Sink can appear in many ways: with innerHTML and outerHTML element properties, with document.write(), document.writeln() functions and any other way provided by JavaScript libraries to update an element or the document.

Location Sink

In the Location Sink, the native JavaScript code updates the location of the document with data controlled or provided by attacker. In this way, it’s possible to use an interesting feature of modern browsers: the JavaScript pseudo-protocol.

By taking the value of “redir” parameter the code above makes possible to redirect the browser to another address, loading another document. If we use a site we control, it would be of no use to attack a target application, since we would be out of context.

But by using the JavaScript pseudo-protocol, which is a way to run JavaScript code directly into the address bar of browsers, it’s possible to run code over the current document and its context.


In the past, data URI was used to replace that trick when “javascript” keyword was flagged by application filters and WAFs (Web Application Firewall). But now modern browsers provide a blank context for JS execution or don’t even allow the navigation anymore as a security measure.

Execution Sink

In the Execution Sink, the native JavaScript code updates its own code flow with data controlled or provided by attacker.

Usually evaluation sinks make part of big and complex code so above we will see a very simple one: there are 3 variables, one for each major stock index: Nasdaq, Dow Jones and S&P 500. Let’s not consider that their values would  come from a database in a real application, so they are not constantly changing.

The “market” object is created. After getting from URL the value of “index” parameter (to know which index to look for) we try to ensure it’s a string with toString() conversion (just a silly way to try to avoid the outcome). Then our code creates in the object “market” the property “index”, dynamically, with “eval” function. Finally, the current value of the queried index is displayed in the paragraph.

An attacker can simply XSS this by providing the JS code he/she wants to run:


Because that code will go straight to evaluation, it’s simple like that. In real world applications, most probably it will require passing some test or assignment according to code flow in order to reach the flawed point.

Other execution sinks besides “eval” are “setInterval” and “setTimeout” functions and template literals.

Those were the 3 sinks we should be aware of when looking for DOM-based XSS.


Chrome XSS Auditor – SVG Bypass

More than an year ago, in my private twitter account Brutal Secrets, I shared an interesting way to bypass Google’s Chrome anti-XSS filter called XSS Auditor. We will see now in details, from a blackbox perspective, a logical sequence of assumptions and conclusions that leads to our XSS vector responsible for the bypass.

We start with a known source of trouble for all XHTML parsers (browsers) out there: Scalable Vector Graphics or SVG. Without getting deeper into the explanation of what SVG can do (check here), all we need to know is that SVG markup is way more complex than simple XML/HTML and full of unexplored resources for an attacker.

Starting with a simple <svg> tag we proceed using an empty anchor, the <a> tag that creates an hyperlink. Nested to this anchor we will use a rectangle to create a larger clickable area, ending up with something like this:

<svg><a><rect width=100% height=100%>

check here

We are now looking for a way to interact with the element but we can’t use event handlers due to Auditor’s blocking. So we will try one of the tags used in animations, notably the <animate> one. The <animate> tag takes an attribute (with attributeName) of a parent element (in our case the <rect> one) and manipulates its value, like “width” for instance. It creates the animation effect with the help of its own attributes “from”, “to” and “dur” (duration).

<svg><a><rect width=100% height=100%><animate attributeName=width from=0 to=100% dur=2s>

check here

The interesting conclusion here is that we are in fact changing the original value of “width” attribute, in sequence. But what if we target a different attribute? Let’s take the href of the anchor (<a>) which is not set but is implicit. With some tweak in attributes and a self-closed <rect>, we are ready to go.

<svg><a><rect width=100% height=100% /><animate attributeName=href to=//google.com>

check here


<svg><a><rect width=100% height=100%><animate attributeName=href from=//google.com to=?>

check here

By clicking in our rectangle now, we are redirected to Google’s website. So to pop an alert box, we will just try to change it to “javascript:alert(1)”.

Not that easy. Even an attempt to fool Auditor using HTML encoding gets blocked.

<svg><a><rect width=100% height=100% /><animate attributeName=href to=javas&#99ript:alert(1)>

check here

We get back to SVG Attribute Reference and find an interesting alternative to “from” and “to”: animation elements can also use a “values” attribute which provides the same set of values for the animation.

By simply setting “values” to “javascript:alert(1)” we get blocked again. But, surprisingly, this time we pop an alert using the HTML encoded form, “javas&#99ript:alert(1)”. Strange enough, any other arbitrary attribute with our obfuscated payload will fire a blocking but that one seems “whitelisted”!

We change the <rect> for an <image> tag, more suitable to attract a victim’s click. A little addition of text/markup and… Boom!

<svg width=12cm height=9cm><a><image href=//brutelogic.com.br/yt.jpg /><animate attributeName=href values=javas&#99ript:alert(1)>

check here

This bypass was found in version 51, although it might work in several past versions. It currently works on Google Chrome v60, the latest version at the time of this publication.