attacker-controlled-site.com
Published 2026-07-25

I registered the domain from the security tutorials

attacker-controlled-site.com sat in advisories and documentation for years while staying publicly registrable. What I found, and what I chose not to build.

If you write about web security, you eventually need a name for the attacker’s server. Something that reads as unmistakably fictional, so the reader knows it is a stand-in and can concentrate on the bug.

attacker-controlled-site.com is a good name for that. It is descriptive, it is obviously a placeholder, and it turns up in a reverse-proxy hijacking writeup, in an explanation of password-reset poisoning, in an account-takeover advisory, in a data-exfiltration walkthrough.

It was also available. Anyone could have bought it. In July 2026 I did.

What it was being used for

Each of these was fetched and checked against the live page before being listed. The full list, with dates, is the reference index.

In an NGINX writeup it is the upstream in a malicious proxy_pass block, quietly forwarding live login traffic away from the real backend. In a piece on insecure password resets it is the value of a poisoned Host header, so the reset link mailed to the victim points at the attacker. In an advisory for a critical account-takeover flaw it is where the reset token ends up. In an exfiltration walkthrough it is /beacon, receiving stolen data one custom HTTP header at a time.

Every one of those is a well-written explanation of a real bug. That is precisely what makes the shared placeholder interesting: the better the writeup, the more likely someone reproduces it exactly as written.

The thing that actually matters

Register a domain like this and you inherit a specific, uncomfortable position: broken systems may send you their secrets.

Not hypothetically. The whole reason this name exists in those articles is that it marks the place where a stolen token is supposed to land. A copied proof of concept, a forgotten test config, a genuinely misconfigured deployment — any of them can turn a documented example into a live request carrying a real password-reset token, a real OAuth code, a real session cookie.

So there is exactly one decision that matters here, and everything else is decoration: do you keep any of it?

If you run a catch-all mailbox, log query strings, record request bodies, store Authorization headers or keep IP addresses, you have built a credential harvester. A well-intentioned one, with a nice explanatory page on the front, and that changes nothing about what is on disk. You would have become the thing the tutorials warned about, using the domain they named for the purpose.

The answer is to keep nothing, and to make that structural rather than a promise.

This site is statically generated and served from a CDN. There is no application, no database and no server-side code of any kind. Nothing here is choosing not to record your request; there is nothing here that could. Every unknown path — /reset, /log, /beacon, /callback, whatever your client sends — gets the same static 404, which does not echo the path or the query string back into the page, because doing that would put whatever it contains into the DOM.

One caveat stated plainly: requests traverse a CDN, which keeps its own edge logs outside this project’s control. That is why the claim on this site is that this project retains nothing, rather than the stronger and untrue claim that nothing is ever logged anywhere.

The domain also publishes a Null MX record (RFC 7505), so it accepts no mail at all. That is deliberate: password-reset poisoning delivers tokens by email, and a catch-all mailbox on this name would be the single most sensitive thing anyone here could operate. There is no mailbox to compromise, because there is no mailbox.

What I decided not to build

Plenty of things would be technically interesting. Most of them are indefensible on this particular domain.

A public sinkhole dashboard. Counting inbound requests by category sounds harmless and mostly is — right up to the point where “mostly” is doing the heavy lifting. Anything that classifies a request has read it. Anything that aggregates it has stored something derived from it. The interesting version of this feature is the version that leaks.

Anything webhook-shaped. A webhook.site-style inspector, where you get a URL and see what arrives, would be genuinely useful software. On this name it would be an exfiltration endpoint with a friendly UI, on a domain already published in exfiltration examples.

A redirect checker. Paste a URL, find out where it goes. It needs a server making arbitrary outbound requests, which is SSRF as a service, and it has nothing to do with this domain’s actual story.

Login-shaped anything. Even a phishing-awareness demo with fake fields. Browsers autofill, password managers autofill, and people type real credentials into things that look like login forms. That is the entire premise of phishing.

What is left is a page that explains the bug classes, a reference index of where the name was used, two tools that run entirely in your browser, and a request for the people whose systems land here to go and fix them.

If you got here from your own logs

Something in your stack is treating a documentation placeholder as a real destination. That is worth finding, and it is usually a single literal string in a config file or a copied payload.

Search your code and configuration for attacker-controlled-site.com. Check your OAuth and login redirect allowlists. Confirm your password-reset links are built from a configured base URL rather than the incoming Host header. Check reverse-proxy targets and any outbound-fetch allowlists.

This site kept nothing. Your logs might have, and so might every hop in between — treat anything that reached here as potentially exposed and rotate accordingly.

The part worth taking away

The domain was fictional. The risk was not.

There are names reserved by standard so that documentation never has to point at something buyable: .example, .test, .invalid. Writing attacker.example makes exactly the same point, and no one can ever register it.

That is the whole lesson, and it is worth ten minutes the next time you write a payload into a blog post.