Skip to content
Blog

I Built a WordPress Scanner That Finds Malware in 10 Seconds

By Rajan Gupta

β€’ ⏱ 7 min read

The problem that started it all

A friend called me one afternoon, panicked. His client’s WooCommerce store was sending spam emails, rankings had tanked, and Google was showing a “This site may be hacked” warning. The culprit? A nulled theme downloaded from a sketchy forum β€” pre-loaded with a PHP backdoor.

He spent two days combing through thousands of PHP files manually, looking for the injected code. That’s when I thought: this should take 10 seconds, not two days.

So I built WP Scan β€” a WordPress-specific security scanner that checks themes, plugins, and raw PHP files against 40+ vulnerability patterns and returns a full report in under 10 seconds.

πŸ”— Try it free: wp-scan.org β€” no account, no credit card, just upload a ZIP and scan.

What it looks like in action

The interface is deliberately simple. Drag your ZIP in, hit scan, get results.

πŸ“Έ Scanner β€” wp-scan.org

πŸ”’ wp-scan.org
Find malware in your WordPress code before attackers do.
Upload a theme or plugin ZIP β€” full report in under 10 seconds. No install, no signup.
πŸ“¦ Upload ZIP
πŸ“ Server Path

πŸ—‚οΈ
Drop your ZIP file here
or click to browse  Β·  Max 20 MB free  Β·  150 MB premium

or enter your license key for premium scan

License key (optional)
Scan Now β†’

And here’s what the results look like. Every finding is tagged by severity (Critical β†’ Low), includes the vulnerable file path, and a code snippet showing exactly what was found:

πŸ“Έ Results β€” critical SQL injection detected

πŸ”’ wp-scan.org Β· Scan results β€” my-plugin.zip
87 files scanned  Β·  12 issues found
● 3 Critical
● 4 High
● 3 Medium
● 2 Low

Critical

SQL Injection β€” Unsanitised $_GET in query

includes/functions.php Β· line 47
46: function get_product_reviews() {
47:     $id = $_GET[‘product_id’]; // ← unsanitised
48:     $wpdb->get_results(“SELECT * FROM wp_reviews WHERE id=$id”);
49: }
✨ Premium Fix Guide
Use absint() + $wpdb->prepare() β€” upgrade to see full guide β†’

Critical

PHP Backdoor β€” eval() with base64_decode()

assets/lib/loader.php Β· line 3
3: @eval(base64_decode(‘cGhwaW5mbygpOw==’)); // backdoor
πŸ”’ Premium: step-by-step removal guide + safe replacement code

High

XSS β€” Unescaped user input in HTML output

templates/display.php Β· line 112

What WP Scan actually detects

I didn’t want a generic “PHP linter”. I focused on the exact patterns that appear in real-world WordPress malware, nulled themes, and poorly coded plugins. The scanner currently detects 40+ vulnerability patterns across these categories:

πŸ”΄ PHP Backdoors & Webshells
πŸ’‰ SQL Injection (raw & $wpdb)
⚠️ Cross-Site Scripting (XSS)
πŸ“‚ Local File Inclusion (LFI)
🌐 Remote File Inclusion (RFI)
πŸ”“ Hardcoded Credentials
πŸ•΅οΈ Obfuscated Malware (base64)
πŸ“§ Spam Injectors & SEO Malware
βš™οΈ Dangerous PHP Functions
πŸ”‘ Weak/Insecure Auth Patterns

The patterns cover PHP, JavaScript, HTML, and template files β€” so it catches JS-based XSS in theme templates and PHP backdoors buried in plugin assets alike.

40+
Vulnerability patterns

<10s
Scan time for most ZIPs

150MB
Max ZIP on premium

How it works under the hood

WP Scan is a pure PHP application β€” no Node, no Python, no external scanning engine. Here’s the flow:

  1. Upload & extract β€” ZIP is uploaded, validated (bomb protection: 10k entries max, 10 MB/file, 300 MB total uncompressed), then extracted to a temp directory.
  2. File walk β€” recursively walks the directory, skipping binaries and files over 2 MB. Scans PHP, JS, HTML, HTM, TPL, INC files.
  3. Pattern matching β€” each file is regex-matched against the pattern library. Patterns are weighted by severity.
  4. Report generation β€” findings are assembled into a severity-sorted HTML report with file paths, line numbers (premium), code snippets, and fix guides (premium).
  5. Cleanup β€” extracted files are deleted immediately. Nothing is stored.

⚑ Performance note: The scanner uses a 90-second hard deadline, a 20,000-file cap, and set_time_limit(120) before every scan so large repositories never crash the server.

The tech stack is intentionally minimal:

PHP 8.x
MySQL / PDO
PayPal IPN
Razorpay (India)
Tailwind CSS (CDN)
Custom SMTP mailer
Zero dependencies

No Composer, no npm, no framework. One PHP file + a pattern library. It deploys to any shared hosting plan in under 5 minutes.

Features I’m most proud of

πŸ”

ZIP bomb protection

Hard limits on entry count, per-file size, and total uncompressed size stop malicious ZIPs from crashing the server.

⚑

Zero-storage scanning

Uploaded files are extracted to a temp directory, scanned, then deleted immediately β€” nothing persists on the server.

🌍

Geo-based pricing

Indian visitors see INR prices via Razorpay (UPI, NetBanking, EMI). International visitors see USD via PayPal.

πŸ“§

Built-in drip emails

A 2-step drip sequence (day 3 + day 7) nurtures free users toward paid, with a 10% coupon on the second email.

♾️

Monthly recurring billing

PayPal subscriptions with IPN handling for renewals, cancellations, refunds, and a dashboard toggle for auto-renew.

πŸ›‘οΈ

CSRF protection

Every POST form is protected with session-based CSRF tokens β€” login, register, settings, password change, and admin actions.

Pricing

Free forever with 2 scans/month. Premium unlocks everything β€” and the lifetime plan is a steal if you audit code regularly.

πŸ“Έ Pricing page

πŸ”’ wp-scan.org/page/pricing

Monthly
$9.99
/month Β· cancel anytime
  • 5 scans/month
  • Exact line numbers
  • Fix guides
  • 150 MB ZIPs
  • HTML export

Get Monthly

Lifetime
$199
one-time Β· forever
  • Unlimited scans
  • All premium features
  • Future updates
  • No recurring fees

Get Lifetime


What I learned building this

1. Shared hosting has real constraints

PHP’s mail() silently fails on Hostinger. I had to build a full SMTP client from scratch using PHP’s stream_socket_client() β€” supporting STARTTLS, SSL, and AUTH LOGIN with no external libraries. It works, and it’s ~150 lines of code.

2. ZIP bombs are a real attack vector

Before adding protection, a crafted ZIP with deeply nested directories could hang the server indefinitely. The fix: hard limits on entry count (10k), per-file uncompressed size (10 MB), total size (300 MB), and a 90-second deadline inside the file iterator.

3. Regex pattern libraries need testing on real malware

My first version of the backdoor patterns was catching too much legitimate code (e.g., WordPress core uses eval() in specific places). I ended up hand-testing patterns against a corpus of real nulled themes and malware samples I found on public security repos.

“The pattern that catches the most real-world malware is the combination of eval() + base64_decode() in the same expression. It appears in virtually every backdoor I’ve ever seen.”

4. Geo-based pricing matters for India

PayPal blocks many Indian cards for domestic merchant accounts. Integrating Razorpay with Cloudflare’s CF-IPCountry header for geo-detection solved this β€” Indian visitors automatically see INR prices and Razorpay as the only payment option. International visitors see USD + PayPal.

5. Drip emails convert

The day-3 “did you find anything?” email and day-7 coupon email run automatically via a cron job. The 10% coupon code is created on-the-fly if it doesn’t exist yet, and the whole drip sequence is logged per-user to avoid duplicates.

What’s next

A few things I’m planning:

  • API access β€” so CI/CD pipelines can scan plugins before deployment
  • WordPress plugin β€” scan your live installation from wp-admin directly
  • Signature database updates β€” crowdsourced malware signatures from users
  • Affiliate program improvements β€” better reporting, Stripe payouts

βœ… WP Scan is live and scanning. If you use WordPress β€” for yourself or clients β€” give it a try. The free tier (2 scans/month) is genuinely useful. And if you find a vulnerability pattern I’m missing, reply to the welcome email and I’ll add it.

Try WP Scan β€” it’s free

Upload any theme or plugin ZIP and get a full vulnerability report in under 10 seconds. No account required.

Rajan Gupta

Rajan Gupta

FullStack Web Developer

Rajan Gupta is a passionate web developer and digital creator who loves sharing insights on WordPress, modern web design, and performance optimization. When not coding, they enjoy exploring the latest tech trends and helping others build stunning, high-performing websites.

Related Articles