DEV Community

Hamza Ahmad Aslam
Hamza Ahmad Aslam

Posted on Originally published at hamzaahmadaslam.com

cPGuard supply chain attack: check your server and WordPress sites

Originally published at hamzaahmadaslam.com.

How this article was made: drafted with AI assistance from a researched brief, then checked against primary documentation before publishing.

The cPGuard supply chain attack put root-level server credentials at risk through a malicious automatic update on November 2, 2026. Check your cPGuard update history first, even if you have already patched the server. If an affected update was installed, rotate the exposed credentials and check every WordPress site hosted there.

What happened in the cPGuard supply chain attack?

An attacker broke into OPSSHIELD's billing and licensing infrastructure, obtained an update-storage key and published fake cPGuard updates.

According to OPSSHIELD's November 3 incident disclosure, the attacker uploaded the updates between 02:04 and 02:09 UTC on November 2, 2026. cPGuard agents installed them between approximately 02:08 and 03:20 UTC.

The affected updates were versions 5.88.12 through 5.88.19. The malicious script ran with root privileges, giving it access to sensitive server files.

OPSSHIELD reported the following figures on November 3:

  • About 2,950 customer servers ran the fake update.
  • About 7,900 billing-portal accounts had their names, company details, postal addresses, email addresses and bcrypt-hashed passwords exposed.
  • The script collected /etc/shadow, /etc/passwd, root SSH files, MySQL credentials where present and root shell history.
  • On a small number of servers, it also collected cloud storage credentials from /root/.s3cfg.

The script sent collected data through OPSSHIELD's compromised licensing system for the attacker to retrieve. The vendor's earlier advisory estimated that the attacker retrieved 32 collected files, each associated with a customer server. That preliminary figure does not limit the number of potentially affected servers.

The attacker also left a hidden root shell and files used by a scheduled task.

The wider OPSSHIELD breach matters to WordPress administrators because root-level access can expose the database credentials, configuration files and website data belonging to every hosting account on that machine.

How do you check if your cPGuard update was compromised?

Check the installed version

Connect using your hosting provider's trusted management console or a known-safe administrative session.

Run:

cpgcli --version
Enter fullscreen mode Exit fullscreen mode

OPSSHIELD instructs administrators to install version 5.88.25 or later. If your server is below that version, run the vendor's documented update command:

cpgcli update --start
Enter fullscreen mode Exit fullscreen mode

This changes the installed software. Preserve relevant evidence first, then verify the version again.

The two vendor notices describe the corrective releases differently. The advisory says 5.88.25 or later removes the known backdoor and staged files. The later disclosure names 5.88.26 as the clean update that removes the hidden root shell, with another update removing the scheduled task.

Treat 5.88.25 as the minimum, as the advisory directs. Install the latest available corrected release.

Check the installation history

Run:

grep 'Successfully updated' /opt/cpguard/logs/install_log
Enter fullscreen mode Exit fullscreen mode

Look for any entry showing version 5.88.12 through 5.88.19.

A matching entry establishes that the server installed an affected update. Treat it as potentially compromised even if the installed version is now 5.88.25 or later.

An installation entry alone does not prove script execution or data transfer. A missing entry is also inconclusive because logs may be incomplete or removed.

If you can establish that cPGuard was offline and did not check in during the affected window, the vendor says it did not receive the fake update. Otherwise, contact OPSSHIELD for help checking its incident records.

Check the files left by the attack

OPSSHIELD lists these customer-server indicators:

  • /etc/cron.d/i93677: the scheduled task.
  • /var/tmp/.i93677/: files associated with that task.
  • /var/tmp/.cpg91511_sh: a hidden setuid-root shell, which can run with its owner's privileges.
  • /var/tmp/.cpg91511/: a collection directory named in the earlier advisory, including /var/tmp/.cpg91511/loot.tgz.

The disclosure also names 94.183.188.91 as the attacker's main operator IP address. Check historical firewall, authentication and network logs for connections involving it. Compare matches with the surrounding activity.

Use this read-only check for the known paths and update history:

#!/usr/bin/env bash

for path in \
  /etc/cron.d/i93677 \
  /var/tmp/.i93677/ \
  /var/tmp/.cpg91511_sh \
  /var/tmp/.cpg91511/
do
  if [ -e "$path" ] || [ -L "$path" ]; then
    printf 'FOUND: %s\n' "$path"
  else
    printf 'NOT FOUND: %s\n' "$path"
  fi
done

log=/opt/cpguard/logs/install_log

if [ -r "$log" ]; then
  printf '\nSuccessful update entries:\n'
  grep 'Successfully updated' "$log" ||
    printf 'No matching entries in readable log\n'
else
  printf '\nInstall log missing or unreadable: %s\n' "$log"
fi
Enter fullscreen mode Exit fullscreen mode

The script reports path existence and prints successful update entries for review. It does not remove files or change configuration.

Missing indicators do not clear the server. Corrective updates remove known attack files, and an attacker may have deleted evidence.

If an indicator remains, preserve its details before removing it. OPSSHIELD directs administrators to delete known malicious files after updating, but removal should be deliberate. Have an incident responder handle evidence preservation if needed.

Which credentials must you rotate, and in what order?

Perform these steps from a trusted machine or recovery environment. Confirm replacement access before revoking old credentials, so you do not lock yourself out.

1. Change root and local account passwords

Reset the root password and passwords for all password-enabled local Linux accounts.

The attacker targeted /etc/shadow, which stores password hashes rather than plaintext passwords. Common or reused passwords may still be guessed offline.

Change reused passwords on other systems from a trusted device.

2. Replace root SSH keys

Treat private keys under /root/.ssh as exposed.

Generate replacements in a trusted environment and verify that the new keys work. Remove the old public keys from authorized_keys on every server that trusted them.

Review authorized keys for unfamiliar entries. Check backup hosts, staging machines and other production servers reachable with the old keys.

Replacing a private key does not revoke it until receiving servers stop trusting the corresponding public key.

3. Rotate MySQL credentials

Change the MySQL or MariaDB root password and any other database passwords stored in /root/.my.cnf.

Update backup jobs, management tools and scripts that depend on those credentials. Test their connections before retiring old access.

A stolen MySQL root password can expose every WordPress database that account can access. Treat those databases as potentially readable and writable.

4. Replace cloud credentials and shell-history secrets

Rotate keys stored in /root/.s3cfg and other cloud credentials found in the affected root account.

Review root shell history for passwords, deployment tokens and API keys typed directly into commands.

Revoke old credentials at their issuing services. Editing shell history does not invalidate a stolen token.

5. Secure the OPSSHIELD billing account

Reset the password for manage.opsshield.com and enable two-factor authentication.

Do this even if your server was offline during the attack window. The billing-portal account exposure was separate from the malicious updates.

What should you check on every WordPress site?

If the server installed an affected update, review every hosted WordPress installation.

With MySQL root access, an attacker may be able to read or change WordPress users, options, posts and other stored information. That exposure does not establish that a particular database was modified.

Change each site's database password

Identify the database account used by each WordPress installation.

Change its password on the database server, then update DB_PASSWORD in wp-config.php.

For a conventional configuration, the setting looks like this:

define( 'DB_PASSWORD', 'REPLACEMENT_SECRET' );
Enter fullscreen mode Exit fullscreen mode

REPLACEMENT_SECRET is an illustrative placeholder, not a password to use.

Keep the existing database name, username and host unless they also need changing. Use the hosting panel's password-change feature if it manages database accounts.

Test the website immediately afterward. A password mismatch can stop WordPress from connecting to its database. Also update scripts or deployment systems using that account.

Regenerate the WordPress salts

WordPress salts are secret values used in its authentication system. Replacing them helps invalidate existing login sessions.

From each WordPress installation directory, run:

wp config shuffle-salts
Enter fullscreen mode Exit fullscreen mode

This write operation modifies wp-config.php. The command is documented in the WP-CLI salt rotation reference.

Back up the configuration first and tell administrators they will need to sign in again. Changing salts does not change user passwords or database credentials.

Review WordPress administrator accounts

Run:

wp user list --role=administrator
Enter fullscreen mode Exit fullscreen mode

The WP-CLI user listing documentation confirms that --role filters users by role.

Compare the results with approved administrators. Investigate unfamiliar usernames, email addresses and registration dates before removing accounts.

For a wider review, follow the procedure for finding and removing hidden WordPress administrator accounts.

Review existing audit logs for account changes. A clean administrator list cannot rule out direct database changes.

Verify WordPress core and plugin files

Run these checks from each installation directory:

wp core verify-checksums
wp plugin verify-checksums --all
Enter fullscreen mode Exit fullscreen mode

The first command compares core files with official WordPress checksums. The second checks eligible plugins against WordPress.org checksums, as documented in the WP-CLI plugin checksum reference.

These commands do not replace files. Investigate unexpected mismatches.

Custom plugins, premium plugins and modified distributions may lack matching WordPress.org checksums. Compare those files with trusted releases or your source repository.

For the separate risk of malicious extension updates, see how WordPress plugin supply chain attacks work.

Reset SFTP, FTP and stored API credentials

Reset credentials that can upload or modify website files. Include hosting-panel users, SFTP and FTP accounts, deployment credentials and automation accounts.

Review secrets stored in wp-config.php and the WordPress options table, including payment, email, backup and external API credentials.

Revoke exposed keys at their issuing services, then update WordPress integrations. Use each plugin's supported settings screen rather than editing serialized database values manually.

Copyable server and WordPress recovery checklist

Use this table as a working record. Complete server recovery before putting replacement secrets into hosted WordPress installations.

Step Command or place What a bad result looks like What to do
1. Preserve evidence Hosting console, cPGuard logs Logs may be lost during cleanup Preserve available logs securely
2. Check version cpgcli --version Version below 5.88.25 Run cpgcli update --start, then verify
3. Check update history grep 'Successfully updated' /opt/cpguard/logs/install_log Version 5.88.12 through 5.88.19 Treat root secrets as exposed
4. Check scheduled task /etc/cron.d/i93677 Path exists Preserve evidence, then remove the malicious task
5. Check task files /var/tmp/.i93677/ Directory exists Preserve and remove known malicious files
6. Check hidden shell /var/tmp/.cpg91511_sh File exists Escalate, preserve evidence and remove through a trusted environment
7. Check collection files /var/tmp/.cpg91511/ Directory or loot.tgz exists Preserve and remove known collection files
8. Check attacker address Network and authentication logs, 94.183.188.91 Unexplained connection or login Investigate related activity
9. Reset local passwords Root and Linux users Old passwords still work Reset from trusted access
10. Replace SSH keys /root/.ssh, remote authorized_keys Old keys still authorize access Replace keys and revoke old trust
11. Rotate database credentials MySQL root, /root/.my.cnf Old credentials remain usable Rotate and update dependent tools
12. Rotate cloud keys /root/.s3cfg, cloud accounts Old keys remain active Revoke and replace them
13. Review shell secrets Root shell history Password or token appears Revoke the exposed secret
14. Secure billing login OPSSHIELD portal Old password or unprotected login Reset password and enable 2FA
15. Rotate site database users Database panel, wp-config.php Old WordPress DB password works Change database user password and DB_PASSWORD
16. Refresh WordPress salts wp config shuffle-salts Old authentication secrets remain Regenerate salts on each site
17. Review administrators wp user list --role=administrator Unrecognized administrator Investigate and revoke unauthorized access
18. Verify core wp core verify-checksums Unexpected mismatch Compare with a trusted release
19. Verify plugins wp plugin verify-checksums --all Unexpected mismatch or unverified code Check trusted plugin sources
20. Reset file access Hosting panel, SFTP and FTP users Old credentials remain active Rotate and revoke old access
21. Rotate application secrets wp-config.php, options table Exposed API key remains active Revoke and replace at the issuing service
22. Review login evidence /var/log/secure or /var/log/auth.log Unknown root login or sudo activity Escalate for incident response and rebuild assessment

Keep a separate completion record for each hosting account and WordPress site. Record the replacement date and responsible administrator without writing passwords into the worksheet.

When do you need to rebuild the server?

OPSSHIELD does not recommend a full operating-system rebuild as the routine response to the known malicious update.

Its November 3 disclosure recommends rebuilding from a clean backup taken before November 1 when the vendor has confirmed that the attacker manually logged in to your server.

Check available authentication logs, commonly /var/log/secure or /var/log/auth.log, for successful root logins, unexplained sudo sessions and administrative activity from unknown IP addresses.

A failed login attempt alone does not prove access. Compare successful logins with known administrator activity.

If manual attacker access is confirmed, additional compromise is discovered or you cannot establish server integrity, contact a qualified incident responder. A rebuild may be necessary.

Have the responder assess backup integrity and retained credentials before restoration. Preserve evidence before deleting files or rebuilding.

What remains unknown as of November 7, 2026?

The two OPSSHIELD notices leave several questions open.

The vendor traced the attack to its billing application but says its logs did not capture the exact request that allowed command execution. It patched two possible entry points.

The advisory identifies 5.88.12 through 5.88.19 as affected. It does not establish whether versions 5.88.20 through 5.88.24 contained the same malicious code.

The full scope of credential retrieval from individual servers remains uncertain. Published indicators may also be incomplete.

Neither notice reports confirmed changes to customer WordPress websites or databases. Undetected changes remain possible.

What should you do next?

Start with the installation log. If an affected update appears, preserve evidence, install the corrected release and rotate root-level credentials before updating WordPress secrets.

Work through each hosted site separately. Prioritize administrative access, database credentials, file-transfer accounts and integrations that can move data or money.

If you find unexplained file changes, unauthorized accounts or other signs of website compromise, use a qualified responder or WordPress malware removal support for application-level investigation. Suspected root-level compromise also requires an operating-system assessment.

Top comments (0)