
A few days ago, “wp2shell” made the rounds — an unauthenticated
RCE chain in WordPress, cobbled together from a SQL injection
(CVE-2026-60137) and a validation bypass in the REST batch
endpoint (CVE-2026-63030). 🩸 Every WordPress ≥6.8 was
exposed, upstream finally fixed it in 7.0.2. For a
nail-biting couple of days, this very blog was running an emergency
nginx block on /wp-json/batch/v1 because the patched
Docker image simply refused to exist yet. Classic. 🙃
So naturally, once the fire was out, I did what any self-respecting
homelab nerd does at 2am: I went spelunking through my own ingress
logs. 🕵️♂️ Turns out the internet does not care that you patched.
The internet keeps knocking anyway.
🔍 Receipts From the Access Log
First pass through the logs, I thought I had a tidy little handful of
hits. Then I went back with a better grep (turns out nginx’s plain
access-log lines don’t repeat the hostname, so my first domain filter
was quietly eating real hits — even homelab nerds have off days
🤦) and found out it was way more than a handful.
- 🌊 The opening flood, 18–19 July — the
day the CVE dropped, my logs lit up with dozens of hits, almost all
from the same wonderfully unsubtle User-Agent,
wp2shell.com scanner, cycling through a small army of
Cloudflare-fronted IPs, mostly landing 207
(processed, pre-mitigation). Basically the internet’s security
researchers (or “researchers” 🧐) doing a synchronized victory lap
the moment the disclosure went public. - 😂 Repeat performance from the same scanner UA on 20
July: 403, blocked by my emergency rule.
💪 Then again on 21 July, rule happened to be off
for a moment: 207, sailed right through. 🎯
(Core CVE was already patched by then, so — anticlimactic
for both of us.) - 🕵️ My personal favorite: an EC2 box in AWS
Tokyo (ec2-*.ap-northeast-1.compute.amazonaws.com,
yes I did the reverse-DNS lookup, yes I felt very smart) wearing a
fake Chrome User-Agent like a cheap Halloween
costume. 🎭 It requested/, waited exactly one (1)
second, then went straight for the exploit endpoint. No CSS. No
JS. No favicon. No human being browses like that — real
Chrome loads a small novel’s worth of assets per pageview. Nice
try, robot. 🤖 - 🆕 Update while I was still writing this post: a
fresh one landed from Google Cloud
(*.bc.googleusercontent.com), two requests, one
second apart, both 207 — and this one bothered to
tag itself with its own tracking parameter,
&_w2s=347b2f52/&_w2s=b58fad1b.
Somebody’s scan tool is apparently numbering its own attempts now.
Efficiency! 📊 Third different major cloud provider spotted so far
(Cloudflare-fronted, AWS, now GCP) — this thing has friends
everywhere.
🤔 Okay, But Who’s Actually Doing This?
Fair question, and worth spelling out for anyone who doesn’t stare at
server logs for fun: not everyone poking at a known vulnerability is a
criminal. There’s a real spectrum here, and the tells matter. 🔬
- 😇 Good-faith end: security research outfits and
scanning services (think Shodan, Censys, GreyNoise-style projects)
constantly sweep the internet for known CVEs — sometimes to
build public threat-intel feeds, sometimes so affected site owners
can look themselves up and go “oh no.” A tool that literally calls
itselfwp2shell.com scannerin its own User-Agent
fits this profile well: it has no reason to hide, and being loud
about who you are is exactly what legitimate scanners do (and
often what responsible-disclosure norms expect). - 😈 Bad-faith end: attackers running the exact same
kind of check, except the goal isn’t “tell someone,” it’s “find a
door I can still walk through” — to drop a webshell, a spam
injector, a cryptominer, or just add the box to a botnet. These
are the ones with every incentive to blend in.
That’s exactly why I keep pointing at the fake Chrome
User-Agent cases (the AWS Tokyo one, the GCP one) as the more
interesting ones: an honest scanner doesn’t need to disguise itself as
a regular visitor’s browser. Something pretending to be a person,
specifically to avoid looking like a scanner, has a reason to hide
— and “I have good intentions” isn’t usually it. 🎭
So, honest layperson’s summary: this is almost certainly a mix of
both, and I can’t prove intent from an access log alone — a 207
response tells me the endpoint answered, not what someone was trying
to do with it. But the self-announcing scanner is probably closer to
“internet background radiation / security tooling,” while the
disguised ones are the closer I’d get to “someone actually hoping to
get in.” Since the underlying WordPress bug is patched either way,
neither camp got anywhere here — but the pattern itself is worth
knowing. 🛡️
Full disclosure, for the nerds keeping score at home: my own
curl tests — the ones I ran to confirm the block
actually worked — are sitting in that same log file, repeatedly,
because apparently I couldn’t just check once. 😅 So technically I’m
also “an IP address probing the vulnerable endpoint.” Guilty as
charged, Your Honor. 👨⚖️
Not just me being paranoid, by the way: Golem.de covered this exact
wave of ongoing exploitation attempts too —
“Lücken in WordPress: Laufende Schadcode-Attacken gefährden Millionen von Websites”
— so this isn’t some isolated homelab-blog conspiracy theory. 📰
It’s a genuinely internet-wide thing.
🕸️ Bonus Round: The Scrapers Wearing a Trenchcoat
While I was down in the log mines looking for wp2shell, I tripped over
something way bigger and, honestly, more interesting: 765
different IP addresses, spread across at least 530
distinct /16 network blocks (so: everywhere, not one datacenter),
each one showing up to make exactly one or two requests
and then vanishing forever. 👻
Every single one of them presented a perfectly normal-looking desktop
browser UA — Chrome or Edge, Windows or Mac, the works. Nothing
you’d flag on sight. But line them all up and the mask slips
immediately: they only ever hit my ~31 tag/category/date
archive pages (/tag/ansible,
/category/devsecops, /2025/05, and so on),
methodically working through the list — and not one of them ever
fetched a single CSS file, JS file, or image afterwards. 🖼️🚫 A real
Chrome tab loads two dozen assets per pageview without you lifting a
finger. These didn’t load any.
That’s the signature of a residential-proxy scraping
botnet: instead of hammering the site from one IP (which gets
you rate-limited or banned in about four seconds), you rent a few
hundred thousand real residential IPs and fire exactly one polite,
totally-a-real-person-honest request from each. Death by a thousand
paper cuts, except the paper cuts are indistinguishable from actual
readers unless you zoom out far enough to see the pattern. 🔎 Given the
target (systematically walking every archive page, i.e. the entire
content index of the site, not just the homepage), my money’s on
large-scale content scraping — could be good old SEO content
theft, could be someone building an AI training set. Either way: 765
“visitors” who read zero words and loaded zero fonts were never
visitors. 🤖🕶️
🎬 Takeaway
The actual hole has been patched for a while now (running
7.0.2 since day one of this being possible). But
scanning for a freshly-announced CVE doesn’t stop just because
someone, somewhere, shipped a fix. 📡 The scanning bots do not read
changelogs. If you run public-facing WordPress, this kind of traffic
is just background radiation — a good excuse to actually glance
at your access logs once in a while instead of assuming “patched ==
done.” 🛡️







