Bulwark

A Go WordPress security auditor for Centmin Mod and LEMP fleets. Built to find real compromises without drowning you in false positives.

Role
Author & maintainer
Stack
Go, WordPress, Security, Linux
Source
https://github.com/prasul/bulwark

Highlights

  • Eleven check categories covering file placement, PHP and JS malware, hook injection, mu-plugins, rogue admins, and the database
  • Tuned for low false positives: patterns require dangerous combinations like `eval` + `base64_decode`, not lone function names
  • Detects the current attack shapes, functions.php C2 callbacks, mu-plugins backdoors, and self-recreating admin accounts
  • Context-aware scanning that skips wp-admin, wp-includes, and known security plugins whose own code trips naive scanners
  • Terminal summary plus an interactive HTML report with collapsible per-site cards, one binary, no runtime dependencies

There is already a famous tool called WPScan. It is the Ruby scanner everyone means when they say the word, it ships on Kali, and it is backed by a large commercial vulnerability database. Bulwark is not that, and it is not trying to be. It solves a narrower problem that the big scanner does not: auditing the WordPress installs on a Centmin Mod or LEMP server you actually run, from the inside, looking for signs that something has already gotten in.

That inside-out difference is the whole point. WPScan looks at a site from the outside, the way an attacker would, and tells you what is exposed. Bulwark runs on the box with filesystem and database access and asks a different question: has this site already been compromised, and where is the payload hiding.

Why I rewrote it

Bulwark started as a bash script called hackchk.sh that I used to sweep client sites after an incident. It worked, but it cried wolf constantly. The bash version leaned on wp plugin verify-checksums, which flags every premium and custom plugin as suspicious because they are not in the WordPress.org repository. When a tool alarms on things that are fine, you stop trusting its alarms, which defeats the entire purpose of running it.

So I rewrote it in Go with one governing rule: a finding should almost always mean something. Fewer alerts that you believe beats a wall of alerts you learn to ignore.

How it stays quiet unless it matters

The false-positive work is the part I am proudest of, because it is the part that took real cleanup experience to get right.

The malware patterns require dangerous combinations, not individual functions. eval on its own is not a finding. eval(base64_decode( is. A lone file_get_contents is normal WordPress. file_get_contents reading straight from $_GET is a remote-fetch backdoor. Every pattern is written to describe the actual shape of an attack rather than a function that attacks happen to use.

It knows where not to look. Scanning wp-admin, wp-includes, or the code of security plugins like Wordfence and Sucuri is a guaranteed way to generate false matches, because those plugins carry malware signatures on purpose so they can detect them. Bulwark excludes those paths deliberately. The base64 blob threshold sits at 300 characters so legitimate encoded assets do not trip it.

What it actually checks

Eleven categories, and they map to how WordPress installs really get owned in 2025 and 2026, not to a generic checklist. PHP files anywhere inside the uploads directory, which are always malicious. functions.php hooks that phone home to a command server. Obfuscated JavaScript and crypto miners injected into theme headers. The mu-plugins directory, which auto-loads and is invisible from the admin UI, and is therefore a favorite hiding place. Admin accounts created in the last seven days, and the username_exists plus wp_create_user pattern that quietly recreates a rogue admin every time you delete it. Then a database sweep for injected scripts, spam, and suspicious cron hooks.

It runs against Centmin Mod paths by default, takes a base directory and a recent-file window as flags, and produces both a colored terminal summary and an interactive HTML report with a collapsible card per site.

The honest scope

Bulwark is not a general-purpose scanner and I would not point it at a site I do not run. It is a fleet auditor for the specific stack I work on every day, built by someone who has spent a lot of nights cleaning up exactly the compromises it looks for. That focus is the reason it can afford to be so opinionated about what counts as a real finding.