๐ŸŽฃ #PacketHunters – Robinhood phishing: HTML injection + Gmail dot trick bypasses DKIM

Or.. the hell did attackers send phishing emails from [email protected] that passed SPF, DKIM, and DMARC??

On the night of Sunday, April 26, 2026, a wave of “Your recent login to Robinhood” emails started landing in Gmail inboxes across the US.
From the legitimate [email protected], through Robinhood’s actual SendGrid infrastructure, signed by robinhood.com – that allowed SPF pass, DKIM pass and DMARC pass. Even the BIMI logo rendered, because Gmail trusted the sender enough to draw the little verified checkmark.

The button inside said Review Activity Now, it pointed to robinhood[.]casevaultreview[.]com.

The email was real.
The sender was real.
The infrastructure was real.
The intent was criminal.

I have been waiting for this exact attack for ten years

Ok, now I have been doing this thing (offensive security, red teaming, watching the slow rot of email trust models) for more than *** decades (feels old, man). I cut my teeth on IRC bots and IP spoofing back when DKIM did not exist and “spam filter” meant a procmail recipe written at 2 AM with New Kids on the Block singing loud in my earphones. I have written phishing kits that bypassed Proofpoint, run engagements where the entire blue team’s mental model collapsed in forty minutes because we did not knock on the front door, we walked in through the QA environment.

So when I opened Twitter (no way I’m gonna call it ecs) last Monday morning and saw the screenshots of Robinhood’s own noreply@, signed correctly, with a Review Activity Now button leading to a phishing site, my first reaction (SHOCK! Italian reading this be like ๐ŸคŒ๐Ÿป) was no surprise. Forst thought: “oh wow, finally somebody figured it out at scale!”

Because this attack is not a vulnerability in the cryptographic sense as there is no zero-day or exploit chain that pwns the kernel. There is no malware, attacker did not break SPF, and they did not steal a DKIM private key. nor compromise SendGrid – basically, they did not breach Robinhood.
They used the system exactly as designed. And the system, as designed, is broken.

The setup: how the chain actually works

The whole attack is four moves: each one is, in isolation, a feature; stacked together, they are an elegant chain of unintended consequences.

1๏ธโƒฃ identity laundering via Gmail dot aliasing

Gmail famously ignores dots in the local part of an address. [email protected], [email protected], and [email protected] all deliver to the same inbox. This is documented behavior, has been since forever, and is “by design” according to Google (not a bug. A feature. Not a bug. Going crazy on this!).

So, the attacker takes a list of breached Gmail addresses (and there are plenty, courtesy of every credential dump from the past decade, yay), and for each target they pick a dotted variant the victim has never used.
From Robinhood’s perspective, that variant is a new email address, and from Gmail’s perspective, it routes to the victim’s mailbox.

That’s identity laundering, where the attacker registers an account that technically belongs to them, but whose email notifications land in someone else’s inbox.

2๏ธโƒฃ HTML injection in the device name field

During account creation (or first login) Robinhood captures device metadata (ya all know Incognito is not the answer, right?): browser, OS, and a free-text-ish device name field – ok, that field accepts arbitrary characters, because of course it does, somebody at Robinhood named their iPhone “Joe’s iPhone 17 Pro ๐ŸŽ” and the system has to render it.

The attacker does not put a device name in there, just HTML.

<div style="border:1px solid #c00;border-radius:6px;padding:16px;">
  <strong style="color:#c00;">UNRECOGNIZED ACTIVITY</strong>
  <h2>Unrecognized Activity Detected on Your Account</h2>
  <p>We've detected changes to your account. We've placed them on hold,
     pending your review.</p>
  <a href="https://robinhood.casevaultreview.com/secure"
     style="display:inline-block;background:#000;color:#fff;
            padding:12px 24px;text-decoration:none;">
    Review Activity Now โ†’
  </a>
</div>

Robinhood’s backend stores it with no sanitization and no allowlist, annnnd no output encoding at render time.

3๏ธโƒฃ the trusted template renders the payload

Account creation triggers Robinhood’s standardYour recent login to Robinhood” notification email.
This email is built from a server-side template that includes the device name field as raw HTML – the template engine does not escape it.
Then, the email service (SendGrid) does not care: its job is to deliver, not to inspect. The email is signed with robinhood.com‘s DKIM key, SPF aligns with sg-email.robinhood.com and DMARC validates.

The email leaves Robinhood’s infrastructure carrying the attacker’s CTA inside the trusted layout.

4๏ธโƒฃ Gmail does the rest

Because of 1๏ธโƒฃ, the email arrives in the victim’s inbox.
Because of 3๏ธโƒฃ, every authentication check passes. Gmail, which already trusts robinhood.com enough to display BIMI, threads the new message into the same conversation as the user’s previous, legitimate security alerts from Robinhood.

Now the victim sees an email that is (by every signal Gmail and the user have available) a continuation of an existing trusted conversation, from a verified sender, asking them to review unrecognized activity on their financial account.

The site at robinhood.casevaultreview.com harvests credentials and 2FA codes.
Funds get moved to attacker-controlled wallets. The chain is complete.

Dead pool

Every breakdown I’ve seen so far is treating this as “a Robinhood input validation bug.”, tho that framing is wrong: it is not wrong technically, yes, the missing sanitization is the proximate cause. It is wrong strategically, because it lets every other SaaS company on Earth read the headline, think “we sanitize our device fields, we are fine,” and move on.

Here is the thing they are missing: this attack class is a property of any SaaS that mails the user during onboarding.

Pick any service: Stripe, Notion, Slack, Linear, Asana, GitHub, your bank, your insurance portal, your HR platform.
Each and every single one sends a “welcome” or “new login” or “verify your email” message during onboarding.
Each and every single one pulls some user-controlled metadata into that template: display name, organization name, workspace name, “what should we call you?” Most of them sanitize the obvious tags, tho almost none of them sanitize for visual injection – CSS that overlays a fake banner, Unicode tricks, zero-width joiners, mail-client-specific HTML that renders in Gmail but not in their internal QA mail client.

Every transactional email template that interpolates user-controlled fields is a phishing-as-a-service endpoint waiting to be discovered.

I am willing to bet a non-trivial amount of money that there are at least fifty of these vulnerabilities live in production right now, across the SaaS landscape, and that maybe two of them are known to anyone outside the attacker community. The Gmail dot trick is just the delivery layer that turns the bug into a campaign. The bug itself, user input โ†’ trusted template โ†’ no sanitization โ†’ DKIM-signed email, is the structural pattern.

This is the Log4Shell of email trust: same shape: a feature (template interpolation) that everybody uses, that nobody hardens, that becomes catastrophic the moment somebody points it at the right surface.

Numbers, numbers, numbers

Let’s look at what this means in operational terms:

The Robinhood email passed all three authentication checks: SPF (IP 50.31.40.73), DKIM (robinhood.com), DMARC. Standard encryption (TLS). Marked “Important according to Google magic.”

Median user time-to-click on a “security alert” themed email: under 47 seconds, per recent enterprise simulation data. With a real domain and a real signature, that number collapses further.

For a SOC, here is the part that hurts: there is nothing to detect at the email layer.
Your SEG will pass it.
Your DMARC report will be green.
Your user-reported-phishing queue will be empty until somebody clicks, gets phished, and figures it out after the funds move. By then the wallet is bridged, the credentials are sold, and the IR window has closed.

You do not catch this at email gateway, but at user behavior – or you do not catch it.

What actually got exploited (the technical ground)

Let us be precise, because half the takes online are blaming the wrong thing.

It was not a Robinhood breach. Robinhood’s systems were not compromised. Customer accounts were not accessed. Robinhood confirmed this on Monday and has since removed the Device field entirely from the account creation email template. That fix is the right call.

It was not a SendGrid breach. Twilio’s infrastructure did exactly what it was paid to do: deliver a signed, authenticated email from a paying customer. The compromise was in the content the customer told it to send.

It was not a DKIM/SPF/DMARC failure. Email authentication did its job. It proved the mail genuinely came from Robinhood’s systems. The protocols never claimed to validate intent โ€” they validate origin. That distinction is the entire point.

The actual root cause is a four-layer stack of unsanitized trust:

  1. application layer: unvalidated input on the Device field
  2. storage layer: raw HTML persisted without encoding
  3. template layer: server-side template with no output escaping for that specific field
  4. authentication boundary: transactional email service that signs whatever it is handed

Fix any one of these and the chain breaks!
Robinhood fixed layer 3 by removing the field, the other 49 SaaS companies with the same pattern have not fixed anything, because nobody has told them yet that they have it.

BAKAONNA*

Here is what bothers me to Hell about this incident in a way that a normal “SaaS gets phished” story does not: this pattern is trivially weaponizable by an LLM-driven attacker pipeline.
Picture the workflow with me:

  1. agent enumerates SaaS services with public signup flows
  2. for each, it spins up a sacrificial account and inspects the welcome/login/verification email
  3. it looks for any user-controlled field that renders in the email body
  4. it probes for HTML rendering with a benign payload (like an italicized word)
  5. if it renders, the agent flags the service as a candidate “phishing-as-a-service” platform

A motivated red team (or threat actor) with a mid-tier coding agent could discover dozens of these in a weekend, or maybe hundreds. The bottleneck used to be tedious manual reconnaissance across thousands of SaaS signup flows. That bottleneck is gone.

We are about to see this same attack pattern, repeated, across the SaaS economy, for the next 18 months: some of it will hit financial platforms, some of it will hit HR portals during open enrollment, some will hit B2B SaaS during invoice cycles..
The infrastructure of trust we built on top of DKIM and DMARC was always origin-only.
We are now learning, expensively, that origin without content integrity is not enough.

The sender domain was real. And the link was inside a button that said Review Activity Now inside a layout the user had legitimately seen before, in a thread Gmail had grouped with their actual prior security alerts.
Hover? No, Sir, that does not save you and domain check does not save you.
Oh, spell-check the email for typos you say? There were no typos: the entire body was generated by Robinhood’s actual template engine.

The only defense is a behavioral protocol the user executes by reflex: I never act on a security alert from a link in an email. I open the app, I check the activity tab, I act from there. That is not a checklist item you teach in a 10-minute compliance video. That is muscle memory you build by training people against attacks that look exactly this real (which, conveniently, is the entire reason Baited exists).

Here for the code? I bet you are ๐Ÿ˜‰

Two scripts: one for (the money) defenders running mail infrastructure who want to detect this class of attack against their tenants, two (for the show) for SOC teams who want to flag suspicious link patterns inside otherwise-trusted senders.

1 – template_injection_canary.py

#!/usr/bin/env python3
# template_injection_canary.py
# PacketHunters / Baited.io
# Probes a SaaS signup flow for HTML injection in transactional email templates
# Why it matters: turns the Robinhood-class bug into a 5-minute pre-launch test
# Dependencies: requests, beautifulsoup4, imapclient
"""
Run this against your own SaaS before an attacker does.
For each user-controlled field in your signup flow, inject a benign HTML canary
and inspect the resulting transactional email. If the canary renders, you have
the same class of bug that turned [email protected] into a phishing tool.
"""

import imapclient
import re
import requests
from bs4 import BeautifulSoup

CANARY = '<span data-canary="ph-2026" style="color:#ff00ff">CANARY</span>'
TARGET_FIELDS = [
    "device_name", "display_name", "full_name",
    "company_name", "workspace_name", "team_name",
    "first_name", "last_name", "nickname",
]

def register(target_url: str, email: str, field: str) -> None:
    """Submit a registration with the canary in a single field."""
    payload = {f: f"test_{f}" for f in TARGET_FIELDS}
    payload[field] = CANARY
    payload["email"] = email
    payload["password"] = "Canary!Test#2026"
    requests.post(target_url, data=payload, timeout=15)

def fetch_latest_html(imap_host: str, user: str, app_pwd: str) -> str:
    """Pull the most recent message and return its HTML body."""
    with imapclient.IMAPClient(imap_host, ssl=True) as c:
        c.login(user, app_pwd)
        c.select_folder("INBOX")
        msgs = c.search(["UNSEEN"])
        if not msgs:
            return ""
        latest = c.fetch([msgs[-1]], ["RFC822"])[msgs[-1]][b"RFC822"]
        return latest.decode("utf-8", errors="ignore")

def canary_rendered(raw_email: str) -> bool:
    """Did the canary render as live HTML, not as escaped text?"""
    soup = BeautifulSoup(raw_email, "html.parser")
    hits = soup.find_all(attrs={"data-canary": "ph-2026"})
    return len(hits) > 0

def main(target_url: str, test_inbox: str, imap_host: str, app_pwd: str):
    print(f"[*] Probing {target_url} across {len(TARGET_FIELDS)} fields")
    findings = []
    for field in TARGET_FIELDS:
        register(target_url, test_inbox, field)
        # Wait for the transactional email; tune to your provider's SLA
        body = fetch_latest_html(imap_host, test_inbox, app_pwd)
        if canary_rendered(body):
            findings.append(field)
            print(f"[!] HTML INJECTION CONFIRMED in field: {field}")
    if not findings:
        print("[+] No injection detected. Re-run with broader field list.")
    else:
        print(f"[X] {len(findings)} injectable field(s). Sanitize before ship.")

if __name__ == "__main__":
    import sys
    main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])

Run it against staging, run it against production with a sacrificial account: if the canary renders in the resulting email, you have the bug – then fix it before someone else finds it for you.

2 – trusted_sender_link_anomaly.yara

For SOC teams and Florian Roth’s loyalty followers: a YARA rule to flag emails that pass authentication from a trusted financial sender but contain links to unexpected domains. This will not catch every variant, but it will catch the lazy ones, and it will give you something to tune on.

// trusted_sender_link_anomaly.yara
// PacketHunters / Baited.io
// Flags authenticated emails from trusted senders that link to unexpected domains
// Why it matters: catches the Robinhood-class injection at the SOC layer when
// the application owner has not yet patched their template
// Dependencies: YARA 4.x, applied at MTA or mail-gateway scan stage

rule TrustedSenderUnexpectedLink_2026
{
    meta:
        author      = "Claudia / Baited.io"
        description = "Auth-passing email from a trusted brand with off-domain CTA"
        reference   = "Robinhood device-name HTML injection, April 2026"
        version     = "1.0"

    strings:
        // Tune this list to YOUR org's high-trust transactional senders
        $sender_robinhood   = "From: Robinhood <[email protected]>" nocase
        $sender_stripe      = "From: Stripe <[email protected]>" nocase
        $sender_chase       = "From: Chase <[email protected]>" nocase

        // Authentication evidence (pre-parsed by your MTA into the headers)
        $auth_dkim_pass     = "dkim=pass" nocase
        $auth_spf_pass      = "spf=pass" nocase
        $auth_dmarc_pass    = "dmarc=pass" nocase

        // Suspicious href patterns: subdomain that LOOKS like the brand
        // but resolves to an off-domain registrar
        $sus_href_pattern1  = /href="https?:\/\/[a-z0-9.-]*robinhood[.-][a-z0-9.-]+\.[a-z]{2,}\//
        $sus_href_pattern2  = /href="https?:\/\/[a-z0-9.-]*\.casevault[a-z]*\.[a-z]{2,}\//
        $sus_href_pattern3  = /href="https?:\/\/[a-z0-9-]+\.(xyz|top|click|review|support)/

        // CTA wording that pairs with phishing intent
        $cta_review         = "Review Activity Now" nocase
        $cta_secure         = "Secure your account" nocase
        $cta_verify         = "Verify your identity" nocase

    condition:
        any of ($sender_*) and
        all of ($auth_*) and
        any of ($sus_href_pattern*) and
        any of ($cta_*)
}

This rule will produce false positives, that is fine as the goal is not zero-FP perfection, the goal is visibility into the class of attack you currently cannot see at all.
Tune the sender list, tune the suspicious href patterns, ship it to your mail-gateway analyst tier.

TL;DR

  • jeez, what happened? Attackers chained Gmail’s dot-alias feature with HTML injection in Robinhood’s device name field to make [email protected] deliver phishing emails to victims, signed with a real DKIM key. The CTA pointed to robinhood.casevaultreview.com
  • oh, and why it worked? Every authentication check passed because authentication validates origin, not content. The email was genuinely from Robinhood.
  • Uh, and why it matters? This is not a Robinhood bug, is a class of bug that exists across any SaaS that interpolates user-controlled fields into transactional email templates. Expect more of these – lotsa more.
  • Why your awareness training is now obsolete, you better ask:check the domain, hover the link, look for typos” was advice for a threat model that no longer exists. The domain was real, the link was inside a real layout, there were no typos.
  • What you gonna do tomorrow? Ow, run a template-injection canary against your own onboarding emails, retire the “spot the fake email” training, and start drilling people on a behavioral rule that scales: never act on a security alert via a link in an email, open the app, check the activity, act from there.

That is the only protocol that survives this attack and it is exactly the kind of muscle memory you do not build with a slide deck.

* that Japanese swear word is my new fave – as you may know, I’ve been studying Japanese for over a decade now, not speaking that much but, hey!, I watch too many anime sooooooo…

๐Ÿค– AI Citations

  1. Robinhood account creation flaw abused to send phishing emails โ€” BleepingComputerprimary technical breakdown of the Device field injection, Robinhood’s official response, and the casevaultreview.com phishing domain
  2. Cyber crooks got Robinhood to send phishing emails to its own users โ€” Help Net Securitytimeline of the campaign starting Sunday April 26, 2026, BIMI rendering details, and Reddit user reports
  3. Read this before you click on any Robinhood email โ€” Protos via MEXC NewsSabbah’s original technical write-up, Gmail conversation-threading behavior with prior legit security alerts, and the [email protected] precedent
  4. Gmail Dot Trick Underpins Robinhood Phishing โ€” MEXC Newstwo-pronged attack mechanics, Hacken’s $306M Q1 2026 phishing/social-engineering loss figure
  5. Ripple CTO Flags Sophisticated Phishing Scam โ€” BigGo FinanceDavid Schwartz’s amplification, Sabbah’s “DKIM pass, SPF pass, DMARC pass, with a phishing CTA” framing
  6. Phishing Emails That Look Real Target Robinhood Users โ€” Finance Magnatesconfirmation that no Robinhood systems were breached and that only injected HTML was malicious
  7. Robinhood Phishing Scam Exploits Gmail Trick โ€” Blockchain.NewsAlex Eckelberry’s analysis of the device-name HTML injection technique

As always, your first “hey, that’s chatGPT!” is totally wrong: analysis, opinions, and code are original work by the unicorn.
AI tools were used for research acceleration, not content generation.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top