Most of the breaches we get called into announce themselves with a bang. This one knocked politely. The ticket said, more or less: one of our email accounts got changed somehow and it’s sending spam — can you look at it? The sort of thing you’d hand to the help desk with a shrug.
We’ll skip to the ending, because the ending is the point. That spam complaint unspooled into a seven-week-old intrusion, a public website riddled with web shells that Google had already indexed, a Windows server that had no business being part of the corporate domain but was anyway, and — waiting at the end of it all like a punchline — the Domain Admin password sitting in a plain text script on somebody’s desktop. Not hashed. Not vaulted. Not even password-protected. Just there, the way you’d leave a sticky note.
Whoever found it used the most powerful credential in the company to do exactly one thing: send junk mail.
(Everything identifying here has been changed — the company, its domain, its
servers, its accounts, the dates. The mechanics are real. I’ll call the company
Korvex and its website korvexgroup.com, and leave it at that.)
Pulling the thread
When a client tells me they’re “sending spam,” I’ve learned not to look at the mailbox first. Spam leaving your building is a symptom, like a cough. You don’t treat the cough. So instead of touching the noisy account, we asked for the unglamorous stuff: firewall logs, mail logs, and the Active Directory logs.
The firewall logs were mostly the internet being the internet — scanners, bots, the usual weather. One cloud-hosted address kept turning up at suspicious moments, so we set it aside and kept reading. The mail logs confirmed the spam but explained nothing: a trusted internal account, sending a great deal of mail it shouldn’t, with no malware anywhere near the mail flow.
The domain logs were where my coffee went cold.
There’s an event Windows records when a program asks to log in as somebody —
think of it as a process saying “authenticate me as this user.” Almost every
entry was wholesome: backup software, antivirus, Exchange talking to itself.
And then, sitting in that list like a stranger in a family photo, was the IIS
web-server process — the thing whose entire job is to hand out web pages —
asking Windows to log it in. Web servers don’t do that. Not unless something
is reaching through the website and out into the machine behind it. A few
lines down there was a second oddity: a binary calling itself Exchange.exe,
running from the Windows temp folder, which is roughly where a con artist keeps
his fake ID.
For good measure, the same window held 914 failed logins crammed into half a day — somebody brute-forcing a door while they jiggled the others. So the attacker had options, and wasn’t precious about which one they used.
The web-server process was the loose thread. We pulled it.
w3wp.exe, asking Windows to log it in — and a stray Exchange.exe running from a temp folder. Web servers don’t authenticate to the operating system. When one does, something is reaching through the website into the host.The internet had already done our reconnaissance
Here’s a thing people forget: a web shell dropped onto a public site is a web page like any other, and search engines crawl it, cache it, and file it away. The attacker’s tools get indexed right alongside the company’s careers page.
So before we logged into a single server, we just searched — the company’s
domain, narrowed with a scrap of web-shell code — and the results came back
with the attacker’s work neatly catalogued. Four separate web shells across
korvexgroup.com, on long random URLs built to hide from a human eye but not
from a crawler. Google’s cache even held a snapshot of the site’s homepage
mid-crash, spilling its file paths and the unmistakable fingerprint of injected,
eval()’d code. The front page itself had been tampered with, and it had been
quietly telling the whole internet so for weeks.
A scan of the site rounded out the embarrassment: an out-of-date WordPress, unpatched plugins, and — the small thing that does the most damage — directory listing left switched on, so the server would show its folder contents, backups and all, to anyone who asked nicely. The attacker barely had to look.
The part that turns “hacked website” into “very bad day”
Up to here it’s an ordinary story. Brochure site gets popped; brochure site gets defaced or abused; you rebuild it and feel sheepish. What made this one serious was where the website lived.
It wasn’t on some isolated, throwaway box in a corner of the network. It was running on XAMPP — a bundle built for developers to tinker with on a laptop, never for production — on a Windows server. And that Windows server was a member of the company’s internal Active Directory.
Read that twice, because it’s the whole case in one sentence. The least important asset in the building shared a trust boundary with the most important one. The public marketing site — the thing a vendor probably set up once and nobody had thought about since — was sitting on a machine the domain trusted. Breaking the website didn’t get the attacker a defaced page. It got them a chair inside the domain.
The timeline put their presence at roughly seven weeks before anyone noticed, and the way they got in had the signature of automated exploitation — the kind of opportunistic, scan-the-whole-internet-and-see-what-pops attack that doesn’t care who you are, only that you were reachable and unpatched.
An anticlimax, which is the worst kind
We imaged the crown-jewel systems — the domain controllers, the mail servers — and went looking for the tools a serious intruder leaves behind. Mimikatz, for stealing credentials out of memory. PsExec, for jumping between machines. BloodHound, for mapping the path to Domain Admin. Persistence in scheduled tasks and services.
Nothing. By every standard signature, the domain controllers looked clean. If you stopped there, you’d write a reassuring report.
You’d be wrong, and two files are the reason. One was a little batch script whose only job was to strip the machine of its recent security updates — peeling back the patches, returning the system to a softer, more exploitable version of itself. Maybe the attacker left it; maybe a harried admin wrote it to make slow updates go away and the attacker just grinned. Same result either way.
The other file is the one that makes you put your pen down. On a support account’s desktop sat a PowerShell script — an administrator’s little helper for resetting passwords across a list of machines. And like a lot of “little helpers,” it had the password it used written straight into the code: the real, working Domain Admin password, in cleartext, on a desktop, on a machine reachable from a hacked website.
That’s why the clean scan didn’t matter. The attacker never needed Mimikatz or
a clever escalation chain — the tools we’d been hunting for, the tools that
leave evidence. The master key was lying in the open. They didn’t pick the
lock to the kingdom. They read the combination off a note taped to the safe.
I’ve seen this more times than I’d like: the fastest route to total control is
almost never an exploit. It’s a password in a script, a config file, an
inventory sheet, a passwords.txt. Attackers go looking for exactly that,
because it works.
”It was only spam”
So what did the intruder do with the run of the place? With seven weeks and the keys to everything, they ran a search-engine spam operation. The web shells weren’t a hacking console; they were a doorway that fetched instructions from an outside server and served pages of junk to search-engine crawlers while showing real visitors nothing odd — quietly renting out the good name of a legitimate domain to game search rankings for someone else’s profit. The hijacked mailbox, firing off its hundreds of spam messages, was the same hustle in a different outfit, and it didn’t even need malware — a built-in mail command did the job.
It is tempting to exhale at that. Only spam. Don’t. That restraint was the attacker’s choice, not Korvex’s defence. The exact same access that sent junk mail could have encrypted every server in the building before lunch. The company didn’t dodge catastrophe because anything stopped the attacker. It dodged it because the person who strolled through the unlocked door happened to be a pickpocket and not an arsonist. That is not a security posture. That is luck.
I should be straight about the limits, too, because a report that pretends to certainty it doesn’t have is worse than useless. Across several systems there were simply no logs — auditing had never been turned on — so the records that would have told us exactly when the first machine was touched and precisely which commands sent the spam don’t exist. We could prove the entry point and prove the attacker could reach anything in the domain; in places we couldn’t prove what they did once there, because nobody had ever written it down. The missing logs aren’t a gap in the story. They’re part of it. And one more nasty detail surfaced on the way out: the web shell was sitting in the website’s backups too, on the same server — so “just restore from backup” would have faithfully restored the infection. The clean copy they thought they had was a fiction.
The fixes are almost insultingly cheap
Here’s the uncomfortable thing about a case like this. Nothing the attacker did was clever, so nothing it would have taken to stop them is expensive.
The single change that matters most costs nothing but discipline: a public-facing website must never live on a machine that’s part of your internal Active Directory. Isolate it — its own segment, its own credentials, no trust into the domain. If your brochure site can authenticate to your domain controller, then your brochure site is, for all practical purposes, your domain controller. Everything else is a footnote to that one rule: don’t run developer bundles like XAMPP in production; keep the thing patched and don’t write scripts that un-patch it; turn off directory listing; and — please — get the Domain Admin password out of a file on a desktop and into a vault, because a credential written into a script is a skeleton key waiting for someone to trip over it.
Two habits would have caught it early, and both are free. Turn on audit logging before you need it — authentication, account changes, new services and tasks, log clearing — and ship it somewhere off the box; the day you wish you had those records is the day it’s too late to start. And once in a while, search the internet for your own domain the way an attacker would, looking for pages and code that shouldn’t exist. This very case was cracked by a search engine that had already found the problem. Yours can be too.
Every company has this website. The one nobody owns, that a vendor stood up years ago, that got quietly joined to the domain because it was easier that afternoon. It hums along forgotten with a maintenance budget of zero — right up until the morning it becomes the most important machine you own, for all the wrong reasons. Breaches like this one are rarely a masterclass in hacking. They’re a tidy little pile of reasonable-at-the-time decisions that line up, one day, into an open door. Most of our job is noticing the pile before someone else trips over it.
Drawn from a real investigation, with every identifying detail changed — the company, the domain, the hostnames, the accounts, the credentials, the dates. The tradecraft and the sequence are faithful to what happened. If you run public-facing web apps, or you’ve got a quiet internet-facing box that you suspect has a line of trust into your internal network, we’re happy to take a look — a forensic-readiness review or an attack-surface assessment is reachable through the contact page.