October 14, 2026
Brevo’s Attacker Didn’t Need to Forge a DNS Record. A Cloudflare Key Let Them Write One.
For five and a half hours a fake verification page ran on Brevo’s own domains while its servers stayed untouched. Reading the post-mortem…
By Josepollman
5 min read
For five and a half hours a fake verification page ran on Brevo's own domains while its servers stayed untouched. Reading the post-mortem sent me back to one line in my own DNS checker.
Brevo, the email marketing company, published a post-mortem about an attack on its own websites on 14 September. One sentence in the root-cause section is why I read it twice:
Because the Worker rewrote responses at the edge and removed security headers such as Content-Security-Policy, our origin servers and files remained unmodified and standard integrity checks did not detect the change.
Nothing on Brevo's servers was altered. The pages visitors received were. Everything in between happened inside a CDN account, and what opened that account was one API key.
What happened on 14 September
The timeline in the post-mortem is short. All times are UTC.
At 14:23 the attacker created a hostname on a Brevo-owned domain. At 14:28 they deployed a Cloudflare Worker, a small script that runs at Cloudflare's edge and can rewrite a response before it reaches the visitor, and tested it on low-traffic Brevo domains. At 15:01 the Worker was routed to all of brevo.com. At 16:07 it was updated to append a loader to three JavaScript files that Brevo customers embed on their own websites: the forms script, the Conversations widget and the SDK loader.
Some visitors got a full-screen page dressed up as a Cloudflare "verify you are human" check. It asked them to press Win+R, then Ctrl+V, then Enter, which runs a command the script had already put on the clipboard. That command downloaded malware onto Windows machines. The technique is known as ClickFix. Brevo says the page "was shown selectively, so most visitors and repeat visits saw nothing." On WordPress sites that embedded a Brevo widget, if the visitor was a logged-in administrator, the script also tried to install and activate a plugin.
The security incident was opened at 19:33, about four and a half hours after the impact began. The Worker and its routes were gone by 20:30. Brevo lists app.brevo.com, its API, email sending and customer account data as not affected.
The key
Here is the root cause in Brevo's words: "A long-lived Cloudflare API key with full account permissions was stored in application source code and was obtained by the attacker. With it, they could create Workers, routes and DNS records on Brevo's zones without triggering an alert."
Two details matter. "Full account permissions" means the key could do far more than whatever job it was made for. And the investigation says the key "was first misused in late August 2026". Brevo found no injected content before 14 September, but someone had the key for weeks.
Brevo published all of this plainly, with times, scope and an unglamorous cause. That is the only reason the rest of this piece can be written.
The line in my own tool
I run notspoofed.com, a checker that reads a domain's public DNS records (SPF, DKIM, DMARC, MX, MTA-STS and DNSSEC) and says what to fix. This morning I ran it on its own domain. One of the green boxes in the result says notspoofed.com "is signed, so its DNS answers", every record on the page included, "cannot be forged in transit."
That sentence is true. DNSSEC lets a resolver confirm that an answer came from the zone and was not altered on the way. It cannot tell anyone how the record got into the zone. DNSSEC signs whatever the zone holds, and Cloudflare described in a 2015 blog post how it generates those signatures at the edge, on demand. A record created through the API with a stolen key goes through the same signing as one the owner typed into the dashboard. It comes out signed and valid, and nothing in the answer separates the two.
The same holds for everything else my checker reads. It reports the SPF record, the DMARC policy and the DKIM keys as they are published at that moment. Suppose someone holding a key like Brevo's had added an include to an SPF record, or published a DKIM key of their own on a new selector. My tool would have reported a correctly formed record, because it would have been one. It has no idea who wrote a record or when.
As far as the post-mortem says, Brevo's attacker did not touch mail records. They created a hostname and pointed web traffic at a script. But the key allowed DNS changes on Brevo's zones, and mail authentication is made of DNS records.
Where the evidence was
Brevo's integrity checks looked at its origin servers and files, and those were clean the whole time. The change lived in configuration: a Worker, its routes, a hostname. An outside DNS check like mine looks at the published result and sees well-formed records. Neither view registers the change as a change.
The account itself is what recorded it. Look at what Brevo says it is changing:
- "Alerting on every Cloudflare audit event that changes Workers, routes, DNS or account access, reviewed by the security team."
- Cloudflare logs streamed into its security monitoring.
- Every Cloudflare key and token kept in HashiCorp Vault, "with automatic rotation and no credentials in source code or configuration files."
- "Integrity protection for versioned embedded assets where technically possible, and regular external scanning of our public pages and scripts for injected content."
For a small operator the first item matters most, because an audit event is the only record of who changed a zone and when. The last item is an outside view, the kind my tool takes, and Brevo lists it as one layer among several.
What I looked up on my own setup
All five of my sites (notspoofed.com, vetthisvendor.com, whattofixfirst.com, subcovered.com and knowyourmouth.com) answer from the same two Cloudflare nameservers. I confirmed that this morning with a plain NS lookup. So Brevo's question applies to me directly: whoever can edit those zones can change the mail records of all five domains, and every outside check, mine included, would report the result as genuine.
The part I cannot answer from outside is the part that decides it: which credentials can edit those zones, how widely each one is scoped, and whether a change would alert anyone. That only exists inside the account, in the token list and the audit log. My tool cannot see it, and neither can any other public DNS checker.
If your DNS lives behind an API
None of this is new, and all of it is easy to put off:
- List every API key and token that can touch your DNS or edge configuration, and what each one is allowed to do. A token that only needs to purge a cache should not be able to create DNS records.
- Keep long-lived keys out of source code. Brevo replaced its key with "narrowly scoped, short-lived tokens."
- Get alerted when a DNS record, a route or an edge script changes. Brevo's attacker had more than four hours on brevo.com before an incident was opened.
- Treat an outside check as a snapshot of what is published. A clean result says the records are well formed. It does not say they are the records you put there.
The second question
The usual question about a DNS record is whether it is correct. Brevo's post-mortem adds a second one: who else can change it. Signing, a strict DMARC policy and a clean scan all answer the first question. Only the account can answer the second.
My check of notspoofed.com still shows "1 issue worth fixing": a 1024-bit DKIM key that my mail provider has to reissue. That one I can see from the outside.
Sources: Brevo, "Post-mortem: malicious "ClickFix" script served via Brevo's Cloudflare account" (status.brevo.com, 14 September 2026); Cloudflare blog, "DNSSEC Done Right" (29 January 2015). I build notspoofed.com, a free SPF, DKIM and DMARC checker.