Small business sites rarely get a targeted attack. They get the same few requests that bots send to every address on a list. This post shows six of those requests, the response a bot hopes for, and the command to test your own site.
Why the volume keeps going up
Three public data points.
- Automated traffic was 51% of all web traffic in 2024, and bad bots were 37%, up from 32% in 2023, according to the 2025 Imperva Bad Bot Report. The report says generative AI lowered the barrier to building and running bots (Thales press release).
- The CVE Program published 28,961 records in 2023, 40,077 in 2024 and 48,244 in 2025 (CVE.org metrics). Patchstack counted 7,966 new WordPress ecosystem vulnerabilities in 2024, 96% of them in plugins (Patchstack).
- Mandiant measured an average time-to-exploit of five days for vulnerabilities exploited in 2023 (Google Cloud / Mandiant).
AI is now part of both finding and exploiting bugs. Google's Big Sleep agent found an exploitable stack buffer underflow in SQLite before it reached a release (Project Zero), and later found CVE-2025-6965, which Google says was known only to threat actors (Google). In DARPA's AI Cyber Challenge final, automated systems analysed more than 54 million lines of code, found 54 of 63 planted vulnerabilities, patched 43, and found 18 real ones nobody planted, at about $152 per task (DARPA). On the attack side, Anthropic reported a campaign where AI did 80 to 90% of the tactical work against roughly 30 targets, including vulnerability discovery and exploit code (Anthropic report).
Nobody has to point these tools at a small brochure site for this to matter. The list of known holes grows faster, and checking every site on the internet for them gets cheaper every year.
The six probes
The request and response pairs below are the examples shown on shieldwave.io. Each one is how a site with that hole answers.
1. A backup left in the web root
> GET /backup-full.zip
< 200 OK, application/zip, 48 MB
Backup plugins, migration tools and tired developers leave archives and SQL dumps in public folders. Bots request common names: backup.zip, backup-full.zip, site.tar.gz, db.sql, wp-config.php.bak, .env, .git/config.
Test it:
for f in backup.zip backup-full.zip db.sql wp-config.php.bak .env .git/config; do
printf '%s ' "$f"; curl -s -o /dev/null -w '%{http_code} %{size_download}\n' "https://example.com/$f"
done
A 200 is not proof on its own. Many sites return 200 with an HTML error page for every path. Check the first bytes: a zip starts with PK, a .git/config starts with [core]. Fix it by moving the file out of the web root and denying *.zip, *.sql and *.tar.gz in the server config.
2. Plugin version fingerprinting through readme.txt
> GET /wp-content/plugins/slider-pro/readme.txt
< 200 OK, Stable tag: 2.1.0
Most WordPress.org plugins ship a readme.txt with a Stable tag line. A bot does not need to log in or guess. It requests the readme for a list of plugin slugs, reads the version and matches it against public vulnerability data.
curl -s https://example.com/wp-content/plugins/contact-form-7/readme.txt | grep -i 'stable tag'
Blocking the readme hides the easy signal, but versioned asset URLs like ?ver=5.9.3 often leak the same number. The real fix is updating or removing the plugin. Unused plugins count, since their files are still reachable.
3. Missing DMARC
> dig TXT _dmarc.yourwebsite.com
< NXDOMAIN
No DMARC record means receiving servers get no policy for mail that fails SPF and DKIM. Someone can send invoices "from" the domain.
dig +short TXT example.com | grep spf
dig +short TXT _dmarc.example.com
Start with v=DMARC1; p=none; rua=mailto:... to collect reports, confirm SPF and DKIM pass for every legitimate sender, then move to p=quarantine.
4. SQL errors from a form
> POST /contact email=' OR '1'='1
< 500, error in your SQL syntax
A database error string in a response is a strong sign that input reaches a query unescaped. Do not run this against a site you do not own or have permission to test. On your own site, the fix is prepared statements ($wpdb->prepare() in WordPress) and turning off error display in production.
5. Old TLS still accepted
> ClientHello, TLS 1.0
< ServerHello, TLS 1.0 accepted
openssl s_client -connect example.com:443 -tls1 </dev/null 2>&1 | grep -E 'Protocol|alert'
Recent OpenSSL builds may refuse TLS 1.0 on the client side, so a failure can mean your client refused, not the server. nmap --script ssl-enum-ciphers -p 443 example.com gives a clearer answer. Fix it by allowing only TLS 1.2 and 1.3.
6. Comment and form script injection
> POST /wp-comments-post.php
< 302 Found, comment saved
The bot submits a payload and checks whether it lands in the page unescaped. The fix is escaping output. A Content-Security-Policy header limits what an injected script can load and run, and X-Content-Type-Options: nosniff stops browsers from guessing file types.
curl -sI https://example.com | grep -iE 'content-security-policy|x-content-type-options|strict-transport-security'
Running all of it at once
I built ShieldWave to run these checks and more than 20 others from the outside and write the result in plain language for the site owner. The free check takes about 90 seconds. A full scan of a verified site takes a few minutes, because it never goes above 15 requests per second and slows down further when the server struggles. I run ENSOMEDIA, a one-person studio, and I wrote the scanner.
The report sorts findings into fix this week, this month, and can wait, and connects findings that make each other worse. It also produces a brief for the developer. This is the sample brief from the home page, for a fictional shop:
Subject: Security fixes for example-shop.com
- /backup-full.zip is publicly accessible (HTTP 200, 48 MB). Remove it from the web root and deny *.zip, *.sql, *.tar.gz in the server config.
- Outdated plugins: slider-pro 2.1.0 (known vulnerability, fixed in 2.4.3), plus two more listed in the attached report. Update and retest.
- No DMARC record. Add _dmarc TXT "v=DMARC1; p=quarantine; rua=mailto:..." once SPF and DKIM pass.
- Missing Content-Security-Policy and X-Content-Type-Options: nosniff. Add at the web server level.
That sample is made up. Here is a real report, for my own studio's site, scanned on 21 October 2026 with the domain blurred. It came back at 98 with two low findings and nothing urgent, which is what a maintained site should look like:
Every finding says why it got its severity, how sure the scanner is, and gives you a command to confirm it yourself, so you do not have to take the score on trust:
And this is the developer brief that scan produced:
Some details developers ask about:
- Active tests need ownership. Without verification, ShieldWave only reads what the site shows everyone. The SQL injection and XSS tests send inputs to forms, so they run only after you verify the site with a DNS record, a file, a meta tag or the WordPress plugin. A test message may arrive in the contact form inbox.
- Traffic is capped. The scanning docs state a budget of at most 5 requests per second for the free check and 15 for a full scan, with a robots.txt opt-out.
- It is not a pentest. A pentester spends days on one target. ShieldWave catches the common holes that bots look for.
- Free tools. Single checks are free at /tools/ssl-certificate-checker/, /tools/spf-dkim-dmarc-check/ and /tools/security-headers-check/.
Pricing, so you know what you are clicking into:
- Free: 5 checks a month for one site, with the score and the three most serious problems. No account, no card.
- Full report for one site: $9, paid once.
- Pro: $12 a month for up to 25 sites, unlimited scans, scheduled scans with email alerts, the developer brief and PDF reports. The first 7 days are a free trial.
If you look after client sites, Pro is the plan for that: one dashboard for all of them, and a brief per site you can forward as it is.
The inside view: a free WordPress plugin
An outside scan cannot see files. The Ensomedia Security plugin, newly listed in the WordPress.org directory after the plugin review, runs 24 checks inside WordPress:
- Core and plugin files against WordPress.org checksums, and themes against their release packages, plus PHP files those releases do not contain.
- Malware and backdoors: web shells, decode-and-execute chains, PHP hidden in images, hidden administrators, injected JavaScript.
- PHP files in uploads, and
.htaccessor.user.inirules that run uploads as PHP. - Known vulnerabilities (opt-in), with the version that fixes each one.
- Login limits, two-factor login with an authenticator app or passkeys, and
wp shieldwave two-factor disableas a CLI rescue.
Screenshot 3 from the WordPress.org listing.
Scheduled scans and email alerts are free and need no account. It changes files, users and settings only when you press a button. The transparency page lists every outside request, and the signed threat feed can be checked with a short Node script:
curl -O https://shieldwave.io/verify/shieldwave-verify-feed.mjs
node shieldwave-verify-feed.mjs
Install it from Plugins, Add New, search "Ensomedia Security". The plugin is new, so false positives are treated as bugs: open a topic in its support forum or write to [email protected], and it reaches me directly.
The two work together. Connecting the plugin to a ShieldWave account is optional, and when you do, its results land in the same dashboard as the outside scan, so each site has one list of problems seen from both sides. The free account shows up to three connected WordPress sites.
Run the outside check on a site you own at shieldwave.io, then install the plugin on the WordPress sites you look after.




Top comments (0)