Reg. No. 40203410806VAT LV40203410806

Theme

Book a consultationBook

02 / 07Mirage field report – 004

UpdraftPlus: PHP backdoor delivery attempts via CVE-2026-10795

WordPress · UpdraftPlus remote management and plugin installation

A forged UpdraftPlus management request delivered a PHP backdoor to a Mirage decoy on 6 September. The retained campaign contains 20 plugin-upload attempts and 76 requests checking exact payload paths. Static analysis identifies command execution, file management and upload functions in the complete recovered sample.

CVE-2026-1079513 min read

§ 01. Summary

At 14:10:43.761 UTC on 6 September 2026, 193.148.57.33 sent a forged UpdraftPlus management request to a Mirage web decoy. The encrypted message requested plugin.upload_plugin, supplied wp_zmfjlow.zip and set activate to true. Its PHP payload implements OS command execution, file management, uploads and an unauthenticated reachability marker.

The retained cohort contains 20 plugin-delivery attempts and four enumeration probes from 2 July through 6 September. Eighteen delivery attempts were followed by 76 GETs to their exact randomized PHP paths. These were attempts against decoys; successful installation or execution was not observed. The malicious plugin arrived inside a forged request, with no evidence of a compromised official UpdraftPlus package or update service.

§ 02. Affected versions

CVE-2026-10795 requires an affected plugin version and applicable remote-communications keys. Wordfence describes sites previously connected to UpdraftCentral. The vendor's release notes identify an active Premium Migrator key, or an UpdraftCentral key in the free or paid plugin, as prerequisites. Retained keys remain relevant after a site stops using remote management.

ComponentAffected / relevant configurationFirst fixed release
UpdraftPlus freeVersions through 1.26.4, with applicable remote-communications key state1.26.5
UpdraftPlus PremiumVersions before 2.26.5, with applicable remote-management or Migrator key state2.26.5
UpdraftCentral base pluginRelated product covered by the vendor's June security update; inventory it separately0.8.32
Table 1Minimum fixed releases; install the latest supported release from the vendor or WordPress.org. The captured requests target UpdraftPlus and do not establish exploitation of the related UpdraftCentral products.

The 5 June vendor advisory provides an official hotfix for customers unable to upgrade, including those with expired Premium licences, and instructions for subsequently moving to a fixed release. UpdraftCentral Premium itself does not require the corresponding update.

§ 03. Delivery request

The complete 4,087-byte POST body used this legitimate application endpoint:

http

POST /wp-admin/admin-ajax.php?action=updraft_central_ajaxcallback
Listing 1The endpoint alone is not a malicious indicator.

Its form included format=1, key_name=0.central.updraftplus.com and udrpc_message. The decrypted JSON follows; only the embedded ZIP has been replaced with a placeholder.

json

{
  "command": "plugin.upload_plugin",
  "time": 1788703843,
  "key_name": "0.central.updraftplus.com",
  "rand": 123456789,
  "data": {
    "filename": "wp_zmfjlow.zip",
    "activate": true,
    "data": "[embedded ZIP omitted]"
  }
}
Listing 2Recovered command from 6 September: upload the supplied plugin and request activation.

The ZIP contains one 3,686-byte PHP file, wp_zmfjlow/wp_zmfjlow.php. A normal plugin installation would place it at /wp-content/plugins/wp_zmfjlow/wp_zmfjlow.php, repeating the same apparently randomized directory and filename stem. Hashes of the complete request, ZIP and PHP are listed under Indicators.

§ 04. Authentication bypass

The observed format=1 selects a legacy message path; separate signature checks in the vulnerable implementation apply to formats 2 and above. The format carries an RSA-encrypted symmetric key followed by an AES-encrypted message.

The implementation uses the RSA decryption result without rejecting failure. An invalid RSA input returns false, which becomes an all-zero AES key; the IV is also zero. This predictable state allows a forged message without a victim's private key. The Wordfence disclosure documents the validation failure and patch.

The complete sample begins with the three-character hexadecimal length 004, followed by AA==: a single NUL byte after base64 decoding, invalid as RSA ciphertext here. The next 16-character hexadecimal length, 0000000000000ec0, specifies 3,776 characters of base64-encoded ciphertext. Offline AES-128-CBC decryption with a 16-byte zero key and 16-byte zero IV recovers the JSON and ZIP.

On a vulnerable site with the required key and associated user state, the dispatcher acts with the configured connection's privileges, potentially reaching plugin installation. The patch rejects invalid key material before dispatch. The listener runs during WordPress loading, so the observed AJAX path is not the only possible request target; inspect RPC fields on other URLs when body evidence exists.

§ 05. PHP backdoor

The complete small payload is a self-contained webshell; these functions require no external download.

CapabilityImplementation
Reachability checkUnauthenticated postpass2 response marker
Password gateroot parameter checked against hard-coded admin888, independently of WordPress login
OS command executionsystem($_GET['c']), subject to the PHP process's permissions and restrictions
File managementDirectory listing, file reading and deletion
File uploadb64data, fn and repeating XOR key myXorSecret

All 76 matching historical checks were bare GETs, without command or password parameters. The marker is ungated; command and file operations require the backdoor password. The parameter name root does not confer OS root privileges.

The upload branch's XOR encoding is separate from the outer encrypted RPC. Its key is also embedded in browser-side JavaScript. The PHP applies basename() to the submitted filename and writes relative to its working directory; selecting a different browsing directory in the file manager does not change the upload destination.

Access to wp-config.php, database credentials, customer uploads or backups would depend on the web process's permissions and local configuration.

Activation and direct access

The payload's top-level code terminates the request when its gate is absent. WordPress's activation routine includes the plugin before updating the active-plugin option. Together, these paths suggest the payload could terminate activation before that option is updated. This is a static inference; the sample was not run.

An activate:true instruction therefore does not guarantee normal activation, even if installation succeeds. A PHP file under a web-served plugin directory may remain directly reachable without activation, depending on server rules. Investigations must include plugin files and direct-path access history, including directories absent from the active-plugin list.

§ 06. Verification requests

The 76 GETs to 18 exact plugin paths match each upload's source IP, requested hostname and recovered path, from upload time until the earlier of 24 hours later or the analysis cutoff. All also share their upload's internal session identifier.

Fourteen small-payload attempts have four checks each (56 GETs); all four larger-payload attempts have five (20 GETs). Two small-payload attempts have no retained match, including the latest request, whose available follow-up window is only 2 hours, 9 minutes and 16.239 seconds.

Event on 17 JulyUTC timeSeconds after upload
Upload names wp_ahzulfl.zip07:37:57.8840.000
GET to matching PHP path07:37:59.5761.692
GET to matching PHP path07:38:00.2032.319
GET to matching PHP path07:38:00.8082.924
GET to matching PHP path07:38:01.4913.607
Table 3Four checks to /wp-content/plugins/wp_ahzulfl/wp_ahzulfl.php after the upload named that path.

A larger-payload delivery on 22 July had five checks 1.675–5.373 seconds later; another on 30 August had five 3.739–16.125 seconds later. These sequences indicate attempted verification. The decoy did not run WordPress's installer, and a generic HTTP 200 response does not establish that the sender received the webshell marker.

§ 07. Payload variants

The archives declare 16 PHP members of 3,686 bytes and four of 19,741 bytes. Their recovered PHP prefixes differ. The larger form presents itself as a WordPress database-cache and media-library handler.

Source 95.85.238.18 submitted a small payload on 15 July and a larger one on 22 July. Three more larger-form deliveries occurred in August; the complete 6 September payload is small. The forms coexist in the retained history, without establishing separate malware families.

Most historical records retain only the first 1,024 characters of the body. Complete available cipher blocks and partial ZIP streams expose commands, archive names, declared sizes and PHP prefixes. They do not recover the larger file in full. The older 30 August object was unavailable for retrieval, so its full behavior and file hash remain unknown. The small webshell's capabilities must not be attributed to the larger form without further evidence.

§ 08. Tool fingerprints

The four enumeration probes request plugin.load_plugins and cycle through 0.central.updraftplus.com, 1.central.updraftplus.com, 2.central.updraftplus.com and migrator.updraftplus.com.

TraitPlugin-delivery requestsEnumeration probes
Decoded RPC commandplugin.upload_pluginplugin.load_plugins
Invalid RSA blockOne NUL byte128 ASCII A bytes
rand fieldFixed 123456789Six hexadecimal characters
JSON serializationSpaces between fieldsCompact serialization
Package namingwp_ plus seven lowercase lettersNo delivered plugin ZIP

The enumeration formatting matches the 12 June revision of a public proof of concept. It does not identify the operator or distinguish an attacker from a researcher or automated service. The upload requests contain a malicious backdoor package.

The central.updraftplus.com and migrator.updraftplus.com names are legitimate application key identifiers, not C2 domains. Blocking them can disrupt legitimate services. A python-requests user agent is also too generic for attribution or blocking.

§ 09. Campaign scope

The cohort spans 11 source IPs: ten upload sources and one enumeration source. Requests reached all eight edge personas, including profiles that did not advertise WordPress, consistent with indiscriminate delivery attempts.

PeriodPlugin-upload requestsEnumeration requestsTotal RPC requests
July 202616420
August 2026303
September through the cutoff101
Total20424

Timeline of 24 retained UpdraftPlus RPC requests from July to September, separating 16 small-payload uploads, four larger-payload uploads and four enumeration probes.

Horizontal positions preserve request times; close uploads are separated vertically and the four enumeration requests are grouped. Download SVG or PNG.

The CSV timeline lists each RPC timestamp, source, command, package name, declared PHP size and subsequent check count. The evidence manifest records the cutoff, hashes and limits. Decoy hostnames and internal identifiers are omitted.

The earliest retained request, 2 July, follows the 5 June patch and 10 June disclosure. All 24 records lacked a CVE identifier in the mirror; their request contents identify the vulnerability.

§ 10. Detection

Request correlation

Ordinary access logs usually contain methods, URIs, status codes and response sizes, not POST bodies. Search them for the AJAX action and subsequent plugin paths. Inspect RPC fields in existing WAF, proxy or application body captures; indiscriminate body logging may expose passwords, sessions and personal data.

shell

# Request-path triage on copied, uncompressed access logs.
rg -n --hidden --no-ignore -i 'updraft_central_ajaxcallback' ./copied-access-logs/

# This campaign uses the same seven-letter stem twice in the PHP path.
rg -n --hidden --no-ignore --pcre2 '/wp-content/plugins/(wp_[a-z]{7})/\1\.php' ./copied-access-logs/
Listing 3The second search requires PCRE2 support. It misses renamed variants and may match legitimate files.

Correlate hits by site, source, timestamp and exact plugin path. Confirm file presence and contents from an evidence copy; do not visit a suspected webshell to test it.

File inspection

Search an isolated filesystem copy for the recovered sample's markers:

shell

rg -l --hidden --no-ignore -g '*.php' \
  -e 'postpass2' -e 'myXorSecret' -e 'admin888' \
  ./site-copy/wp-content/
Listing 4This lists files containing any marker. Review the combination of postpass2, myXorSecret, b64data and admin888; one string is insufficient, and no matches do not clear a site.

The YARA rule targets the complete small payload, not CVE-2026-10795 generally or the larger form. It matches the recovered file and produced no matches across 843 PHP files in the official UpdraftPlus 1.26.5 package. Required-string, optional-string and size-bound checks passed. This limited corpus does not establish a field false-positive rate.

After finding unauthorized PHP, also inspect must-use plugins, drop-ins, themes, uploads, scheduled tasks, .user.ini and server configuration. These are incident-scoping checks, not observed persistence mechanisms in this campaign.

§ 11. Incident response

Updating closes the vulnerability but does not remove an earlier backdoor. For evidence of intrusion:

  1. Preserve and contain. Record discovery time and timezone; preserve filesystem and database snapshots, web/WAF logs and deployment or administration history. Keep infected snapshots separate from trusted backups. Restrict a confirmed malicious endpoint or isolate the site. Do not post keys, database exports or complete request bodies in public support threads.
  2. Scope the incident. Record the UpdraftPlus edition, version and retained remote or migration keys. Check unexpected plugin files, administrator accounts and neighboring sites sharing the hosting account. Preserve suspicious files and hashes before removal.
  3. Patch. Install a current fixed release or official vendor hotfix. Remove obsolete remote-management or migration keys through supported interfaces after preserving relevant evidence.
  4. Recover. Rebuild or restore from a verified clean point using trusted packages; infected backups can reintroduce the implant. Validate custom and paid code against trusted developer distributions.
  5. Rotate access. From a clean workstation, rotate relevant WordPress, hosting, SSH/SFTP, database and service credentials; revoke suspect sessions and keys; replace WordPress authentication salts. Scope changes to accessible credentials and coordinate dependent services. Rotate again after cleanup if exposure continued during containment, then monitor.

See the WordPress hacked-site guidance for recovery procedures.

Checksum verification

The WP-CLI core verifier runs before WordPress loads; use a trusted CLI and configuration. The plugin verifier can bootstrap WordPress, and --skip-plugins still loads must-use plugins. It can therefore execute untrusted PHP from a compromised installation.

Core checks do not validate wp-content, databases or server configuration. WordPress.org plugin checksums do not cover every commercial or custom plugin. Missing checksums alone do not indicate malware; passing checksums do not clear an incident.

§ 12. Indicators

IP addresses are observed request sources and may be shared or reassigned. They do not establish an operator's identity.

IndicatorRole / scope
3b321d1d5568f32b0049682e935a4f6807096985e4aa857ec65cdde5fcf243a4SHA-256 of complete 4,087-byte POST body
8e8d88878379f0a6c640c99f48b0edf064761a6b79bb14aba553996aa143a59fSHA-256 of the embedded ZIP
ac87d574d74c854d1be68274a7915cfa5a3a6750ca3e89cec2ea02301d66cabeSHA-256 of extracted 3,686-byte PHP
wp_zmfjlow/wp_zmfjlow.phpMember path in the latest package
postpass2 · myXorSecret · b64data · admin888Correlated strings in the recovered small webshell
193.148.57.33Source of the complete 6 September payload; also observed in July
107.189.14.87 · 193.148.56.148 · 193.148.56.79 · 193.148.56.134 · 193.148.56.61Additional upload sources
95.85.238.18 · 45.13.186.114 · 198.98.53.213 · 45.61.185.216Additional upload sources, including the larger payload form
163.223.211.27Enumeration-only source in this cohort; do not label it a confirmed backdoor uploader
Table 6Observations through 6 September 2026, 16:20 UTC. Hashes identify this sample; filenames and source addresses may change.

Download the CSV timeline, JSON manifest, YARA rule and scope and reproduction notes. The bundle excludes the executable webshell and ready-to-send forged requests.

§ 13. Method and limits

The cohort comes from content searches of the retained Mirage event mirror, excluding explicitly benign and tenant-origin records, through 6 September 2026, 16:20 UTC. All 24 RPC records came from eight public edge personas; native-sensor traffic is excluded.

Analysis decoded recoverable messages and archive metadata, then correlated subsequent GETs to exact paths. The complete 6 September raw body was hashed against its collection record; its ZIP and PHP were examined statically. Historical body previews remained partial evidence. No extracted payload was executed or attacker server contacted.

Retention, truncation and capture policy limit these counts to stored requests. Missing records or later GETs do not establish an absence of activity. First seen in this archive does not establish first exploitation elsewhere. No victim installation, execution or data theft was observed. Victim counts and operator identity remain unknown.

§ 14. Prior research

vtim discovered the vulnerability through the Wordfence Bug Bounty Program; TeamUpdraft released the fixes. The Wordfence disclosure explains the predictable-key failure and plugin-installation route. The vulnerable source, public proof of concept and laboratory implementation predate this report.

§ 15. Observation timeline

  1. 2026-06-05TeamUpdraft releases the fixes; this date is vendor context, not a Mirage observation
  2. 2026-06-10Wordfence publishes its technical disclosure, crediting researcher vtim
  3. 2026-07-02Earliest matching plugin-delivery attempt in the retained Mirage cohort
  4. 2026-07-10Four plugin-enumeration probes cycle through distinct remote key names
  5. 2026-07-22First retained ZIP member declaring a 19,741-byte PHP payload
  6. 2026-08-30Larger payload form followed by five requests to its exact plugin path
  7. 2026-09-06Complete small payload recovered from the 14:10 UTC request; analysis closes at 16:20 UTC

Have similar exposure in your environment?