LSN โ€” Local Sovereign Network

TI-2026-060D โ€” The Redirect That Told the Truth

Classification: HIGH ยท Mechanism Analysis Confidence: HIGH โ€” direct sensor evidence, decoded verbatim from the wire TLP:WHITE โ€” attacker target paths shown in full; the operator's own hostnames are redacted. Series: The 200 That Lied โ€” Part D. Every prior part fought the mask. This part discovers that the strongest mask in the corpus is also, by its own design, an informant.

The cold open: a scanner asks for a secret, is handed a login page, and leaves satisfied

A scanner probes one of our authentication-protected services for /.env โ€” the flat file where web applications keep their database passwords, API keys and cloud credentials. It expects one of two answers: the file (jackpot), or a 404 (move on). Instead, a forward-auth proxy โ€” Authelia โ€” steps in front of the request and returns something the scanner did not ask for and cannot use: a 200 OK and a login single-page app.

To the scanner's tooling, this reads as "protected, no file here." It moves on. To a status dashboard watching our edge, it reads as 200 โ€” success, green, a satisfied visitor. Across this series we have called that the mask, and it is the strongest mask we have found: not a catch-all guessing a 200, not an SPA defaulting to one, but a security control deliberately answering every hostile probe with the same success code a real user gets, by design, as its whole job.

And yet. When Authelia intercepted that request, it had to solve a mundane problem: after the user logs in, where do we send them back to? It cannot forget the original destination โ€” so it writes it down, in a query parameter on the login URL, called rd (redirect). URL-decode that parameter and there it is, verbatim, the thing the 200 concealed:

rd = https://<redacted-host>/.env       (the target)
rm = GET                                 (the method)

The proxy that produced the perfect mask also produced a perfect record of what it masked. The scanner asked for a secret, was handed a login page, and walked away certain it had left no trace โ€” while the security control that stopped it quietly wrote its exact intent into the redirect. This part is about that redirect, and about the paradox at its heart: the best hiding place in the modern web is also the best witness.

The mechanism: how a mask becomes a confession

It is worth being precise about why this works, because the paradox is not luck โ€” it is structural, and structural things generalise.

A forward-auth proxy sits in front of a protected service. Every request is first shown to the proxy, which asks a single question: is this user authenticated? If yes, the request passes through. If no โ€” and an unauthenticated scanner is always "no" โ€” the proxy does not return the resource and does not return a 404. It returns its login interface, so a real user can sign in. That interface is a normal web page: HTTP 200.

But a login page is useless unless it can send the user back to where they were going. So the forward-auth flow embeds the original request in the login URL. In Authelia's case, two parameters carry it:

Our pipeline does the decode automatically: it extracts rd, URL-unescapes it (often double-encoded), splits it into rd_target_host and rd_target_path, and recovers rm as the effective method. The result is that a masked 200 is re-expressed as its effective_status (401 โ€” unauthorised) and annotated with the exact resource the attacker was after. The status told us nothing; the redirect told us everything.

This is the general principle the whole series orbits, stated at its sharpest: the features that make a system convenient are the same features that make an attack against it invisible โ€” except where the convenience must, mechanically, keep a record. A catch-all 200 keeps no record and is pure mask. An auth-proxy 200 must keep the redirect, and so the mask leaks. Find the place where the convenience is forced to remember, and the camouflage becomes evidence.

What the redirect said: a .env dictionary attack, in full

Decode the rd parameter across the corpus and a campaign resolves out of the wall of 200s that no status dashboard could ever have seen. The dominant target is not a page. It is a secret, guessed by dictionary:

Decoded ?rd= targetHitsWhat it is
/.env35the primary prize โ€” app/cloud secrets
/.env.local ยท /.env.development14 ยท 14local & dev deployment secrets
/.env.production ยท /.env.prod13 ยท 9production secrets
/.env.test ยท /.env.staging12 ยท 10pre-prod secrets
/.env.dist ยท /.env.sample ยท /.env.example10 ยท 10 ยท 9template files devs forget to delete
/.env.save ยท /.env.backup ยท /.env.bak10 ยท 9 ยท 9careless backups โ€” the softest hit
/api/.env ยท /.env.dev9 ยท 9nested & alt-named variants
/.git/config17source-repo config โ†’ full code + secrets
/wp-includes/id3/license.txt80a WordPress version tell (fingerprinting)

Count them: more than fifteen distinct .env filename variants. This is not a scanner that tries /.env once and gives up. It is a dictionary โ€” a curated list that anticipates every deployment convention (production, staging, local), every framework default (.env.dist, .env.example), and every human error (.env.bak, .env.save, the file you copied "just in case" before an edit). Each variant is a hypothesis about how this victim might have been careless, and the attacker tests all of them. /.env.bak and /.env.save are the tell of a professional: they are not guessing that the secret is exposed; they are guessing that a backup of the secret is exposed, which is the mistake even careful teams make.

Every single one of these requests returned 200. Every single one of them is, on a status dashboard, a satisfied visitor. And every single one of them was recorded, filename-exact, in a redirect parameter the attacker never imagined anyone would read. This is the .env-harvesting industry โ€” the one documented in this corpus as The .env Harvesters (046A) and The Keys Left in the Door (059E) โ€” operating at full volume behind an auth proxy, measurable only because the proxy confessed.

This is not "Scanners in the Mirror"

A companion dossier, TI-2026-059D โ€” Scanners in the Mirror, looked at the auth proxy too โ€” but at a different problem: how to triage the scanner-fingerprint bucket, separating real hostile scanners from crawler-impersonators from legitimate search bots that all get the same login 200. That is the classification question โ€” who is knocking.

Part D is the forensic question โ€” what they asked for, read verbatim from the mechanism. 059D taught you not to blanket-ban the bucket; 060D shows you that inside the bucket is a decoded, filename-exact confession of intent, and that the confession exists because the auth flow is architecturally incapable of not writing it down. The two are complements: one tells you which knocks to take seriously, the other tells you exactly what each serious knock was reaching for.

The seven questions

Who is this? The .env-harvesting cohort โ€” the same broad population documented across 046A/059E โ€” now observed operating behind our auth proxy, its intent legible only through the decoded redirect.

Why did it attack? Credential theft. A .env file is a single flat file that can contain a database password, a cloud IAM key, an SMTP login and a third-party API secret all at once. It is the highest-value-per-byte target on the web, and the dictionary of variants is a machine for finding it wherever a developer left it.

Who does it claim to be? It makes no claim โ€” it relies on the proxy's 200 to make the claim for it, to the defender. The mask is not the attacker's costume; it is the defender's own instrument, borrowed against them.

Whom is it related to? The .env census ties directly to the credential-harvest campaigns of the wider corpus (046A, 059E) and to the same probe-intent (credential_harvest) that lights up across the honeypot and web layers. The redirect is one more lens on an actor set the entity graph already tracks.

Where is the money? A single leaked production .env is worth more than a thousand webshells: it is instant, authenticated access to the victim's cloud account, database and mail โ€” sold directly or drained directly. The dictionary is cheap; the payout is a company.

What is the official anchor? The auth proxy itself is the anchor of this finding โ€” a legitimate, widely-deployed open-source security control (Authelia and every forward-auth proxy like it) whose correct, standards-compliant behaviour produces both the mask and the record. There is no misconfiguration here to blame; the paradox is in the design.

What if it is not who it claims / what is hidden? See below.

What ifโ€ฆ

Steelman: the strongest case that the redirect is a distraction

"You are congratulating yourselves for reading a parameter your own proxy generates. The ?rd= is an artefact of your specific stack; most of the web does not use forward-auth, so this 'confession' is a curiosity, not a general method. And a blocked .env probe is a non-event โ€” the file wasn't there, nobody got owned. You have written a dossier about an attack that failed, decoded from a field only you have."

It is the fairest challenge in the series, and it is wrong in a way that matters:

The steelman is right that we decoded a field peculiar to our stack. It is wrong that this makes the finding peculiar. Confidence: HIGH.

Read between the lines

There is a strange justice in the mechanism, and it is the emotional core of this whole series. Three times now the villain has been a success code standing where a warning should be โ€” the SSRF 200 of Part A, the catch-all 200 of Part B, the WebDAV 207 of Part C. Each was the web's convenience turned into the attacker's camouflage. The .env scanner arrived expecting the same deal: hide inside a 200, leave no trace.

But the auth proxy is a different kind of 200. It is the only mask in the series that cannot afford to forget, because forgetting would break its actual job โ€” sending a real user home after login. And so, uniquely, its convenience does not merely camouflage the attack; it records it. The attacker exploited the defender's instrument to become invisible, and the very same instrument, doing the very same job, wrote their intent into a parameter and handed it to us. They hid inside the one 200 that keeps a diary.

That is the lesson to take from Part D and carry to the rest of the web: do not ask a system what status it returned. Ask it what it had to remember to do its job โ€” and read that. Convenience that must keep state is convenience that must keep evidence.

The human layer

Behind /.env.production is a specific, ordinary catastrophe. Somewhere a developer, under deadline, committed a production secrets file to a web-reachable directory, or left a .env.bak after a late-night fix, and moved on. They will never know a machine guessed all fifteen names for that file, because on their server โ€” with no auth proxy, no decode, no sensor โ€” every guess returned a 200 or a 404 that told them nothing. The first they will hear of it is a cloud bill for someone else's crypto-mining, or a ransom note, or a customer database for sale. The redirect we decoded is, in the end, a list of the exact mistakes that end companies โ€” written down by the only witness in the room, on a case where, this time, no one got hurt. The census is a warning we can publish because it is, for once, a near-miss instead of an obituary.

Defense โ€” decode the redirect, and hunt the dictionary

Two controls fall out of Part D. (1) Decode, do not trust. A 200 from a forward-auth proxy is a 401 with a payload; extract the rd/rm (or your proxy's equivalent), recover effective_status and the true target, and score that. Any stack running forward-auth already has this evidence โ€” it is one URL-decode away from being intelligence instead of noise. (2) Hunt your own dictionary. The decoded census is a wordlist: /.env and its fifteen variants, /.git/config, the framework tells. Turn it around โ€” make sure not one of those paths is reachable on your estate, on any host, auth-proxied or not. The attacker did the work of enumerating every way a secret leaks; the redirect handed you their list; the only remaining task is to be certain the list comes back empty.

Investigation metadata

FieldValue
DossierTI-2026-060D โ€” The 200 That Lied (Part D)
Date2026-07-05
Primary sensorLSN web-threats layer โ€” Authelia forward-auth interception, ?rd=/rm= decode
Key evidence9,670 masked requests (majority mask_source=authelia); decoded .env dictionary (15+ variants) + /.git/config + WP version tells
CorroborationCross-corpus credential-harvest series (TI-2026-046A The .env Harvesters; 059E The Keys Left in the Door); companion 059D (scanner triage)
MechanismForward-auth 200 login SPA preserves original target in rd; decode recovers effective_status=401 + verbatim path
ConfidenceHIGH (decoded verbatim from the wire; mechanism is standards-mandated, not incidental)

Next in the series โ€” TI-2026-060E: The Unified Field โ€” the synthesis. Four axes (status, host, method, redirect), one actor at a time: how cross-axis convergence turns four weak, individually-deniable signals into a single attribution the entity graph can name.

Every other mask in this series forgot the attack the moment it hid it. The auth proxy could not afford to forget โ€” so it hid the attack and, in the same motion, wrote it down. The scanner trusted the 200 to keep its secret. The 200 kept a diary instead.

โš  Personal capacity. Research published independently โ€” not reflecting employer views. Derived from passive observation of attacks against personal infrastructure. Full disclaimer โ†’
โ† Previous The 200 That Lied โ€” 4 / 5 Next โ†’