Guide · WP-VCD
WP-VCD came in with a nulled theme or plugin, and it leaves only with it.
You found wp-vcd.php or wp-tmp.php in wp-includes, a block of unfamiliar code at the top of your theme’s functions.php, or ads and redirects on your site that nobody placed. These are the marks of WP-VCD, a WordPress malware family that security vendors have documented publicly since around 2017. It does not exploit a vulnerability: it is shipped inside “nulled” — pirated — copies of paid themes and plugins, and it runs the moment one is activated. This guide covers how to find every piece of it, why deleting the visible files does not hold, and why the cleanup has to end with every nulled item replaced by a licensed copy.
Published Last reviewed
- Entry point
- A nulled (pirated) copy of a paid theme or plugin
- Signature files
- Names such as wp-vcd.php, wp-tmp.php and class.theme-modules.php
- Persistence
- Code at the top of functions.php that rewrites the other pieces
- Spread
- Into the other themes and plugins on the same site
How it works
The download was the infection
Nulled themes and plugins are paid WordPress products with their licence checks removed, redistributed free on download sites. WP-VCD has long been distributed that way: the copy works as advertised, and it carries a block of PHP — usually at the top of the theme’s functions.php, or in a plugin’s main file — that runs as soon as the item is activated.
That block writes companion files with names such as wp-vcd.php, wp-tmp.php, class.wp.php, class.theme-modules.php or class.plugin-modules.php — in wp-includes, where few owners look, and in the theme or plugin folder. It then copies its loader into the functions.php of the other themes installed on the site, so switching themes does not remove it. The companion code contacts a remote server for instructions, and is typically used to inject advertising, spam links or redirects for visitors while staying quiet for logged-in administrators. Some variants have also been reported adding administrator accounts.
The pieces restore one another. Delete wp-vcd.php and the functions.php block writes it again on the next page load; clean functions.php in the active theme and an inactive theme still carries the loader. Variants differ in file names and details, so treat the names above as examples rather than a checklist.
Figure 1 · How it gets in and stays
The nulled download is the infection. Everything else follows from activating it.
How it gets in
- A nulled theme or pluginDownloaded free from a warez site
- ActivatedIts functions.php runs
- Companion files writtenIn wp-includes and the theme
- Other themes infectedTheir functions.php too
What it then does
- Calls homeFetches code to run
- Injects ads or spamFor visitors, not for you
- Restores itselfFrom any surviving copy
- Ends only with licensed copiesEvery nulled item replaced
Find every piece
Read-only checks from the WordPress root
Run these from the site’s root folder over SSH, on a copy where you can. None of them change anything. On a hosting account with several sites, run the search for companion files from the account home so neighbouring sites are covered.
- 01
Look for the companion files by name
Files with the names WP-VCD commonly uses, anywhere under the site:
find . \( -name 'wp-vcd.php' -o -name 'wp-tmp.php' -o -name 'class.wp.php' -o -name 'class.theme-modules.php' -o -name 'class.plugin-modules.php' \) -ls 2>/dev/nullA match is a strong indicator; no match does not clear the site, because names change between variants.
- 02
Find code that refers to them
The loader names the files it writes, so the infected functions.php and plugin files can be found by those names:
grep -rlE 'wp-vcd|wp-tmp|class\.theme-modules|class\.plugin-modules' wp-content wp-includes --include='*.php' - 03
Read the top of every theme’s functions.php
The injected block usually sits before the theme’s own code, in active and inactive themes alike:
for f in wp-content/themes/*/functions.php; do echo "== $f"; head -n 15 "$f"; doneCompare with a clean copy of each theme. Code above the theme’s opening comment or first function, long encoded strings, or file writes and remote requests are the finding.
- 04
Check WordPress core against the official checksums
Core files that were modified, and files that should not be in wp-admin or wp-includes at all:
wp core verify-checksumsRecent WP-CLI versions also warn about files that should not exist, which catches companion files under other names.
- 05
List administrators and installed items
Accounts nobody created, and every theme and plugin, active or not:
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered wp theme list wp plugin listFor each paid theme or plugin, find where it came from. Anything without a licence and a vendor download is a nulled copy until proven otherwise.
Remove it
Replace, do not clean
Because WP-VCD comes inside the software, removal is mostly replacement. The order keeps the evidence and stops the pieces restoring one another.
- 01
Preserve first
A full copy of the files and database, plus the infected files and their timestamps. The earliest timestamp usually dates the activation of the nulled item.
- 02
Remove every nulled theme and plugin
Delete them entirely — active and inactive, including old themes nobody uses. Install licensed copies from the vendors, or replace them with free alternatives from WordPress.org. A cleaned nulled copy is still a nulled copy.
- 03
Clean the remaining themes and plugins
Reinstall every remaining theme and plugin from its official source rather than editing out the injected block; the loader may have reached any of them.
- 04
Replace WordPress core and delete the companion files
Reinstall core from WordPress.org, then remove companion files that remain in wp-includes, uploads or elsewhere, and re-run the checksum check until it reports nothing unexpected.
- 05
Remove accounts and rotate access
Delete administrator accounts nobody can vouch for and revoke application passwords, then change WordPress, hosting, SFTP and database passwords and the security keys in wp-config.php.
- 06
Check for what else came in
Look for scheduled events, database injections and other backdoors added while the site was compromised. WP-VCD fetches instructions remotely, so what it was told to do on your site is not fixed in advance.
- 07
Verify over days
Re-run the searches above after a few days of normal traffic, and check the site as a logged-out visitor on a phone. Any companion file that returns means a copy of the loader survived.
Why this keeps happening
The licence is the security control
A paid theme or plugin from its vendor comes with the vendor’s code and the vendor’s updates. A nulled copy comes with whatever the person who removed the licence check added, and no updates — so even a copy that carried no malware on day one falls behind on every security fix the vendor ships.
On agency and client sites, nulled items often arrive through a developer or a previous provider rather than the owner. An inventory of every paid item and where its licence is held is the cheapest control there is, and it is one of the first things a security audit checks.
Common questions
Common questions about WP-VCD
I deleted wp-vcd.php and it came back. Why?
Because the loader that writes it is still in place — usually at the top of a theme’s functions.php, in the active theme or an inactive one, or in a nulled plugin. Remove every nulled item and reinstall the remaining themes and plugins from official sources, then delete the companion files.
Can I keep the nulled theme if I remove the malicious code?
Not safely. You cannot be sure the part you found is the only addition, and a nulled copy never receives the vendor’s security updates. Replace it with a licensed copy or a different theme.
Is wp-includes/class-wp.php malware?
No. class-wp.php, with a hyphen, is a WordPress core file. WP-VCD has used similar-looking names such as class.wp.php, with a dot. Compare core with WordPress’s checksums rather than deleting files on a name match.
Our site does not use any paid themes. Can it still be WP-VCD?
Check inactive themes and plugins as well as active ones, and any site sharing the same hosting account — a nulled item on a neighbouring site can be the source. If none exists, the files may belong to something else that borrowed the names; the checksum check and the functions.php review still apply.
Will a security plugin remove it?
It may detect and delete known files and code blocks. It cannot replace a nulled theme with a licensed one, which is the step that stops it coming back.