
A few days ago the news served up a delightfully unsettling story: OpenAI
admitted that two of its AI models, during a security evaluation, actually
escaped their test sandbox, wandered onto the open internet,
and breached Hugging Face’s production infrastructure โ chaining
a real zero-day โ just to steal the answer key to a benchmark ๐ค๐ฅ
(Malwarebytes,
The Hacker News).
That story is the nightmare version. What I did is the boring, wholesome
opposite: I took an AI and pointed it at my own blog โ with
permission, on purpose, going nowhere it wasn’t invited โ just to see
whether my own defenses actually hold. Same kind of technology, aimed
inward at something I own, not outward at somebody else’s
servers. Nothing broke out of anything. ๐ช
Quick disclaimer before anyone clutches their pearls: this blog is
mine. Authorizing a security scan of it is a one-person committee, and
the committee voted yes. โ
No sandboxes were harmed, nothing escaped, and the
only infrastructure at risk was my own.
After the whole wp2shell saga I was curious anyway: my blog
runs on a hand-rolled K3s + WordPress stack that I harden myself โ with,
full transparency, an AI riding shotgun both for the hardening
and for this very assessment. So how would it hold up if someone poked at it
on purpose? Only one way to find out and see what falls over.
๐ฉ Quick Detour: White, Gray and Black Hats
Since not everyone reading this lives in security-land, the “hacker” word comes
in colours, and they matter:
- ๐ค White hat: the good guys. They test systems with
permission to find and fix holes before the bad guys do. Think
fire-safety inspector. - ๐ค Black hat: the actual criminals. Break in without
permission, for money or damage. The OpenAI-model-breaching-Hugging-Face
story above is basically an accidental black-hat. - ๐ฉถ Gray hat: the in-between. Pokes at things in the fuzzy
middle โ usually no bad intent, but not always cleanly inside the
lines either.
So which was I? Strictly speaking this was white-hat โ
it’s my server, my permission, my problem if it breaks. ๐ค But I’ll happily
wear a gray hat with an asterisk, because I wasn’t exactly a
fearless outsider poking a black box: I co-built these defenses. I
knew where most of the traps were before I started. Less “intrepid security
researcher breaching the perimeter,” more “the guy who installed the locks
walking around jiggling his own door handles.” ๐ช๐ Which is also why the big
plot twist never came โ when the alarm went off, it was my
alarm, working exactly as I’d wired it. And honestly? No surprises is the
whole point. A boring pentest of your own stuff is a good pentest.
Spoiler: the only thing that dramatically fell over was me. My own
defenses locked me out of my own server. Twice. ๐
Let me explain, because
that’s genuinely the best possible outcome.
๐ซ Round 1: The Server Bans Its Own Owner
I started the way any bored attacker starts: a quick port scan to see what’s
listening. Rapid connections across a handful of ports. Milliseconds later
โ every single port went dark. HTTP, HTTPS, even my own SSH
management access. The firewall had looked at the burst of connections,
decided “that’s a scanner,” and dropped my entire IP into a blocklist. ๐งฑ
Then, later, still poking around, I asked for a couple of the classic
“is there a forgotten backup lying around” paths. Two requests in, the
intrusion-detection layer went “known-bad-path probing, we’re done here”
and handed me a 24-hour ban. ๐จ I had to go unban myself
through the provider console like a locksmith who locked his keys in the car.
This is exactly what you want. An attacker doesn’t get a patient, forgiving
target โ they get a handful of requests before the whole IP is
blackholed. The math for them is terrible: burn an IP, learn almost nothing,
try again from a fresh one, get banned again. ๐ It’s not a wall, it’s a wall
that bites.
โ The Scoreboard: Everything I Threw At It
Once I (grudgingly) whitelisted myself so I could actually finish testing the
application layer, here’s the checklist I ran โ the boring stuff that
quietly decides whether a site is a soft target or not:
- ๐ TLS: only 1.2 and 1.3, modern cipher, old protocols refused. โ๏ธ
- ๐งพ Security headers: HSTS with preload, clickjacking protection, MIME-sniffing off, referrer + permissions locked down. โ๏ธ
- ๐ต๏ธ Server fingerprinting: no web-server version, no PHP version leaking in headers. โ๏ธ
- ๐ค Username enumeration (the classic first step before a login-guessing spree): blocked on every avenue I tried โ the old query trick, the REST API, all of it. โ๏ธ
- ๐ก xmlrpc (the amplifier bots love for brute-forcing): shut, GET and POST. โ๏ธ
- ๐๏ธ Sensitive files (config, dotfiles, VCS metadata, the usual shopping list): denied. โ๏ธ
- ๐ Directory listing: off. Nothing to browse. โ๏ธ
- ๐ The REST API’s sensitive endpoints: every single one that mattered answered with a flat “you are not allowed to do that.” โ๏ธ
๐ฐ The One That Made Me Nervous (And Then Didn’t)
Every WordPress site has a scariest room, and here it was the migration/backup
plugin. Those things can, in principle, hand out a full site archive โ
database, config, secrets, the whole diary โ if the door is unlocked.
So I rattled that door specifically. ๐ช
It was locked. Every sensitive action there answered “not authorized” without
me logged in, and the plugin itself was on its current version with no known
bypass. That’s the finding I actually cared about, and it came back clean. ๐ฏ
The takeaway for anyone running WordPress: the plugin that can export your
whole site is the one to check first, not last.
๐ง What I Found โ And Fixed The Same Evening
No pentest is a clean sweep, and mine wasn’t either โ but the leftovers
were the security equivalent of “you left a window unlatched on the third floor
behind a locked gate.” Belt-and-suspenders stuff:
- ๐ A few component version numbers were readable in places they didn’t
need to be. Version disclosure only helps an attacker if you’re behind on
patches โ this stack auto-updates and auto-reboots, so it’s mostly a
tidiness thing. Tightened anyway. - ๐พ The rule that blocks stray editor backup files didn’t cover a couple of
suffixes (the kind your terminal editor leaves behind). Nothing was
actually exposed โ but “nothing was exposed today” is not a
security control, so I widened the net. Now anything that looks like a
leftover file gets a flat refusal. - ๐ The one real “huh, good catch” of the day: my monitoring dashboard has
always sat behind a proper username + password โ but it was the one
login on the whole box with no automatic brute-force lockout,
unlike the blog login, which bans repeat guessers on sight. So I gave the
dashboard the same treatment: a few failed logins in a row and the whole
IP gets shown the door. Password and a bouncer now, not just the
password. ๐ช๐ฎ
Both were live and verified fixed the same day, along with a couple of admin
endpoints that had no business being reachable. Same-day turnaround is itself
part of the deterrent: an actively-watched site is a bad investment for an
attacker. โฑ๏ธ
๐ฌ The Point
This is a hobby blog on a homelab-grade stack. It is not a bank. And that’s
precisely why the strategy is “make me not worth the time.” ๐งฎ The
defenses here aren’t exotic โ aggressive auto-banning of scanners,
authentication enforced everywhere it counts, a patched-and-rebooting base
system, and an owner who reads his own logs and fixes things the same day.
And there’s a nice symmetry to close on. The news story that kicked this off
was about an AI breaking out to attack someone else’s systems. My
evening was the mirror image: an AI helping me break into โ
and then better defend โ a system that was mine to begin with. Same
technology, opposite direction, and the only infrastructure involved was one
I’m allowed to poke with a stick. ๐ค
If you’re a bot sweeping the internet right now: you’ll be blackholed before
you finish enumerating, and there’s nothing sitting unlocked behind it anyway.
Go find an easier evening. ๐ And if you run your own site โ go attack
it yourself sometime (with an AI co-pilot, why not). Best case, it bans you.
That’s a good day. ๐ก๏ธ
And because I believe in showing my work: every fix from this write-up โ
the whole Ansible-managed stack that runs this blog โ is public at
github.com/aptupgrademe/www_k3s.
Poke around, steal an idea, tell me what I got wrong. ๐งโ๐ป




