Skip to Content
Free · Open Source · MIT License

WP-Narcan

For the morning after your WordPress site gets hacked. WP-Narcan rebuilds the site from clean, verified sources into a new folder next to the old one, and sets aside anything it can't check, so you can look everything over before you swap it in.

Python · v2.0 · MIT License

wpnarcan.py
$ wpnarcan /var/www/example.comWP-Narcan 2.0.0 (Hopscotch)Site       /var/www/example.comWordPress  7.0.6, en_US, single siteOutput     /var/www/example.com-rebuiltReport     /var/www/example.com-rebuilt-reportWP-Narcan only reads /var/www/example.com.Write the rebuild and report to the places above? [y/N] yWordPress core  Downloading WordPress 7.1.2 (en_US)  Verified the download's SHA-1 and 3,782 file checksums  Old core files (checked against WordPress 7.0.6): 1 modified    modified    wp-includes/functions.phpPlugins (4)  akismet            5.7 -> 5.7.2     wordpress.org, verified  contact-form-7     6.0.6 -> 6.1.7   wordpress.org, verified                       old copy: 1 modified, 1 added (contact-form-7 6.0.6)  display-widgets    2.6              set aside: closed on wordpress.org (2021-01-30, Security Issue)  woo-extras         3.1              set aside: not on wordpress.orgThemes (2)  example-child      1.0              set aside: not on wordpress.org  twentytwentyfive   1.5              wordpress.orgMust-use plugins, drop-ins and other files  set aside   wp-content/mu-plugins/wp-cache-helper.php: must-use plugin (WordPress runs these on every page) [3 suspicious]Uploads  Copied 1,284 files (412 MB). Left out 2.    wp-content/uploads/2024/03/cache.ico  (contains PHP code)    wp-content/uploads/2025/11/photo.php.jpg  (hidden .php extension)  Kept 2 .htaccess files that only block access (WooCommerce, Wordfence and others use them).wp-config.php  set aside   wp-config.php: 2 suspicious lines, so it wasn't put in the rebuild [2 suspicious]    line 74     base64_decode() is often used to hide code    line 74     eval() runs a string as PHP codeSummary  Rebuilt into   /var/www/example.com-rebuilt  WordPress      7.1.2 (was 7.0.6)  Plugins/themes 3 of 6 came from clean sources  Set aside      5 items for you to review in /var/www/example.com-rebuilt-report/review  Old site       2 files or folders show signs of tampering (the rebuild doesn't use them)  Left out       2 files from uploads  Not included   the database. WP-Narcan never touches it, and can't tell you how the attacker got in.Done, but some things need your attention (listed above).Read /var/www/example.com-rebuilt-report/NEXT-STEPS.txt before you swap the rebuild in.

An example run. Your site will have its own list of plugins.

How It Works

  1. 01

    Checks It's WordPress

    Works out the version, the language, whether it's multisite, and where wp-config.php and the content folder are. It reads wp-config.php, so a renamed wp-content turns up by itself.

  2. 02

    Fresh, Verified Core

    Downloads WordPress and checks the zip and every file against wordpress.org's checksums, then tells you which of the old core files were changed or shouldn't be there.

  3. 03

    Clean Plugins and Themes

    Fresh copies from your own zips or wordpress.org, checked against the published checksums once it's sure they're the same plugin. Plugins wordpress.org has closed aren't installed, because they may be how the attacker got in.

  4. 04

    Sets Aside What It Can't Check

    Must-use plugins, drop-ins, custom themes, stray PHP files and a wp-config.php with suspicious code go into a review folder with the reasons, not into the rebuild.

  5. 05

    Uploads, Minus Anything That Runs

    Leaves out scripts, photo.php.jpg-style double extensions, images and icons with PHP inside, SVGs with scripts and symbolic links. Keeps the .htaccess files that protect private downloads.

  6. 06

    A Report and Next Steps

    Writes a standard .htaccess so permalinks keep working, and puts a report next to the rebuild, never inside it: a summary, report.json, the full log and NEXT-STEPS.txt.

Try It Here

The Config Check

One step of a rebuild is reading wp-config.php for things that shouldn't be there. This is that check, with the same 19 patterns the script uses, running in your browser. Paste a config in, or try the dodgy one I made earlier.

Runs on this page · Nothing Is Sent

What It Won't Do

  • It doesn't touch your database. Rogue admins and injected posts live there; NEXT-STEPS.txt has the queries to find them, but the cleaning is up to you.
  • It won't tell you how the attacker got in, though the report points at what was tampered with.
  • It can't download paid plugins or themes. Give it the zips, or they're set aside for you to replace.
  • It doesn't prove the set-aside code is safe. Its checks are hints for a person, not a malware scanner.
  • It doesn't copy caches, backups or logs. It lists them, and you should take them off the server too.
  • It won't replace your live site. You do that, after you've checked the rebuild.

Running It

You need Python 3.9 or newer (or Docker) and a copy of the hacked site's files. The rebuild lands next to the original in a folder ending in -rebuilt, with the report beside it in -rebuilt-report.

# Install it (you get a wpnarcan command)$ pip install git+https://github.com/ReignOfComputer/WP-Narcan.git# A dry run first: it checks everything and writes nothing$ wpnarcan /path/to/hacked-site --dry-run# Then the real thing$ wpnarcan /path/to/hacked-site# Paid plugins? Hand it your zips$ wpnarcan /path/to/hacked-site \$     --local-repo-plugins /path/to/plugin-zips \$     --local-repo-themes /path/to/theme-zips# No Python? Build the image in a copy of the repo,# then run it from the folder your site is in$ docker build -t wpnarcan .$ docker run --rm -it -v "$PWD:/work" wpnarcan /work/public_html

Rather Not Do It Yourself?

I clean up hacked WordPress sites, database included, and harden them afterwards.