Start with the problem you are seeing
Could this match the problem on your WordPress site?
Use this entry when deleted malware rapidly returns, an unknown plugin appears in both plugins and mu-plugins, or suspicious advanced-cache.php, db.php, .user.ini, ZIP, and theme changes occur together.
Observed in this investigation
- Deleted malware files can reappear within seconds or after the next PHP or WordPress request.
- The same unfamiliar plugin can exist as both an ordinary plugin and an automatically loaded MU plugin.
- Short hexadecimal loader and ZIP names can appear beside modified WordPress drop-ins.
Possible warning signs
- Administrators may not see the malicious plugin or concealed user in normal wp-admin lists.
- Administrator credentials and authenticated browser sessions may be exposed.
- Other WordPress sites under the same hosting account may also become infected.
These signs are investigation leads, not proof of this artifact by themselves.
Questions this research can help answer
- Why does WordPress malware come back immediately after I delete it?
- Is Trace Scanner Lite a legitimate WordPress plugin?
- Which file, database, memory, and browser persistence locations must be checked together?
Evidence boundary: The recovered code proves the listed capabilities for this sample set, but a matching filename alone is not a diagnosis and the original entry route remains unknown.
Seeing one of these signs on your site?
I can investigate the files, database records, users, and server evidence, explain what is confirmed, and help plan a safe cleanup.
Research focus: Analysis of SC 4.0.3 WordPress malware that rebuilds itself through MU plugins, drop-ins, themes, PHP configuration, database payloads, ZIP files, memory, and browsers.
I encountered self-healing WordPress malware that appeared to regenerate almost immediately after its files were deleted. On this site, the visible components included a plugin using the alias Trace Scanner Lite, an identically named must-use plugin, the WordPress drop-ins advanced-cache.php and db.php, two loader files in wp-content, a .user.ini directive, an injected theme functions.php, and hidden ZIP backups.
This was not a collection of unrelated malicious files. Static analysis shows that they form a coordinated, self-repairing persistence mesh. Several components can restore the main payload, while the payload can rebuild the other components. In this incident, temporarily renaming wp-content interrupted those paths long enough to remove the files together. That observation was the key clue: deleting only the file currently visible did not remove the source that recreated it.
Assessment: Critical compromise. The malware can steal administrator passwords, mint authenticated cookies for existing administrators, create a concealed administrator, receive remote code and JavaScript, persist in server and browser contexts, and spread to sibling WordPress installations accessible to the same hosting account.
The initial vulnerability or stolen credential used to enter the first site cannot be determined from these samples alone. This report separates the behavior proven by the recovered code from possible initial-access hypotheses.
Scope and methodology
This analysis is based on the recovered PHP files and screenshots listed below. I decoded the string-substitution layers, unpacked embedded Base64/gzip content, compared payload hashes, and traced file, database, WordPress-hook, browser, and network behavior. The samples were handled as text and were not executed. No command-and-control endpoint or Ethereum RPC endpoint was contacted during analysis.
The recovered variant uses fake plugin author metadata. A WordPress plugin header is attacker-controlled text, not reliable attribution. Other infected sites can use completely different plugin names, descriptions, authors, and filenames while retaining the same underlying persistence system.

Figure 1 — The infected wp-content directory. The leading dot on .59e2e230.php makes the larger loader hidden on Unix-like systems. The smaller visible 59e2e230.php is a wrapper. The listing also shows .user.ini; 83af5c0a.zip is another recovery source.
Executive findings
- The recovered main payload is the 185,171-byte file named
trace-scanner-lite.phpon this victim. It contains an internal4.0.3identifier even though its fake plugin header claims version1.1.0. The plugin name is an alias, not a dependable family-wide indicator. - The normal plugin and MU-plugin copies are redundant. The MU copy is automatically loaded by WordPress and was changed to permission mode
0444to make casual editing harder. db.phpand the infected block in the theme’sfunctions.phpeach embed an exact compressed copy of the main payload. After decoding, both have the same SHA-256 as the recovered plugin.advanced-cache.phpcan restore the payload from a surviving file, a ZIP backup, a database option, or System V shared memory.- A malicious
.user.inisetsauto_prepend_fileto the visible loader; that loader includes the hidden loader inwp-content. This activates recovery code before normal PHP application code in the directive’s directory scope. - The payload also stores itself in WordPress options, shared memory, and ZIP files, and creates hashed installers, loaders, guards, and a service worker.
- It uses Ethereum smart-contract calls as a resilient command-and-control locator, then exchanges XOR-obfuscated JSON with the returned C2 URLs.
- It captures administrator passwords, creates a hidden administrator, and forges valid WordPress authentication cookies for every existing administrator.
- It can search shared-hosting paths for sibling WordPress sites and infect those writable by the same operating-system account.
- The malware deliberately backdates files. Dates displayed in a hosting file manager are not a trustworthy infection timeline.
Observed plugin aliases: a living list
The attacker-controlled plugin identity is camouflage. It should be treated as a secondary search term and a pivot for finding related samples—not as the primary detection criterion. This table is intentionally designed to be extended when another alias is recovered.
| Observed display name or slug | Locations observed | Sample status | Notes |
|---|---|---|---|
Trace Scanner Lite / trace-scanner-lite | Normal plugin and matching MU-plugin | Confirmed in this sample set | Fake header claims version 1.1.0; internal persistence markers identify 4.0.3. |
When adding a new alias, record its display name, directory slug, PHP basename, file hash, internal SC marker/version, contract addresses, selector, and whether its decoded payload matches an existing sample. A shared plugin name alone is weak evidence; matching persistence behavior, markers, decoded code, or C2 configuration is much stronger.
Future samples should extend this alias record without changing the article’s primary keyword or detection strategy.
The persistence architecture
The easiest way to understand the infection is as a graph, not a chain:
site-root or wp-content/.user.ini
│ auto_prepend_file
▼
wp-content/59e2e230.php ──includes──► wp-content/.59e2e230.php
│
▼
restores the MU payload
WordPress bootstrap
├── wp-content/mu-plugins/<alias>.php ────────────┐
├── wp-content/plugins/<alias>/<alias>.php ───────┤
├── wp-content/advanced-cache.php ────────────────┤ restore each other
├── wp-content/db.php ────────────────────────────┤
└── active-theme/functions.php ───────────────────┘
│
▼
database option + shared memory + ZIP backups + cache installers
│
▼
browser service worker with scope "/"
This design creates circular recovery. Removing one node leaves several others able to reconstruct it on the next PHP request, WordPress bootstrap, cron run, shutdown callback, or infected administrator-browser action.
Recovered component map
| Artifact | Proven role |
|---|---|
.user.ini | Configures PHP auto_prepend_file to run the visible wp-content loader before requested PHP scripts in its directory scope. |
wp-content/59e2e230.php | A 231-byte wrapper that includes the hidden loader. |
wp-content/.59e2e230.php | Locates wp-content and restores the MU payload from the normal plugin, a cache installer, or ZIP backup. |
mu-plugins/<alias>.php | Primary automatically loaded payload and preferred persistence copy. The recovered alias was trace-scanner-lite.php. |
plugins/<alias>/<alias>.php | Redundant primary payload presented as an ordinary plugin. The recovered slug was trace-scanner-lite. |
advanced-cache.php | WordPress drop-in that can recover the payload from files, shared memory, ZIP, or a database option. |
db.php | WordPress database drop-in containing a complete embedded copy of the payload. |
theme functions.php | Legitimate theme file with an appended SC_TH_BEGIN block containing the complete payload. |
83af5c0a.zip and copies | Backup archives containing the fake plugin directory and PHP payload. |
| hashed cache/core PHP files | Installer, loader, guard, and service-worker delivery mechanisms. |
| WordPress options and transients | Compressed source, manifest, C2 state, captured credentials, scheduling, and recovery data. |
| System V shared memory | Fileless backup of the payload surviving deletion of disk copies while PHP workers remain alive. |
| browser service worker | Captures login submissions, injects admin responses, receives JavaScript, and can reinstall the plugin. |
Why the files returned within seconds
The observed regeneration is fully consistent with the code.
- A request enters PHP. Because
.user.inidefinesauto_prepend_file, PHP runs the small loader before the requested script. That loader reaches the hiddenwp-contentloader, which can recreate the MU plugin. - During WordPress startup, MU plugins load automatically. WordPress also recognizes
advanced-cache.phpanddb.phpas special drop-ins. Any surviving one can run recovery logic. - Both the infected theme and
db.phpcarry a complete copy of the main payload. Deleting the plugin directory therefore does not delete the payload. advanced-cache.phpcan recover from ZIP, a WordPress option, or shared memory even when the visible PHP copies have been removed.- Scheduled and opportunistic callbacks repeat the checks. An infected administrator browser can also participate through its service worker.
PHP documents that auto_prepend_file is parsed before the primary file, and that per-directory .user.ini files are used by CGI/FastCGI SAPIs. PHP also caches per-directory configuration; the default user_ini.cache_ttl is 300 seconds. That is why deleting .user.ini alone may not immediately stop already running workers. See the PHP documentation for per-directory INI files, auto_prepend_file, and INI directive defaults.
The incident description places .user.ini in the site root, while Figure 1 appears to show a copy in the wp-content listing. Multiple copies may have existed, or the captured/downloaded location may have changed. This distinction matters: a .user.ini affects its directory and descendants. Responders should inspect the document root, wp-content, parent directories, and PHP-FPM/vhost configuration instead of assuming there was only one directive.
Renaming wp-content worked as an emergency interruption because several calculated and hard-coded paths suddenly stopped resolving. A safer repeatable response is to take the virtual host offline or stop its PHP workers, preserve evidence, remove every recovery node in one maintenance window, clear the non-file stores, and restart clean workers.
The two plugin locations are intentionally related
WordPress loads must-use plugins automatically, and they are not disabled through the normal Plugins screen. That makes mu-plugins an attractive persistence location. The malware copies itself there, sets restrictive permissions, removes its normal copy from WordPress’s active-plugin list, and hides its entries from several administrative views. WordPress’s own documentation explains the automatic-loading behavior of must-use plugins.

Figure 2 — The ordinary plugin directory. Its matching MU-plugin copy is not a second infection; both are replicas in the same recovery system.

Figure 3 — The MU copy was 180.83 KB and set to 0444. The payload can change permissions when it needs to replace a file and set them back afterward.
The plugin also hooks WordPress plugin lists, update data, Site Health output, user-interface rendering, and popular file-manager/elFinder filters. It can remove its own rows and counts or filter its filenames from file-manager results. Therefore, “not visible in wp-admin” is not evidence that the file is absent.
Drop-in and theme implants
WordPress recognizes files such as advanced-cache.php and db.php as drop-ins with special bootstrap behavior. advanced-cache.php is loaded when WP_CACHE is enabled; db.php can replace the database abstraction layer. These are legitimate filenames used by real caching and database products, so the filenames alone are not malicious. The recovered contents are malicious. WordPress maintains the official drop-in list and documents the advanced-cache loading switch.
The samples use distinct markers:
SC_ADV_BEGIN:4.0.3:d17ba8dfSC_DB_BEGIN:4.0.3:24263291SC_TH_BEGIN:4.0.3:ccd02512
The db.php implant and the block appended to functions.php each contain 71,804 Base64 characters representing a gzip-compressed plugin. Decoding either produces 185,171 bytes with SHA-256:
8284d68274c0475118732110680e38d9da27b4173997c0db0d9f6e5283109554
That is exactly the hash of the recovered trace-scanner-lite.php. This byte-for-byte match is the strongest proof that the files belong to one persistence system.

Figure 4 — The injected block in the active theme’s functions.php. Restoring only the plugin while retaining this block allows the theme to recreate it.
Backups in ZIP, database, memory, and cache
The main payload does not rely only on PHP implants:
- It writes ZIP archives containing
<plugin-slug>/<plugin-slug>.phpunderwp-content, the current uploads year/month directory, and one writable theme. - It saves compressed source in a WordPress option and records path, size, and modification data in
sc_persist_manifest. - Where the PHP extension is available, it stores the payload in System V shared memory. The key is derived with
ftok(wp-config.php, 's'). - It creates hashed installers and loaders in cache and other writable locations, using temporary files and atomic rename/copy operations.
- It invalidates OPcache entries after replacements so the newly restored code can be used promptly.

Figure 5 — A second 83af5c0a.zip in a writable theme directory. Searching only the top level of wp-content would miss backup copies.
Why names differ between affected sites
Some names are deterministic hashes of the victim’s WordPress absolute path (ABSPATH) plus a short role label. The code uses the first characters of an MD5 digest—not for security, but to create stable, site-specific names.
For the private site root used in this analysis (redacted), the calculations produce:
| Role | Formula | Result observed in the incident |
|---|---|---|
| Loader | first 8 of md5(ABSPATH + "ldr") | 59e2e230 |
| ZIP | first 8 of md5(ABSPATH + "zip") | 83af5c0a |
| Cache installer | first 8 of md5(ABSPATH + "inst") | b411933e |
| Service worker | first 8 of md5(ABSPATH + "sw") | e777f187 |
| Database option | first 10 of md5(ABSPATH + "opt") | 8aeb646ca5 |
| Theme marker suffix | first 8 of md5(ABSPATH + "th_m") | ccd02512 |
A different absolute path produces different hexadecimal names. The fake plugin slug can also vary. Within one site, the MU-plugin and ordinary-plugin copies retain the same basename so the loaders can locate and restore either replica. Defenders should hunt for behavior, markers, payload hashes, and C2 configuration—not only 59e2e230 or trace-scanner-lite.
Command-and-control design
The malware does not hard-code only one ordinary C2 domain. It treats Ethereum as a decentralized dead-drop resolver:
- It rotates through a list of public Ethereum JSON-RPC providers.
- It issues
eth_callwith function selector0x3bc5de30against one of three embedded contract addresses. - It parses the contract response to obtain an XOR key and encrypted, newline-separated C2 URLs.
- It posts XOR-obfuscated JSON to the resolved servers and remembers the last RPC provider that worked.
Ethereum documents eth_call as a method that executes a contract call without creating a transaction: Ethereum JSON-RPC API. The public RPC services in the IOC list are legitimate shared infrastructure being abused as a resolver. Their appearance is supporting evidence only and is not a reason to label the providers malicious.
The outbound status data can include the domain, WordPress paths, payload version, installed and active plugins, MU plugins, the login URL, malware-created credentials, captured administrator credentials, generated auth cookies, and error information. The returned encrypted configuration can contain replacement PHP, plugin-deletion and injection-cleaning rules, JavaScript, service-worker content, and templates for the loader, guard, drop-ins, theme block, .htaccess, .user.ini, and installer.
Before installing a remotely supplied plugin update, the malware performs a token-level PHP syntax check and then replaces its payload atomically. It disables TLS certificate verification for its direct C2 requests, further reducing transport safety.
Credential theft and concealed access
The code demonstrates several independent ways to retain administrator access:
- It hooks WordPress authentication at a late priority. After a successful administrator login, it records the submitted plaintext password in a hidden option.
- It creates a hidden administrator with a name beginning
admin_,adm_,administrator_, orbackup_, followed by ten lowercase hexadecimal characters. If normal WordPress APIs fail, it can insert the user and metadata directly into the database. - It filters user queries, REST results, counts, and admin notifications to conceal that account.
- It generates fresh WordPress authentication and logged-in cookies for every existing administrator, with a 14-day expiry, and can send them to C2. The attacker therefore does not need to know each administrator’s password.
- It suppresses selected new-user and login notifications that could alert the site owner.
Changing one administrator password is not sufficient. All accounts, sessions, salts, hosting credentials, and other secrets exposed to the compromised environment must be treated as compromised.
The administrator browser becomes another persistence layer
The built-in service-worker template is 6,429 bytes with SHA-256:
7bafee00a010ed3db3521a89e8f5d50d2f677204c56995e99c7603768a9f0bd2
It can intercept POST submissions to wp-login.php, extract log and pwd, resolve C2 through the same Ethereum mechanism, encrypt the data, and post it to a /w endpoint. It can alter navigations to wp-admin, receive Base64 JavaScript and execute it, and attempt plugin reinstallation using an authenticated administrator’s upload nonce and WordPress’s plugin-upload route.
The malware sends Service-Worker-Allowed: / and registers scope /, allowing a worker delivered below the root to control a broader area when the browser accepts it. MDN documents the scope-expansion role of the Service-Worker-Allowed header.
This means server cleanup alone may leave an administrator browser capable of leaking new credentials or helping reinstall the payload. Every browser/profile used to administer the site should have the site’s service worker unregistered and its site data cleared after the server has been cleaned.
Scheduled recovery and remote maintenance
The payload registers sc_cron_fetch on a custom 36,000-second interval (10 hours), initially scheduled roughly one to five minutes after installation. It also establishes another approximately hourly recovery event and can perform C2 work during shutdown after flushing the response. WordPress cron is traffic-driven: a due event executes when a visit occurs after its scheduled time. See wp_schedule_event().
The server can return dynamic rules telling the implant which plugins or injected snippets to remove. The code can deactivate or delete matching plugins and strip matching code from plugins, MU plugins, or themes. Because the actual regular expressions are delivered by C2 and were not present in the static files, the precise targets cannot be responsibly claimed from this sample.
Lateral spread across a hosting account
The malware scans parent directories, open_basedir roots, and common shared-hosting paths including /home, /var/www, /var/www/vhosts, /srv/www, and /srv/users. Within a limited depth and time budget, it looks for sibling directories containing wp-admin.
For a writable WordPress root, it places the payload in the target’s MU-plugin directory or ordinary plugin directory. In the latter case, it reads the sibling site’s wp-config.php to obtain DB_NAME and $table_prefix, then attempts to update that site’s active_plugins value directly. Successful spreading is throttled for about three days; an unsuccessful scan can retry sooner.
This behavior explains how multiple sites under one shared Unix account can show the same malware without each site being independently exploited. It does not prove which site was patient zero.
Obfuscation and anti-forensics
The sample uses a large indexed string table, substitution decoding, random-looking function names, split strings, Base64/gzip payloads, fake plugin metadata, suppressed errors, and exception handling. It also uses advisory locks or transients to prevent its own workers from colliding.
More importantly, it manipulates timestamps. It can copy plausible timestamps from existing WordPress PHP files or backdate artifacts by roughly a year, then apply deterministic adjustments. The displayed “Last Modified” values in the screenshots cannot establish the infection date. This aligns with MITRE ATT&CK’s description of Timestomp (T1070.006).

Figure 6 — A second directory capture corroborates the same loader, drop-in, and backup pattern. Its displayed dates should be treated as presentation metadata, not a reliable compromise timeline.
Database persistence confirmed in the recovered site
Read-only database queries performed after the static file analysis confirmed that the database was an active part of this persistence mesh. The results were collected directly from the WordPress options table using its site-specific prefix. Complete option values are withheld because they can contain payloads, credentials, cookies, paths, or other sensitive incident data.

Figure 7 — The query returned six stable sc_* options and nine SC transients or timeout rows. The largest confirmed row was sc_payload_persistent at 257,408 bytes.
The confirmed option rows were:
| Option name | Captured size | Evidence classification |
|---|---|---|
sc_initialized | 1 byte | Initialization flag |
sc_persist_manifest | 356 bytes | Persistence manifest |
sc_last_recovery_check | 10 bytes | Recovery timing state |
sc_last_rpc | 34 bytes | RPC state |
sc_payload_persistent | 257,408 bytes | Large persistent payload store |
sc_last_fetch_ts | 10 bytes | Fetch timing state |
The exact names, their grouping, and the large payload row directly corroborate the database behavior implemented in the recovered PHP. They are much stronger evidence than a generic large-option search.

Figure 8 — The transient-specific query isolated administrator-tick, payload, spread, guard, and recovery records. _transient_sc_payload_t contained 193,053 bytes.
The transient and timeout pairs show that the malware used WordPress’s temporary option mechanism for more than scheduling flags. In this capture, one transient held another large payload-sized value. The associated timeout row was only 10 bytes, consistent with WordPress storing transient expiration separately.
Point-in-time cron finding

Figure 9 — The serialized WordPress cron option was 8,091 bytes, but the exact sc_cron_fetch string was not present when queried.
This is a bounded negative result. It shows only that the known hook was absent from that row at the time of capture. It does not contradict the scheduling logic in the recovered code, establish that the hook was never registered, or exclude a renamed or separately implemented event.
Large hexadecimal option names require validation

Figure 10 — The largest-options view showed the confirmed SC payload rows alongside 60909f14f4a5 at 261,136 bytes, ce9ccf62d5c0 at 151,112 bytes, and c9fe246a17 at 76,112 bytes.
The first two hexadecimal rows had option IDs interleaved with the confirmed sc_* records, making them high-priority investigation candidates. The screenshot does not prove that any of the three is malicious. Legitimate plugins can also store large values under generated names, and the displayed size or hexadecimal format is not a safe deletion criterion.
The previously calculated database-option name 8aeb646ca5 came from the recovered code’s site-path formula. It was not returned in this database capture, and this report does not claim that one of the newly observed hexadecimal options is equivalent to it. Across other sites, responders should first hunt stable SC markers, then export and validate each site-specific candidate through ownership checks, safe offline decoding, manifest references, neighboring option IDs, and decoded-payload hashes. Hexadecimal names from one victim must never become a universal deletion list.
Generated administrator pattern not observed

Figure 11 — No username matched admin_, adm_, administrator_, or backup_ followed by ten lowercase hexadecimal characters.
This second negative result does not clear the user table. The malware can change, an operator can choose an ordinary-looking username, or an account may already have been removed. Responders must still list every database-level administrator by capabilities metadata and verify each account with the site owner.
What the samples do—and do not—reveal about initial access
The recovered code proves persistence, credential theft, C2, defense evasion, browser compromise, and lateral spread. It does not contain reliable evidence of the first exploit used against the original hosting account.
Reasonable hypotheses include:
- a vulnerable or malicious plugin/theme;
- a stolen WordPress administrator or hosting-control-panel credential;
- compromised SFTP, FTP, SSH, deployment, or developer credentials;
- a writable file-manager flaw; or
- lateral spread from another compromised WordPress installation under the same operating-system user.
The malware contains file-manager hiding logic, but that is post-compromise evasion and is not proof that a file-manager plugin was the entry point. To determine initial access, correlate the earliest clean and infected backups with raw HTTP access/error logs, WAF records, control-panel audit logs, SFTP/FTP/SSH logs, database audit data, WordPress audit logs, and browser history. Because the files are timestomped, prioritize server and provider logs over filesystem modification times.
Incident-response and cleanup procedure
Do not clean this infection by deleting files one at a time on a live site. That gives the surviving components an opportunity to recreate them.
1. Preserve evidence and contain execution
- Take a filesystem and database snapshot before changing anything. Preserve access, error, WAF, control-panel, SFTP/FTP/SSH, and database logs outside the compromised account.
- Put the site behind a static maintenance response or isolate the virtual host. Stop or restart its PHP-FPM pool/web workers as part of the maintenance window so cached
.user.ini, OPcache, and process-resident shared memory are not left active. - Do not sign in from a normal administrator browser until that browser’s service-worker state is addressed.
- Identify every site running as the same operating-system user. Treat all writable sibling sites as in scope.
2. Remove every recovery source in one coordinated pass
- Remove the malicious normal and MU-plugin copies.
- Remove both the visible and hidden hashed loaders, plus malicious
auto_prepend_filedirectives in.user.ini,.htaccess, virtual-host, or pool configuration. - Replace
advanced-cache.phpanddb.phpwith known-clean versions from their legitimate product, or remove them only after confirming the site does not require the drop-in. - Restore infected theme files from a trusted source or remove the exact marked block after a careful diff. Inspect inactive themes too.
- Find and remove the malicious ZIP copies from
wp-content, uploads, and theme directories. - Find hashed installers, service-worker scripts,
.g_*.phpguards, and PHP files in cache/upload directories. Do not assume that every hexadecimal filename is malicious; validate content and hashes. - Remove malicious WordPress options, transients, and cron hooks, including stored payloads, manifests, C2 state, and captured credentials. Export and review them before deletion when possible.
- Clear the implant’s System V shared-memory segment or restart the isolated PHP/web environment in a way that destroys it. Clear OPcache after clean files are in place.
- Rebuild WordPress core, plugins, and themes from verified vendor packages. Diff and separately restore only reviewed custom code and uploads.
3. Revoke access and recover securely
- Remove unknown administrator accounts, especially the generated prefix patterns described above. Verify users directly in the database as well as through wp-admin.
- Rotate every WordPress password, WordPress salts/keys, hosting and control-panel credentials, SSH/SFTP/FTP credentials, database password, deployment tokens, SMTP credentials, and API keys available to the compromised site.
- Destroy all WordPress sessions. WP-CLI provides
wp user session destroyfor this purpose. - From every administrator device and browser profile, unregister the site’s service worker and clear cookies, caches, IndexedDB, Cache Storage, and other site data. Then sign in only after server cleanup and credential rotation.
- Reinstall security and monitoring controls from clean sources, verify outbound network policy, and monitor filesystem changes, new administrators, scheduled events, service-worker endpoints, and egress to the indicators below.
- Where possible, isolate unrelated sites under separate operating-system users and PHP pools. Restrict the web-server account’s write access to code, allowing only the directories that genuinely require it.
Detection and hunting guidance
High-value content indicators include:
SC_ADV_BEGIN:4.0.3:
SC_DB_BEGIN:4.0.3:
SC_TH_BEGIN:4.0.3:
sc_cron_fetch
sc_payload_persistent
0x3bc5de30
auto_prepend_file
Hunt all sites owned by the account, not only the site where the first file was noticed. Examine:
wp-content/mu-plugins, ordinary plugins,advanced-cache.php,db.php, and active/inactive themefunctions.phpfiles;- document-root,
wp-content, and parent.user.ini/.htaccessfiles, plus PHP-FPM/vhost configuration; - ZIP files under
wp-content, uploads, and themes; - unexpected PHP in cache or uploads, files beginning
.g_, and short hexadecimal filenames; - WordPress options, transients, cron arrays, user/user-meta tables, and unknown administrators;
- outbound JSON-RPC POSTs containing
eth_calland selector0x3bc5de30; and - administrator browsers under Developer Tools → Application → Service Workers, especially a worker with scope
/.
The exact filename 59e2e230.php is sample-specific because it depends on ABSPATH. Contract addresses and the selector are stronger family indicators. Public Ethereum RPC URLs are low-confidence network indicators because unrelated legitimate software uses them.
The higher-confidence content, hash, and protocol indicators are listed below. None requires a particular fake plugin name.
MITRE ATT&CK mapping
This is an approximate defensive mapping, not a claim that every enterprise ATT&CK scenario applies directly to WordPress:
| Technique | Evidence in this sample |
|---|---|
| T1027 — Obfuscated Files or Information | Substitution-encoded strings, random identifiers, split strings, and compressed/Base64 payloads. |
| T1056.003 — Web Portal Capture | WordPress login password capture in PHP and the browser service worker. |
| T1070.006 — Timestomp | Plausible timestamp copying and deliberate backdating. |
| T1053 — Scheduled Task/Job | WordPress cron events repeatedly fetch configuration and repair persistence. |
| T1102.001 — Dead Drop Resolver | Ethereum smart-contract responses locate the current C2 servers. |
| T1505.003 — Web Shell | Closest ATT&CK fit for a persistent server-side web implant receiving remotely controlled PHP/JS and commands. |
Indicators of compromise
SHA-256 hashes
| Recovered file | SHA-256 |
|---|---|
recovered core payload (trace-scanner-lite.php alias) | 8284d68274c0475118732110680e38d9da27b4173997c0db0d9f6e5283109554 |
hidden .59e2e230.php (downloaded as 59e2e230.php) | 5e0634b36af4fab6931e3efc120e5baee1953f32f14a516cbc4c927786d4309c |
visible 59e2e230.php wrapper (downloaded as 59e2e230 (1).php) | 96d36f0432295b0a833e6d835c8f3100f900ace2f08f0584ea0d2a83e7df8b82 |
advanced-cache.php | 1bd222238cd2679cd8dcdd8e1d391339ae07f009b67277929951d18d75d55cf0 |
db.php | a3c6fcd1df2c1fec977427f056aa45852f7ae7a9f63ff79813057e451eeed6c3 |
infected functions.php | b516bd7eb514b80555fc3a04ac30405e52f128c6d08da3baf03e74f1ad2fe1fb |
.user.ini | ac7c266d12c6d3d979594b4d74c545ecf2546ac704f2ac07bde1276010747c0d |
| extracted service-worker template | 7bafee00a010ed3db3521a89e8f5d50d2f677204c56995e99c7603768a9f0bd2 |
Ethereum contract and protocol indicators
0x9A4752cAA1C15868487A0ACb691F81bfA901E063
0x839d1cE5c3F259e8d3D17114d7186EDabdbeA94b
0x6d2c5435EF70196740a48904B69377935D50abBB
eth_call selector: 0x3bc5de30
The full RPC endpoint list, sample-specific names, markers, hashes, and context are in iocs.csv.
Confidence, limitations, and attribution
Confidence is high for the persistence relationships, exact embedded-payload equality, credential theft, browser service-worker behavior, Ethereum resolver, and sibling-site spreading because each is directly implemented in the recovered code. Confidence is low for the initial-access method because the necessary historical logs and earliest artifacts were not part of this sample set.
I refer to this cluster provisionally as SC 4.0.3 self-healing WordPress malware because the internal markers are more stable than the attacker-selected plugin identity. Trace Scanner Lite is one observed alias, not the family name. The versioning may itself be campaign metadata rather than a formal malware-family designation. I make no attribution to an individual or organization.
During this analysis I found independent August 2026 reporting describing the same SC 4.0.3 marker scheme and Ethereum bootstrap. Those reports corroborate the broader campaign but do not reveal the initial access for this particular site: Monarx analysis and Seven Labs case study.
Conclusion
The defining feature of this infection is not any single file. It is the redundancy between PHP startup configuration, WordPress MU plugins and drop-ins, a normal plugin, theme code, ZIP archives, the database, process memory, scheduled jobs, and administrator browsers. Each deletion that leaves one trusted execution path intact gives the malware a chance to rebuild.
The correct response is therefore coordinated eradication under execution containment, followed by complete credential and session revocation, browser cleanup, clean-source rebuilding, sibling-site inspection, and monitoring. The fact that temporarily renaming wp-content stopped the immediate regeneration was not a coincidence—it interrupted the paths connecting the persistence mesh. The permanent fix is to remove every node and close the original access route once log analysis identifies it.
Support My WordPress Security Research ☕
If this work helped you and you would like to support future malware investigations and practical guides, you can buy me a coffee. It is completely optional—everything remains free to read.