← AEU Hosting Blog

Self-healing WordPress malware hides in database and RAM

2026-10-07

Self-healing WordPress malware hides in database and RAM

Security analysts at Sucuri have documented a WordPress malware family they call SC, a self-healing mesh that survives removal by storing copies of itself in at least eight places at once and rebuilding any deleted component within seconds. The research, published on September 30, 2026 by Sucuri security analyst Gabriel Barbosa, comes from a real cleanup where the same backdoor kept returning no matter how carefully visible files were deleted. The name SC comes from the 'SC_' markers found in injected content.

According to Sucuri, every recovered SC file shares the same obfuscation scheme: there is no eval, no marker on the newest pieces, and no readable function names. Instead, each file carries a table of scrambled strings and a small decoder that resolves a numeric index into a real function name through a positional substitution cipher.

The infection has eight core components. The first is a .user.ini trigger with a single line setting auto_prepend_file, a PHP configuration directive that runs a specified file before every request in that directory tree, even requests that never reach WordPress. PHP caches this value, which is why deleting the prepend target while the cache is still warm causes every PHP request on the account to fail until the cache expires.

The second component is a visible shim PHP file. In the documented case it was named c1b12371.php, but the name varies from site to site. The .user.ini points at this shim, and the shim does one thing: if a hidden dot-prefixed file exists next to it, it includes that hidden file. If a defender removes only the hidden file, the shim silently does nothing and the site stays up, buying the attacker time while another component recreates the hidden file.

The third component is the hidden dot-prefixed file, the first real loader. It locates the fake plugin and rebuilds it in the mu-plugins directory from three sources tried in order: an existing copy in the plugins folder, an encoded stub in the cache directory, and a ZIP restore bundle with a random hex name. It writes through a temporary file, sets file permissions to 0644, and calls opcache_invalidate so the freshly written file executes immediately. It also defines a constant named SC_AUTO_PREPEND, tying it to the auto_prepend side of the family.

The fourth component is the wp-content/db.php drop-in, a special WordPress file loaded very early during startup. This copy carries the entire backdoor payload inside itself as a gzip plus base64 blob, two common compression and encoding formats. On any request where the fake plugin is missing or too small, it decodes the blob and writes the plugin straight back to disk.

The fifth component is the wp-content/advanced-cache.php drop-in, the earliest and most dangerous position of all. When caching is enabled, WordPress loads advanced-cache.php before ordinary plugins. Unlike db.php, it does not carry the payload inline. It is a finder that can rebuild the plugin from five independent sources in order: an existing mu-plugin, an existing plugin copy, a System V shared-memory segment holding PHP code, a ZIP bundle searched across several folders, and finally the database itself. For the database path, it opens a direct connection using the site's own credential constants and reads the payload out of a specific option row, decoding the same gzip format. Once it has a copy, it hooks plugins_loaded and includes it.

The sixth component is a theme injection. The attacker appended a bounded block to the bottom of the active theme's functions.php, fenced by begin and end markers. That block is the theme-resident twin of db.php: it rewrites the plugin whenever it goes missing.

The seventh and eighth components are two identical copies of the actual backdoor payload, installed both as a must-use plugin (which loads automatically and invisibly) and as a normal plugin. The plugin folder even ships a convincing fake settings page, a shortcode, and an activation hook in plain sight, so a quick look suggests a real caching plugin. The two copies are deliberately redundant, and the payload competitively updates one from the other so both stay on the same version.

The payload is the reason the rest of the mesh exists. Once running, it performs several distinct jobs. It hides itself by filtering the plugin list, the update transient, and the site and network plugin views to remove its own entry, so it does not appear in the admin plugins screen or in update checks. It also emits admin JavaScript to scrub itself from the plugin table as a fallback.

For command and control, the payload talks over blockchain infrastructure. It carries a list of roughly twenty public Ethereum RPC gateways and a set of smart-contract method selectors. Rather than a single hardcoded server, it sends requests to those legitimate third-party gateways to read instructions from a smart contract. Because these gateways are legitimate, blocking only the one seen in traffic leaves the rest as fallbacks, so the whole set must be blocked together.

The payload fingerprints the site and fetches a payload. It collects the site URL and host, WordPress and plugin versions, path hashes, active themes, the mu-plugin list, and current administrator session tokens, encrypts that bundle, and posts it to the resolved endpoint. The reply can carry front-end JavaScript to inject, which on an online store enables checkout skimming, plus new PHP to install, and lists of security plugins to deactivate and delete. When told to remove a security plugin, it can deactivate it, wipe its directory, and even reassign or elevate another account first.

The payload also creates a hidden administrator. It either adopts an existing hidden admin or generates a new one, writing the account directly into the users and usermeta tables when the normal WordPress API is unavailable. It stores capabilities under the default capabilities meta key, then hides the account from the user list, user counts, and role views through query and count filters. It also forges valid authentication cookies for that account, so the operator can log in without a password.

The payload exposes a live beacon. A magic request parameter, in the form of a short prefix followed by a fixed value, is handled before WordPress finishes loading. While the mesh is present, that request returns a normal response with content and then exits.

Finally, the payload rebuilds the entire mesh. It hooks a large set of WordPress actions and filters whose only purpose is to write every other component back out. It regenerates the db.php and advanced-cache.php drop-ins, the theme injection, the hidden loader and its .htaccess and .user.ini prepend directives, the ZIP restore bundle, the shared-memory segment, and the database copy of the payload. It even opens a raw database connection to insert its payload option directly, bypassing WordPress.