Advanced levelFree

Reflected XSS on a 429 page via Google Analytics — the tracking snippet as vector

The Google Analytics snippet took part of the URL as unsanitized input. A string-literal breakout inside the script itself: the injection was already in JavaScript context.

Gorka El BochiMay 9, 20269 min

Quick answer

The 429 (Too Many Requests) error page of a subdomain belonging to a Q&A social network reflected part of the URL inside the Google Analytics <script> block, without sanitizing it. Because the input was already in JavaScript context, there was no need to break out of HTML: it was enough to break the string literal with '-alert(document.domain)-'. A classic reflected XSS, but the vector — the GA snippet — is interesting.


1. Context — the 429 error page

When a user makes too many requests to a subdomain of the platform, the server returns a 429 error page. This page includes a Google Analytics snippet that takes part of the URL as input to build its tracking calls.

The problem is that this URL value is inserted directly inside the Google Analytics <script> block without any kind of sanitization, turning it into a JavaScript injection point.


2. The payload and the vulnerable URL

bash
https://subdomain.target.tld/'-alert(document.domain)-'

The part of the URL after the slash ('-alert(document.domain)-') is reflected directly inside the Google Analytics script in the HTTP response.


3. The vulnerable code in the response

html
<script type="text/javascript">
  ...
  ga('set', 'dimension1', 'board-'-alert(document.domain)-'');
  ga('set', 'dimension2', 'False');
  ga('set', 'dimension3', 'False');
  });
});
</script>

The URL value is concatenated inside a single-quoted JavaScript string literal. The payload closes the opening quote with ', injects the alert(document.domain) call through string concatenation (-alert(document.domain)-), and reopens the quote so the rest of the code stays syntactically valid.

Payload breakdown

lua
dimension1', 'board-   ← original string up to where the input lands
'-alert(document.domain)-'  ← attacker input

The resulting sequence in the script is:

javascript
ga('set', 'dimension1', 'board-' - alert(document.domain) - '');

JavaScript evaluates -alert(document.domain)- as an arithmetic operation, which forces alert(document.domain) to be evaluated as part of the expression. The arithmetic result doesn't matter — what matters is that alert(document.domain) executes.


4. Condition to trigger the XSS

The 429 error page is only shown when the server detects too many requests from the same client. For the victim to load the vulnerable page, they first need to be rate-limited. In practice this is achieved in several ways:

  • The attacker sends many prior requests from the victim's IP (non-trivial).
  • The victim is already naturally rate-limited by their own usage.
  • The link is sent directly when they are already in a 429 state.

5. Technical classification

FieldValue
Vulnerability typeReflected XSS — input reflected in a <script> block
CWECWE-79 — Improper Neutralization of Input
VectorURL segment reflected in the Google Analytics snippet
PersistenceReflected — triggers only when loading the malicious URL
PreconditionActive 429 (rate limit) state on the client

6. Technical notes

  • The XSS lives inside an existing <script> block, not in an HTML attribute. This makes it especially direct: there's no need to escape an HTML context, the input is already inside a JavaScript context where any code is directly executable.
  • The vector is the Google Analytics snippet, a third-party block of code that the platform included in its error pages. The attack surface is in how the app built the parameters passed to ga() using URL data without escaping it.
  • The 429 condition slightly limits exploitability compared to a Reflected XSS with no preconditions, but it doesn't eliminate it — the malicious URL is still crafted by the attacker and shareable.

General lesson

When a client value ends up inside a <script> block, the vector is already half-cooked: you only need to break the context of the string literal that contains it. Don't only look at HTML when you audit error pages or tracking snippets — JavaScript context is where you'll usually find the least sanitization.


Practice reflected XSS in script blocks and bypasses with arithmetic concatenation: XSS labs.

Practice this in a lab

Xss

Solve

Keep learning · free account

Save your progress, unlock advanced payloads and rank your flags.

Create account

Related articles

hunters training
711

hunters training

labs from real reports
55

labs from real reports

completions
1,205

completions

in bounties practiced
$213,970

in bounties practiced

46 flags captured this week·Real reports from HackerOne · Bugcrowd · Intigriti·No commitment·Free Academy