cPGuard Update Channel Compromise (OPSSHIELD)

Malicious script pushed through the legitimate auto-update channel of a Linux server security product · credential files collected and uploaded to the vendor's licensing server · hidden backdoor shell left · no actor or malware name assigned by any source
Threat
cPGuard versions 5.88.12 through 5.88.19 (fixed in 5.88.25 or later)
Severity
HIGH
Type
Supply chain / credential theft / backdoor (Linux)
Access
Compromised credentials on the vendor update CDN, 2026-11-02 02:06 UTC; no vulnerability exploited
Scale
Vendor: 32 retrieved files (floor). BleepingComputer: 2,946 servers (single-sourced)
Version
v0.1 · 2026-11-04
Author
HuntPack
Confidence
High on mechanism and the four named artifacts (vendor-published); low on scale and upload details
01

Executive Summary

What happened. On 2 November 2026 at 02:06 UTC an unidentified attacker used compromised credentials to reach the update distribution infrastructure (CDN) of OPSSHIELD, the maker of cPGuard, a server security product for cPanel and Linux hosting servers. The attacker used that access to push a malicious script through the legitimate auto-update channel in cPGuard versions 5.88.12 through 5.88.19. OPSSHIELD says it detected the attack within 12 hours, secured the compromised account, cleared its infrastructure credentials and stopped further distribution. How the credentials were obtained is still under investigation.

What the script does. Per the vendor, it collected /etc/shadow and /etc/passwd, root's MariaDB/MySQL configuration (my.cnf), root's shell history and root's SSH authorization and known-hosts files, staged them in a hidden directory under /var/tmp, uploaded them to OPSSHIELD's own licensing server under a compromised account for the attacker to retrieve later, and left a backdoor shell. OPSSHIELD confirmed the attacker retrieved some of that data and estimates 32 collected files, each tied to one customer server. This is a preliminary count of confirmed retrieval, not the number of affected servers.

Scale, stated plainly. BleepingComputer reports that an OPSSHIELD spokesperson said 2,946 servers were affected and that a billing system database was also copied. Neither figure appears in the vendor advisory, The Hacker News or SecurityOnline, so both are single-sourced here and are not used by any query. The BleepingComputer snapshot is also a model-generated extraction, not a verbatim copy (see Section 2).

Correction to a common summary. Some coverage says the script took "root SSH keys". The vendor's list is root's SSH authorization and known-hosts files. Private keys under /root/.ssh appear only in the vendor's rotation advice ("replace SSH private keys stored under /root/.ssh"), as a precaution. Rotate them anyway, but do not report private-key theft as confirmed.

Highest-value defensive angle. The product is the delivery vehicle, so a clean version number today proves nothing: the vendor says a server that installed an affected update must be treated as potentially compromised even if it now runs 5.88.25 or later, because credentials copied before cleanup may still work. Run the install_log check, hunt the named hidden paths and the credential-sweep and upload behavior, then rotate credentials. The vendor does not recommend a routine rebuild; it may if you find other signs of compromise.

Defender priority: on every cPGuard server run grep 'Successfully updated' /opt/cpguard/logs/install_log; any entry from 5.88.12 to 5.88.19 means treat the host as compromised. Update with cpgcli update --start, confirm 5.88.25 or later with cpgcli --version, then reset root and every password-enabled local account from a trusted environment and rotate database passwords, S3 keys, DigitalOcean tokens, secrets in root's shell history and the SSH private keys under /root/.ssh.

02

Source Review & Web Hunter Notes

TierSourceKey FindingCarry Forward
1OPSSHIELD: cPGuard security advisory, update channel compromise (published 2026-11-03)Vendor timeline, affected range, collected data, install_log check, the four named artifacts, fix version, remediation, 32-file estimateyes
2The Hacker News (2026-11-03)Independent write-up of the advisory; adds that the support-access feature places an OPSSHIELD public key in root's authorized_keys and that exposure of the matching private key is not statedyes
2SecurityOnline (Daily CyberSecurity)Corroborates the date, versions, hidden folder under /var/tmp and backdoor path. Says "root SSH keys" loosely (see Section 1)partial
2BleepingComputer (2026-11-03)Only source for the 2,946-server figure and the billing database claim. Direct fetch was blocked (HTTP 403); the snapshot is a WebFetch extraction, not verbatimpartial

Decisions and conflicts

  • Artifacts. All four atomic artifacts (script name, backdoor shell, collection directory, loot archive) were verified by hand against the saved vendor snapshot and The Hacker News snapshot. The backdoor shell path is also in the SecurityOnline snapshot. The Hacker News derives its list from the vendor, so these are not independent sources: every artifact ships as detect, never prevent.
  • No hashes, IPs or malicious domains were published by any source. None are invented; the Falcon CSV carries labelled REPLACE_WITH_ placeholders.
  • Versions are not IOCs. 5.88.12 through 5.88.19 (affected) and 5.88.25 (fixed) live in prose and the version table only. Versions 5.88.20 to 5.88.24 are described by no source as either affected or fixed.
  • Time zone. The vendor's example install_log lines use a day-first date (02-11-2026 02:46:04) with no stated zone; do not assume UTC.
  • Unpublished: the upload hostname and method, the update process names, how the script is launched, whether the backdoor shell is a script or a binary, and whether the support-access private key was exposed.
  • Scale conflict. The vendor's "32 files" is retrieval from its licensing server and one extraction of the BleepingComputer text garbles it as "from each customer". Treat 32 as a confirmed floor, not a scope.
  • Injected instructions. No fetched page contained text directing an AI agent to act. Nothing was dropped on that ground.
  • Naming. No source assigns an actor, campaign or malware name. The pack uses only "cPGuard" and "OPSSHIELD".
03

Hunt Brief & Attack Chain

Affected and fixed versions (not IOCs)

ItemValueSource
Affected updatescPGuard 5.88.12 through 5.88.19, inclusiveOPSSHIELD advisory; The Hacker News
Fixed5.88.25 or later (removes the known backdoor and staged files)OPSSHIELD advisory
Unknown5.88.20 to 5.88.24: no source says affected or fixed. Update to 5.88.25 or laterGap
WindowAccess gained 2026-11-02 02:06 UTC; detected within 12 hoursOPSSHIELD advisory

Hunt hypotheses

#HypothesisQueryConfidence
H1The malicious script ran on the host: its filename appears on a command lineQ1High
H2The hidden backdoor shell or collection directory exists and was executed or writtenQ2, Q3High
H3Root credential files were swept by one parent processQ4Medium
H4The loot archive was built and uploadedQ5, Q6, Q7Medium
H5The host updated through the affected range (scoping)Q8 plus the Section 7 install_log checkMedium

Attack chain

StepBehaviorTelemetryHunt Angle
1Attacker uses compromised credentials on the vendor CDN and publishes the script in the update channelNone on your side: vendor infrastructureNot huntable; confirm affected versions with install_log
2Server's scheduled update check installs 5.88.12 to 5.88.19 and runs the scriptProcess creation with command line and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents); product log on diskQ1, Q8; install_log check
3Script reads shadow, passwd, root SSH files, my.cnf and shell historyProcess creation with command line (same as step 2). Shell-builtin reads leave no eventQ4
4Files staged in /var/tmp/.cpg91511 and archived to loot.tgzProcess creation with command line; executable or file written (CrowdStrike: ElfFileWritten for executables)Q2, Q3, Q5
5Archive uploaded to the OPSSHIELD licensing server under a compromised accountProcess creation with command line; DNS lookup with the requesting process (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents DnsQueryResponse); outbound network connectionQ6, Q7
6Hidden root backdoor shell left at /var/tmp/.cpg91511_shProcess creation; executable writtenQ2, Q3
7Attacker later retrieves the data from the licensing server and may use hashes and keysOutside endpoint telemetry (vendor server; later logons need sshd or authentication logs)Section 7 log checks; credential rotation
04

Consolidated IOC Table

TypeValueConfidenceActionContext
filenamecpguard-v58812-i91511.shhighdetectMalicious script (OPSSHIELD advisory; The Hacker News)
path/var/tmp/.cpg91511_shhighdetectBackdoor shell removed by the corrective update (advisory; The Hacker News; SecurityOnline)
path/var/tmp/.cpg91511highdetectCollection directory (advisory; The Hacker News)
path/var/tmp/.cpg91511/loot.tgzhighdetectArchive staged by the script (advisory; The Hacker News)
sha256REPLACE_WITH_SCRIPT_SHA256placeholderdetectNo hash published; fill from a preserved copy
sha256REPLACE_WITH_BACKDOOR_SHELL_SHA256placeholderdetectNo hash published; fill from a preserved copy
domainREPLACE_WITH_LICENSING_SERVER_DOMAINplaceholderdetectUpload point; hostname not published and legitimate for normal licensing
evidence file/opt/cpguard/logs/install_logn/aenrichLegitimate product log used for the exposure check; not an indicator

Four sourced indicators, three placeholders. cpgcli, /opt/cpguard/ and the cPGuard binaries are legitimate product files: they are context only and never ship as prevent. No IP addresses are published; the version numbers 5.88.12 to 5.88.19 and 5.88.25 are not indicators.

05

Affected Surface & Telemetry Matrix

SurfaceTelemetryPriorityGap
Linux hosting servers running cPGuard (cPanel and other Linux hosting)Process creation with command line, path and parent (CrowdStrike: ProcessRollup2, SyntheticProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)HighSensor must be on the server; shell builtin reads and in-process activity produce no event
Hidden executables under /var/tmpExecutable written (CrowdStrike: ElfFileWritten. SentinelOne: File Creation. Defender: DeviceFileEvents FileCreated)MediumNon-executable files such as loot.tgz and a script backdoor are not reported as executables
Upload to the licensing serverDNS lookup (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents DnsQueryResponse); outbound connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents)MediumHostname unpublished; destination is a legitimate vendor host
Update history and product logFile content on disk (/opt/cpguard/logs/install_log); no endpoint event carries itHighOutside endpoint telemetry: use the Section 7 shell check, RTR or a configuration tool
Downstream credential use (SSH, database, S3, DigitalOcean)Authentication logs, database logs, cloud audit logsHighOutside endpoint telemetry
06

ATT&CK Mapping

TacticTechniqueObserved BehaviorQuery / Control
Initial AccessT1195.002 Compromise Software Supply ChainMalicious script delivered through the vendor update channelQ1, Q8; Section 12 items 1, 5
ExecutionT1059.004 Unix ShellShell script and backdoor shellQ1, Q2
Defense EvasionT1564.001 Hidden Files and DirectoriesDot-prefixed /var/tmp/.cpg91511 and .cpg91511_shQ2, Q3
Credential AccessT1003.008 /etc/passwd and /etc/shadowPassword hashes collectedQ4; Section 12 items 2, 6
Credential AccessT1552.001 Credentials In FilesRoot my.cnf collectedQ4; Section 12 item 8
Credential AccessT1552.003 Shell HistoryRoot shell history collected (secrets recorded in it)Q4; Section 12 item 8
Credential AccessT1552.004 Private KeysRoot SSH authorization and known-hosts files collected; private keys at risk, theft not confirmedQ4; Section 12 item 2
CollectionT1074.001 Local Data Staging; T1560.001 Archive via Utilityloot.tgz built in the hidden directoryQ5
ExfiltrationT1041 Exfiltration Over C2 Channel (used loosely: upload to a vendor-controlled collection point)Archive uploaded to the licensing server under a compromised accountQ6, Q7; Section 12 item 3
07

Native Audit-Log Hunts

These are read-only checks for each cPGuard server. The vendor says a missing or incomplete log does not show a server was unaffected, and the corrective update removes the named files.

7.1 Update history (the vendor's own check)

grep 'Successfully updated' /opt/cpguard/logs/install_log
grep -E 'Successfully updated to 5\.88\.1[2-9]' /opt/cpguard/logs/install_log

Any entry from 5.88.12 to 5.88.19 means the server installed an affected update. The vendor's example lines show five entries on 02-11-2026 (day-first, zone unstated).

7.2 Installed version

cpgcli --version

Must report 5.88.25 or later. A fixed version does not clear a host that installed an affected one earlier.

7.3 Named artifacts

# Filesystem sweep (read-only)
ls -la /var/tmp/.cpg91511 /var/tmp/.cpg91511_sh 2>/dev/null
find /var/tmp /tmp /root /opt/cpguard -maxdepth 3 \( -name '.cpg*' -o -name 'cpguard-v*-i*.sh' \) 2>/dev/null
stat /etc/shadow
# Remember: the corrective update removes these files, so a clean result does not show the script never ran.

7.4 Logons since the compromise (credentials may have been used)

last -F root | head -50
grep -E 'Accepted (password|publickey)' /var/log/secure /var/log/auth.log 2>/dev/null | tail -200
cat /root/.ssh/authorized_keys

Look for logons you cannot explain since 2026-11-02 and for unknown keys. An OPSSHIELD support-access key is legitimate if support access was granted; OPSSHIELD is shipping an update to remove those keys.

7.5 Database and cloud audit

Review MariaDB/MySQL logs for root logins from unexpected sources, and review S3 and DigitalOcean audit logs since 2026-11-02 for the keys and tokens that were present on the server or recorded in root's shell history (the vendor names S3 access keys and DigitalOcean tokens).

08

Hunt Queries: CrowdStrike CQL, SentinelOne PowerQuery and Defender XDR KQL

Show all queries in
Pick your tenant's cloud first: every "Open in Falcon" button below uses this selection.
Q1 · cPGuard update script execution (exact name and name pattern)
CONF HIGHFP LOWCOST LOW

Looks for: Looks for any Linux process whose command line names the malicious script cpguard-v58812-i91511.sh. The regex also matches the same cpguard-v<digits>-i<digits>.sh shape, because the digits look like a version and an incident id; that generalisation is a hunting heuristic, not something a source states. FP: none known for the exact name; the vendor ships no legitimate script with this name.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: A script run by a process the agent did not observe starting is not captured.
  • PowerQuery assumes: endpoint.os holds the lowercase value linux.
  • PowerQuery assumes: The script name appears in tgt.process.cmdline when a shell interpreter runs it.
  • KQL – Defender XDR: A script run by a process the sensor did not observe starting is not captured.
  • KQL – Defender XDR assumes: DeviceProcessEvents has no operating system column, so no Linux filter is applied; the script name pattern is specific to the campaign.
  • KQL – Defender XDR assumes: The script name appears in ProcessCommandLine when a shell interpreter runs it.
// HUNT: cPGuard update channel malicious script execution
// MITRE: T1195.002, T1059.004
// CONF: high  FP: low  COST: low | REQUIRES: Linux process creation with command line (ProcessRollup2)
// SOURCE: script filename cpguard-v58812-i91511.sh (OPSSHIELD advisory; The Hacker News)
// NOTE: the pattern below also matches the same name shape with other digits (heuristic, unconfirmed)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| event_platform=Lin
| CommandLine=/cpguard-v\d+-i\d+\.sh/i
| table([@timestamp, ComputerName, aid, UserName, FileName, CommandLine, ParentBaseFileName, ImageFileName, SHA256HashData])
Q2 · Hidden backdoor shell and collection directory execution under /var/tmp
CONF HIGHFP LOWCOST LOW

Looks for: Looks for execution of the hidden backdoor shell /var/tmp/.cpg91511_sh or anything run from, or pointed at, the hidden collection directory /var/tmp/.cpg91511. The basename match uses FileName with no path slash; the command-line branch escapes the slash. The digits are generalised to \d+ (heuristic). FP: none known.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Files deleted by the corrective update leave no process event to find after the fact.
  • PowerQuery assumes: endpoint.os holds the lowercase value linux.
  • PowerQuery assumes: tgt.process.name holds the basename of the executed file.
  • KQL – Defender XDR: Files deleted by the corrective update leave no process event to find after the fact.
  • KQL – Defender XDR assumes: DeviceProcessEvents has no operating system column, so no Linux filter is applied; the dot-prefixed path pattern is specific to the campaign.
  • KQL – Defender XDR assumes: FileName holds the basename of the executed file.
// HUNT: cPGuard hidden backdoor shell or collection directory in use
// MITRE: T1564.001, T1059.004
// CONF: high  FP: low  COST: low | REQUIRES: Linux process creation with command line (ProcessRollup2)
// SOURCE: /var/tmp/.cpg91511_sh and /var/tmp/.cpg91511 (OPSSHIELD advisory; The Hacker News; SecurityOnline for the shell)
// NOTE: absence proves nothing, the corrective update deletes these files
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| event_platform=Lin
| FileName=/^\.cpg\d+_sh$/i or CommandLine=/\/var\/tmp\/\.cpg\d+/i
| table([@timestamp, ComputerName, aid, UserName, FileName, CommandLine, ParentBaseFileName, ImageFileName, SHA256HashData])
Q3 · Hidden executable written under /var/tmp (ElfFileWritten)
CONF MEDIUMFP MEDIUMCOST LOW

Looks for: Looks for Linux executables written to the hidden /var/tmp/.cpg<digits> paths named by the vendor, plus any hidden dotfile executable written directly under /var/tmp. The backdoor shell may be a copied shell binary (not stated by any source), which is why this watches executable writes. FP: the second branch is generic and can match installers that unpack hidden helpers into /var/tmp; the cPGuard-specific first branch should not.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: approximate-analyticProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: No ELF-content filter exists, so hidden non-executable files also match.
  • PowerQuery: File modifications of an existing hidden file are not covered; only creations.
  • PowerQuery assumes: endpoint.os holds the lowercase value linux.
  • PowerQuery assumes: The CQL second branch is case-sensitive; matches here is case-insensitive, which only widens it slightly.
  • KQL – Defender XDR: No ELF-content filter exists, so hidden non-executable files also match.
  • KQL – Defender XDR: Modifications of an existing hidden file are not covered; only creations.
  • KQL – Defender XDR assumes: FolderPath holds the full path of the written file on Linux sensors.
  • KQL – Defender XDR assumes: FileCreated is the action recorded for a new file.
// HUNT: Hidden ELF written under /var/tmp (cPGuard backdoor shell path and generic dotfile executables)
// MITRE: T1564.001, T1105
// CONF: medium  FP: medium  COST: low | REQUIRES: Linux executable-write telemetry (ElfFileWritten)
// FALSE POSITIVES: installers and package scripts that stage hidden helper binaries in /var/tmp
// TUNING: after a 30 day baseline, drop the generic second branch for known installers by adding
//         | ContextBaseFileName != /^(rpm|dpkg|yum|dnf|apt|apt-get|cpanel_pkg)$/i  (confirm the field on your sensor)
// NOTE: the exact backdoor path is /var/tmp/.cpg91511_sh per the OPSSHIELD advisory
#event_simpleName=ElfFileWritten
| event_platform=Lin
| TargetFileName=/^\/var\/tmp\/\.cpg\d+/i or TargetFileName=/^\/var\/tmp\/\.[^\/]+$/
| table([@timestamp, ComputerName, aid, TargetFileName, SHA256HashData, ContextProcessId])
Q4 · Credential-file harvesting commands from one parent (shadow, root SSH, my.cnf, history)
CONF MEDIUMFP MEDIUMCOST MEDIUM

Looks for: Counts processes under the same parent that name two or more of the files the vendor says the script collected: /etc/shadow, /etc/passwd, root's SSH authorization and known-hosts files, root's my.cnf and root's shell history. A script doing the harvest produces several sibling commands under one parent, which is the signal. File reads by shell builtins produce no process event, so a quiet result is not clearance. FP: backup agents, configuration management and compliance scanners read the same files.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: Reads done inside the shell with builtins and redirects create no process and are not counted.
  • PowerQuery: any() returns one sample per group, not the full list the CQL collects.
  • PowerQuery assumes: endpoint.os holds the lowercase value linux.
  • PowerQuery assumes: src.process.pid identifies the creator within one endpoint and short window; PID reuse over a long window can merge two creators.
  • KQL – Defender XDR: Reads done inside the shell with builtins and redirects create no process and are not counted.
  • KQL – Defender XDR assumes: DeviceProcessEvents has no operating system column, so no Linux filter is applied; the credential paths are Linux-specific.
  • KQL – Defender XDR assumes: InitiatingProcessId identifies the creator within one device and short window; PID reuse over a long window can merge two creators.
// HUNT: Multiple root credential-file reads from one parent process (cPGuard script collection step)
// MITRE: T1003.008, T1552.001, T1552.003, T1552.004
// CONF: medium  FP: medium  COST: medium | REQUIRES: Linux process creation with command line (ProcessRollup2)
// FALSE POSITIVES: backup agents, configuration management, compliance and audit scanners
// TUNING: exclude known tooling after baselining, e.g. add before groupBy:
//         | ParentBaseFileName != /^(ansible-playbook|puppet|chef-client|salt-minion|rsnapshot|borg)$/i
// NOTE: reads done inside the shell with builtins and redirects do not create a process, so this under-counts
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| event_platform=Lin
| CommandLine=/(\/etc\/shadow|\/etc\/passwd|\/root\/\.ssh\/(authorized_keys|known_hosts)|\/root\/\.my\.cnf|\/root\/\.bash_history)/i
| groupBy([aid, ComputerName, ParentProcessId], function=[count(as=credCmds), min(@timestamp, as=firstSeen), max(@timestamp, as=lastSeen), collect([FileName, CommandLine, ParentBaseFileName], limit=20)])
| credCmds >= 2
Q5 · Loot archive staging (tar, gzip, zip against the hidden directory or loot.tgz)
CONF HIGHFP LOWCOST LOW

Looks for: Looks for archive utilities building or touching /var/tmp/.cpg91511/loot.tgz or any /var/tmp/.cpg<digits> directory. FP: none known.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: An archive built inside a script interpreter library without launching a tool is not captured.
  • PowerQuery assumes: endpoint.os holds the lowercase value linux.
  • PowerQuery assumes: tgt.process.name holds the basename of the executed archive tool.
  • KQL – Defender XDR: An archive built inside a script interpreter library without launching a tool is not captured.
  • KQL – Defender XDR assumes: DeviceProcessEvents has no operating system column, so no Linux filter is applied; the archive path is Linux-specific.
  • KQL – Defender XDR assumes: FileName holds the basename of the executed archive tool.
// HUNT: cPGuard credential loot archive staged in the hidden directory
// MITRE: T1074.001, T1560.001
// CONF: high  FP: low  COST: low | REQUIRES: Linux process creation with command line (ProcessRollup2)
// SOURCE: /var/tmp/.cpg91511/loot.tgz (OPSSHIELD advisory; The Hacker News)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| event_platform=Lin
| FileName=/^(tar|gzip|pigz|zip|7z|7za|bsdtar|cpio)$/i
| CommandLine=/(loot\.tgz|\/var\/tmp\/\.cpg\d+)/i
| table([@timestamp, ComputerName, aid, UserName, FileName, CommandLine, ParentBaseFileName, ImageFileName])
Q6 · Transfer tool referencing the loot archive or hidden directory (upload)
CONF MEDIUMFP LOWCOST LOW

Looks for: Looks for curl, wget, scripting runtimes or copy tools whose command line references loot.tgz or the hidden /var/tmp/.cpg<digits> directory, which is how the archive would reach the licensing server. The upload method and destination host are not published, so this tests the command line only; if the script uses a runtime library with the path built at run time, the string will not appear. FP: none known.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: An upload made from inside an already running interpreter, with the archive named only in a script file, is not captured.
  • PowerQuery assumes: endpoint.os holds the lowercase value linux.
  • PowerQuery assumes: tgt.process.name holds the basename of the executed transfer tool.
  • KQL – Defender XDR: An upload made from inside an already running interpreter, with the archive named only in a script file, is not captured.
  • KQL – Defender XDR assumes: DeviceProcessEvents has no operating system column, so no Linux filter is applied; the archive path is Linux-specific.
  • KQL – Defender XDR assumes: FileName holds the basename of the executed transfer tool.
// HUNT: Transfer tool referencing the cPGuard loot archive (upload to the licensing server)
// MITRE: T1041, T1105
// CONF: medium  FP: low  COST: low | REQUIRES: Linux process creation with command line (ProcessRollup2)
// NOTE: upload method and destination host are not published by any source
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| event_platform=Lin
| FileName=/^(curl|wget|php|perl|python[0-9.]*|ruby|scp|sftp|rsync|ftp|nc|ncat)$/i
| CommandLine=/(loot\.tgz|\/var\/tmp\/\.cpg\d+)/i
| table([@timestamp, ComputerName, aid, UserName, FileName, CommandLine, ParentBaseFileName, ImageFileName])
Q7 · DNS lookups for the licensing server (placeholder, inactive until filled)
CONF LOWFP LOWCOST LOW

Looks for: Looks for DNS requests for the OPSSHIELD licensing server hostname the script uploaded to. No source publishes that hostname, so the pattern is the labelled placeholder REPLACE_WITH_LICENSING_SERVER_DOMAIN and the query matches nothing until you replace it with the hostname from your own cPGuard configuration or egress logs. Expect legitimate traffic to the same host from every cPGuard server, so the useful signal is volume and timing around 2026-11-02, not presence. FP: every healthy cPGuard server queries its licensing host.

Validation: static-reviewedEquivalence: unsupported-without-redesignProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: The licensing server hostname is unpublished, so no SentinelOne lookup can be written for it.
  • KQL – Defender XDR: The licensing server hostname is unpublished, so no Defender lookup can be written for it.
// HUNT: DNS lookups for the cPGuard licensing server (REPLACE_WITH_LICENSING_SERVER_DOMAIN)
// MITRE: T1041
// CONF: low  FP: medium  COST: low | REQUIRES: DNS lookup telemetry (DnsRequest)
// FALSE POSITIVES: the legitimate licensing check that every cPGuard server performs
// TUNING: set the search window to 2026-11-02 00:00 UTC onward and compare against the prior 30 days per host;
//         the signal is a burst of lookups near the update times in install_log, not the lookup itself
// NOTE: replace the placeholder with the hostname from your own configuration; no source publishes it
#event_simpleName=DnsRequest
| event_platform=Lin
| DomainName=/REPLACE_WITH_LICENSING_SERVER_DOMAIN/i
| groupBy([aid, ComputerName], function=[count(as=lookups), min(@timestamp, as=firstSeen), max(@timestamp, as=lastSeen)])
| sort(lookups, order=desc, limit=200)
Q8 · cPGuard update activity inventory since 2026-11-02 (exposure scoping)
CONF LOWFP HIGHCOST LOW

Looks for: Lists hosts where a process with a cPGuard updater command line ran since the compromise started, to scope which servers to check with the install_log test in Section 7. It does not show the installed version and it does not show that the malicious script ran; the vendor's install_log is the authority. Set the search window to start at 2026-11-02 02:00 UTC. FP: every normal cPGuard update, so treat it as a scoping list, not a detection.

Validation: schema-aligned, static-reviewedAutomated: static-passedEquivalence: equivalent-with-schema-assumptionsProfiles: microsoft-defender-xdr, sentinelone-endpoint-ocsf
Known limitations and deployment notes
  • PowerQuery: any() returns one sample command line per host, not the ten the CQL collects.
  • PowerQuery: Every legitimate cPGuard update and version check also matches.
  • PowerQuery assumes: endpoint.os holds the lowercase value linux.
  • PowerQuery assumes: Updater process names are not published, so the match is on the command line only.
  • KQL – Defender XDR: Every legitimate cPGuard update and version check also matches.
  • KQL – Defender XDR assumes: DeviceProcessEvents has no operating system column, so no Linux filter is applied; the cPGuard paths are Linux-specific.
  • KQL – Defender XDR assumes: Updater process names are not published, so the match is on the command line only.
// HUNT: cPGuard update activity per host since 2026-11-02 (scoping list, not a detection)
// MITRE: T1195.002
// CONF: low  FP: high  COST: low | REQUIRES: Linux process creation with command line (ProcessRollup2)
// FALSE POSITIVES: all legitimate cPGuard updates and version checks
// TUNING: this is a scoping list; confirm each host with the install_log check in Section 7 before acting
// NOTE: set the search window to start 2026-11-02 02:00 UTC; updater process names are not published, so match on command line
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| event_platform=Lin
| CommandLine=/(cpgcli|\/opt\/cpguard\/)/i
| CommandLine=/update/i
| groupBy([aid, ComputerName], function=[count(as=updateRuns), min(@timestamp, as=firstSeen), max(@timestamp, as=lastSeen), collect([CommandLine], limit=10)])
| sort(updateRuns, order=desc, limit=500)
09

Detection Rules by Platform

Three queries are exact enough to promote. All ship as detect only. The names come from a vendor advisory, but the hidden-path and script checks are cheap and low-noise. cpgcli, /opt/cpguard/ and the cPGuard binaries are legitimate product files and no IOA blocks or kills them.

CrowdStrike: Custom IOA

IOA namePromotesPatternExclusionsDeployment Path
DETECT-cPGuard-UpdateScriptExecutionPromote Q1Linux process creation, command line matching cpguard-v[0-9]+-i[0-9]+\.shNone knownCustom IOA (process creation), severity High, action Detect only
DETECT-cPGuard-HiddenBackdoorShellPromote Q2Linux process creation, image name ^\.cpg[0-9]+_sh$ or command line matching /var/tmp/\.cpg[0-9]+None knownCustom IOA (process creation), severity High, action Detect only
DETECT-cPGuard-LootArchiveStagingPromote Q5Linux process creation, image name tar, gzip or zip, command line matching loot\.tgz or /var/tmp/\.cpg[0-9]+None knownCustom IOA (process creation), severity High, action Detect only

Do not promote Q3, Q4, Q6, Q7 or Q8. Q3 and Q4 fire on installers, backup and audit tools; Q6 depends on an unpublished upload method; Q7 is a placeholder; Q8 is a scoping list.

SentinelOne: custom detection rules

RuleSource queryRun asNotes
DETECT-cPGuard-UpdateScriptExecutionQ1, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"
DETECT-cPGuard-HiddenBackdoorShellQ2, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"
DETECT-cPGuard-LootArchiveStagingQ5, PowerQuery tabScheduled custom detection ruleBind entities so the alert names the host instead of "Unknown Device"

Create the rule with POST /web/api/v2.1/cloud-detection/rules, queryType: "scheduled" and queryLang: "2.0", with the query in data.scheduledParams.query. Use 2.0 operator spellings: a rule written with the 1.0 token ContainsCIS was accepted and never fired on a tenant. The API does not warn, so lint the body before deploying.

Microsoft Defender: custom detection rules

RuleSource queryRun asNotes
DETECT-cPGuard-UpdateScriptExecutionQ1, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself
DETECT-cPGuard-HiddenBackdoorShellQ2, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself
DETECT-cPGuard-LootArchiveStagingQ5, KQL – Defender XDR tabCustom detection rule, alert only, every hourThe query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself

Q3, Q4, Q6, Q7 and Q8 stay a hunt in this pack, not a rule. A rule can generate only 150 alerts per run. Continuous (NRT) frequency needs a single table and no comment lines, so delete the generated comment header from the query first.

10

Machine-Readable IOC Appendix

Grouped quick-copy blocks. The Falcon CSV ships three placeholders and no importable atomic rows: no source publishes a hash, IP address or malicious domain, so none is invented. Replace each REPLACE_WITH_ value from your own preserved evidence before import, or delete the row. The four filesystem artifacts are not a Falcon IOC type, so they live in their own block and are hunted by Q1, Q2, Q3 and Q5. Nothing here is a prevent action: the artifacts are single-lineage (the vendor and its re-publisher) and cPGuard files are legitimate product files.

Falcon IOC Management CSVbulk import (placeholders)
type,value,action,severity,expiration,description,tags
sha256,REPLACE_WITH_SCRIPT_SHA256,detect,high,2027-05-04,cpguard-v58812-i91511.sh (no hash published by any source; fill from your own preserved copy),campaign:cPGuard-OpsShield-SupplyChain
sha256,REPLACE_WITH_BACKDOOR_SHELL_SHA256,detect,high,2027-05-04,/var/tmp/.cpg91511_sh (no hash published by any source; fill from your own preserved copy),campaign:cPGuard-OpsShield-SupplyChain
domain,REPLACE_WITH_LICENSING_SERVER_DOMAIN,detect,medium,2027-05-04,OPSSHIELD licensing server used as the upload point (hostname not published; legitimate for normal licensing),campaign:cPGuard-OpsShield-SupplyChain
SentinelOne IOC importnot included

This pack does not ship a SentinelOne import block: the import format could not be verified against SentinelOne's own documentation. Export a sample from your console and the block can be added with confidence.

Filesystem Artifactsnamed by OPSSHIELD
# Filesystem artifacts named by OPSSHIELD (all detect; the corrective update deletes them, so absence proves nothing)
cpguard-v58812-i91511.sh                 malicious script filename
/var/tmp/.cpg91511_sh                    backdoor shell
/var/tmp/.cpg91511                       collection directory
/var/tmp/.cpg91511/loot.tgz              archive staged by the script

# Evidence file, NOT an indicator (legitimate product log used for the exposure check)
/opt/cpguard/logs/install_log
Behavioral Signatureswhat to hunt on
# Behavioral signatures (the combination is the signal; each tool alone is legitimate)
Process lineage        : a non-interactive shell started by the cPGuard update path running cpguard-v<digits>-i<digits>.sh
Hidden paths           : /var/tmp/.cpg<digits>, /var/tmp/.cpg<digits>_sh (dot prefix, cpg prefix, 5 digits in the reported case)
Credential sweep       : several commands in a row naming /etc/shadow, /etc/passwd, /root/.ssh/authorized_keys,
                         /root/.ssh/known_hosts, /root/.my.cnf, /root/.bash_history under one parent process
Staging                : tar or gzip building /var/tmp/.cpg91511/loot.tgz
Upload                 : transfer to the OPSSHIELD licensing server under a compromised account (host not published)
Install log evidence   : lines 'Successfully updated to 5.88.12' through '5.88.19' in /opt/cpguard/logs/install_log
Not malicious (do not alert alone):
  - cpgcli, /opt/cpguard/ and the cPGuard binaries (legitimate product files, detect context only)
  - OPSSHIELD support-access public key in /root/.ssh/authorized_keys (added by the product's support-access feature)
Exposure and Fix Auditversion and log check
# Exposure and fix audit (run as root on each cPGuard server; read-only)
grep 'Successfully updated' /opt/cpguard/logs/install_log
cpgcli --version
# Affected updates are 5.88.12 through 5.88.19 inclusive. Fixed: 5.88.25 or later.
# A missing or incomplete log does not prove the server was unaffected (OPSSHIELD).
11

Detection Validation Gates

GateQ1 (script)Q2 (hidden shell)Q3 (ELF write)Q4 (cred sweep)Q5 (archive)Q6 (upload)Q7 (DNS)Q8 (inventory)
Telemetry readyReady process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)Ready process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)Pending executable written (CrowdStrike: ElfFileWritten. SentinelOne: File Creation. Defender: DeviceFileEvents); confirm the event is licensed and populated on LinuxReady process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)Ready process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)Ready process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)Pending DNS lookup (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents DnsQueryResponse); needs the real hostnameReady process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents)
Benign baseline30 days: expect none30 days: expect none30 days: list hidden executables under /var/tmp; expect installers only30 days: list parents; expect backup and audit tooling, exclude by name30 days: expect none30 days: expect nonePer host, prior 30 days of lookups to the licensing host for comparisonCompare with install_log results per host
Positive-control testNot yet run. On a lab host run sh -c 'echo test cpguard-v1-i1.sh' and confirm the process eventNot yet run. On a lab host run sh -c 'echo test /var/tmp/.cpg1'Not yet run. In a lab, copy a harmless binary to /var/tmp/.cpg1_sh and confirm the write event, then delete itNot yet run. In a lab run cat /etc/passwd and cat /root/.bash_history in one script and confirm credCmds is 2Not yet run. In a lab run tar -czf /tmp/loot.tgz /etc/hostnameNot yet run. In a lab run curl -s -o /dev/null --max-time 1 http://192.0.2.1/ -d loot.tgz (documentation address)Not yet run. Replace the placeholder with a lab hostname and resolve itNot yet run. Run cpgcli --version on a lab host with cPGuard installed
PromotionIOA candidate (Section 9)IOA candidate (Section 9)Hunt onlyHunt onlyIOA candidate (Section 9)Hunt onlyHunt onlyHunt only

Run every positive control in a lab, never on production. A zero result from Q1 to Q8 does not clear a host: the vendor's cleanup deletes the named files, shell builtins create no process event, and the decisive check is the install_log test in Section 7. No query has been run against a tenant yet.

12

Hardening: Tiered

Immediate (this week)
  1. Update and verify cPGuard. Run cpgcli update --start on every server, then cpgcli --version and confirm 5.88.25 or later (OPSSHIELD advisory; MITRE M1051 Update Software). Verify: cpgcli --version. Note that updating does not make a host that ran the script safe.
  2. Rotate every exposed credential from a trusted environment. Reset root and all password-enabled local accounts, change reused passwords elsewhere, rotate database passwords, S3 keys, DigitalOcean tokens and secrets found in root's shell history, replace the SSH private keys under /root/.ssh and remove the old public keys from every system that trusts them (OPSSHIELD advisory; M1027 Password Policies, M1026 Privileged Account Management). Verify: passwd -S root, key fingerprints, and cloud provider key-age reports.
  3. Restrict outbound traffic from hosting servers. Default-deny egress with an allowlist so a root process cannot upload a loot archive to an arbitrary host (M1037 Filter Network Traffic; CIS Linux Benchmark host firewall section). Caveat: a vendor licensing host that is already allowed (as here) is not blocked by this, so it limits unknown destinations only. Verify: nft list ruleset or iptables -S.
  4. Review root's authorized_keys. Remove unknown keys; remove the OPSSHIELD support-access key unless you actively use support access (OPSSHIELD is shipping a removal update). Verify: cat /root/.ssh/authorized_keys.
Near term (1 to 4 weeks, pilot first)
  1. Stage security-tool updates. Run updates on a canary group and hold the rest for a defined period before they apply, where the product allows deferral. ⚠ best-practice, no formal benchmark; no source says cPGuard exposes a deferral setting, so confirm in OPSSHIELD documentation. Reduces blast radius of any future poisoned update; the 12 hour detection window here is the span a hold would have to cover.
  2. Audit access to credential files. auditd watches on /etc/shadow, /root/.ssh, /root/.my.cnf and root history, plus exec watches under /var/tmp (M1047 Audit; CIS Linux Benchmark auditd section). Gives a file-read record that endpoint telemetry lacks. Playbook 3. Verify: auditctl -l.
  3. Mount /var/tmp noexec,nosuid,nodev. Stops direct execution of a binary dropped there (CIS Linux Benchmark separate /var/tmp partition and mount options; M1022). Limit: a script run as sh /var/tmp/x is not stopped, and some cPanel components expect to execute from temp directories, so pilot. Playbook 4. Verify: findmnt /var/tmp.
  4. Stop leaving secrets where a script can read them. Keep passwords out of shell history (HISTCONTROL=ignorespace, no secrets on command lines) and use scoped, short-lived cloud credentials instead of long-lived keys in files (M1027; T1552.003). Verify: search history for key-shaped strings after rotation. ⚠ best-practice, no formal benchmark for the history control.
Strategic (1 to 3 months)
  1. Treat root-level security agents as supply-chain risk. Inventory every product that auto-updates as root, require vendor release signing or checksum verification where offered, and track vendor incident notices (M1016 Vulnerability Scanning for the inventory process). ⚠ best-practice, no formal benchmark.
  2. Remove standing secrets from servers. Move to a secrets manager and short-lived credentials so a one-time file sweep yields nothing durable (M1026). Verify with a secret-scan of home directories.
  3. Forward logs off host. Send auth, audit and product logs to central storage so evidence like install_log and logons survive cleanup (M1047). The vendor warns a missing log does not clear a host.
13

Deployable Playbooks

Playbook 1. Triage and preserve evidence (read-only apart from the copy)

Prerequisites: Root shell on the server; free space on a trusted off-host or separate volume
Reboot required: No
Rollback: none required: read-only; delete the evidence folder when the case closes

# 1. Record version and update history
cpgcli --version
grep 'Successfully updated' /opt/cpguard/logs/install_log

# 2. Preserve the named artifacts BEFORE the corrective update removes them
mkdir -p /root/cpguard-evidence-20261104
cp -a /opt/cpguard/logs/install_log /root/cpguard-evidence-20261104/ 2>/dev/null
cp -a /var/tmp/.cpg91511 /root/cpguard-evidence-20261104/ 2>/dev/null
cp -a /var/tmp/.cpg91511_sh /root/cpguard-evidence-20261104/ 2>/dev/null
sha256sum /root/cpguard-evidence-20261104/.cpg91511_sh /root/cpguard-evidence-20261104/.cpg91511/loot.tgz 2>/dev/null
# Copy the folder off the host. Do NOT execute the backdoor shell.

Playbook 2. Update cPGuard and verify

Prerequisites: Playbook 1 evidence copied off host; maintenance window agreed
Reboot required: No
Rollback: none required: do not downgrade. A failed update means contact OPSSHIELD Support; never roll back to an affected version

cpgcli update --start
cpgcli --version
# Expect 5.88.25 or later.

Playbook 3. auditd watches for credential files and /var/tmp execution

Prerequisites: auditd installed and running; rules directory writable
Reboot required: No (unless audit is set immutable with -e 2, which then needs a reboot to change)
Rollback: Delete the rules file and reload: rm /etc/audit/rules.d/99-credfiles.rules && augenrules --load

cat > /etc/audit/rules.d/99-credfiles.rules <<'EOF'
-w /etc/shadow -p r -k cred_read
-w /root/.ssh -p rwa -k root_ssh
-w /root/.my.cnf -p r -k root_mycnf
-w /root/.bash_history -p r -k root_hist
-a always,exit -F arch=b64 -S execve -F dir=/var/tmp -k vartmp_exec
EOF
augenrules --load
auditctl -l | grep -E 'cred_read|root_ssh|vartmp_exec'
# Note: the execve rule's dir= filter matches exec of files in that directory only on kernels that support it; verify with a test.

Playbook 4. Mount /var/tmp noexec,nosuid,nodev

Prerequisites: Pilot on a non-production host; confirm cPanel components and backups that execute from /var/tmp; fstab backed up
Reboot required: No for the remount; the fstab entry applies at next boot
Rollback: Restore the backup: cp /etc/fstab.pre-vartmp /etc/fstab && mount -o remount,exec,suid,dev /var/tmp

cp /etc/fstab /etc/fstab.pre-vartmp
findmnt /var/tmp
# If /var/tmp is its own mount, add noexec,nosuid,nodev to its options in /etc/fstab, then:
mount -o remount,noexec,nosuid,nodev /var/tmp
findmnt /var/tmp
# If /var/tmp is not a separate mount, a bind mount of a dedicated directory is required; test before production.

Playbook 5. Credential rotation checklist (run from a trusted device or recovery environment)

Prerequisites: Out-of-band console access; a plan for dependent services; cloud console access
Reboot required: No
Rollback: Lockout recovery only: use console access to reset the password again. Do not restore old credentials, they are compromised

# Per OPSSHIELD: from a trusted environment
passwd root
# repeat for every password-enabled local account (list them):
awk -F: '($2 !~ /^[!*]/) {print $1}' /etc/shadow
# SSH: generate replacement keys on a trusted machine, deploy the new public key, then remove the old key lines
ssh-keygen -t ed25519 -f ./replacement_key
# Rotate: MariaDB/MySQL passwords (and sync cPanel's stored copy through its supported tool),
#         S3 access keys, DigitalOcean tokens, and any secret seen in root's shell history.
14

Containment Runbook

PhaseActionsOwnerEvidence
IsolateScope first: servers whose install_log shows an update from 5.88.12 to 5.88.19, plus servers with a missing or incomplete log. Isolate any server with the named hidden paths, a credential-sweep hit (Q4) or an upload hit (Q6). A server that only installed the update and shows none of these can usually stay online while you update and rotate credentials.SOCHost list with install_log output and isolation timestamps
PreservePlaybook 1: keep install_log, the hidden directory, loot.tgz and the backdoor shell with hashes, copied off host. Do not run the backdoor.IREvidence folder hashes
EradicatePlaybook 2: update to 5.88.25 or later (the vendor says this removes the known backdoor and staged files). Confirm the named paths are gone. The vendor does not recommend a routine rebuild; rebuild if you find other signs of compromise or cannot establish integrity, and ask OPSSHIELD Support for a case assessment.Endpoint engineeringcpgcli --version output; find results
Scope credentialsAssume the harvested set is exposed: shadow hashes, root SSH files, my.cnf secrets and anything in shell history. Review logons, database and cloud audit logs since 2026-11-02 (Section 7.4 and 7.5). Hashes are cracked offline, so weak passwords are the highest risk.IR with identity and cloud teamsCredential inventory; audit extracts
RecoverPlaybook 5: rotate from a trusted environment, replace SSH keys, revoke sessions and tokens. Confirm no unexplained logons after rotation.Identity / Endpoint engineeringRotation records
Prevent recurrenceSection 12 items 3, 5, 6, 7 and 11; add Q1, Q2 and Q5 as detect-only IOAs after the Section 11 gates pass.Security engineeringControl verification output
15

Detection Coverage Map

TechniqueBehaviorCQLIOACrowdStrike coverageSentinelOneDefender XDR
T1195.002Malicious script delivered via the vendor update channelQ1, Q8Q1Partial (script run seen; the delivery and installed version are outside endpoint telemetry, use install_log)Partial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1059.004Shell script and backdoor shell executionQ1, Q2Q1, Q2Good (if the sensor is present and the script runs as a new process)Good, with assumptionsGood, with assumptions
T1564.001Hidden dotfile paths under /var/tmpQ2, Q3Q2Partial (executables and command lines; a non-executable backdoor file write is not reported)Partial: Q3 approximatePartial: Q3 approximate
T1003.008/etc/shadow and /etc/passwd collectedQ4n/aPartial (command-line sweep only; builtin reads are invisible)Partial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1552.001, T1552.003Root my.cnf and shell history collectedQ4n/aPartial (same limit as above; add the Playbook 3 audit rules)Partial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1552.004Root SSH authorization and known-hosts files collectedQ4n/aPartial (same limit as above)Partial: same limit as CrowdStrikePartial: same limit as CrowdStrike
T1074.001, T1560.001Staging and archive in the hidden directoryQ5Q5Good (if archived with a utility that appears on the command line)Good, with assumptionsGood, with assumptions
T1041Upload to the licensing serverQ6, Q7n/aGAP (method and host unpublished; Q6 tests the command line only and Q7 is an unfilled placeholder)GAP: same limit as CrowdStrikeGAP: same limit as CrowdStrike
T1078Later use of harvested credentialsnonen/aGAP (outside endpoint telemetry: authentication, database and cloud audit logs)GAPGAP

Honest gaps. The vendor deletes the named files during cleanup, the upload details are not published, and file reads inside a shell leave no process event. A zero result is not clearance: run the Section 7 install_log check on every cPGuard server. Validation gates are in Section 11; no positive control has been run yet.

16

Hunt Summary Ticket

TITLE:      Hunt for the cPGuard update channel compromise (exposure, not confirmed compromise per host)
SEVERITY:   High (vendor-pushed script reached servers via auto-update; credential files collected; backdoor shell left;
            vendor estimates 32 retrieved files, BleepingComputer reports 2,946 servers affected, single-sourced)
SCOPE:      Linux servers running cPGuard that installed an update from 5.88.12 through 5.88.19 on or after
            2026-11-02 02:06 UTC (fixed in 5.88.25 or later)
HYPOTHESIS: The update ran cpguard-v58812-i91511.sh, collected root credential files into /var/tmp/.cpg91511,
            uploaded loot.tgz to the licensing server and left /var/tmp/.cpg91511_sh.
QUERIES:    Q1-Q8 (Section 8); native checks (Section 7)
DO FIRST:   1) grep 'Successfully updated' /opt/cpguard/logs/install_log on every cPGuard server.
            2) Preserve the hidden artifacts (Playbook 1), then update to 5.88.25 or later (Playbook 2).
            3) Rotate credentials from a trusted environment (Playbook 5).
FINDINGS:   [fill in after execution]
GAPS:       Upload host and method unpublished; named files are deleted by cleanup; shell builtin reads and later
            credential use are outside endpoint telemetry; no hash, IP or domain published.
ACTIONS:    Promote Q1/Q2/Q5 to detect-only Custom IOA after the Section 11 gates pass. Deploy Section 12 items 1 to 4.
OWNER:      HuntPack
VERSION:    v0.1 -- 2026-11-04
17

Changelog

v0.12026-11-04Initial HuntPack (draft). Built from the OPSSHIELD advisory plus three corroborating articles, all saved to the sources folder (the BleepingComputer snapshot is a model-generated extraction because direct fetch was blocked). Four sourced filesystem artifacts, three labelled placeholders, no prevent rows. Eight queries; upload and later-credential-use gaps stated in the Coverage Map. No actor, campaign or malware name is assigned by any source and none is used. SentinelOne PowerQuery and Microsoft Defender KQL tabs added to Q1-Q8: SIEM Query Rosetta release 0.1.0 (public-beta; static-reviewed translations).
18

References

TierSourceUsed ForAccess Date
1OPSSHIELD: cPGuard security advisory, update channel compromise (2026-11-03)Timeline, versions, collected data, named artifacts, install_log check, remediation2026-11-04
2The Hacker News: cPGuard Update Channel Hijacked to Backdoor Linux Servers and Collect Password Hashes (2026-11-03)Corroboration, artifact descriptions, support-access key note2026-11-04
2BleepingComputer: Hackers hijack cPGuard autoupdate to deploy backdoor, steal data (2026-11-03)Single-sourced 2,946-server figure; extraction snapshot only2026-11-04
2SecurityOnline: cPGuard Supply Chain Attack Pushes Backdoor Through Update ChannelCorroboration of date, versions and backdoor path2026-11-04

All four sources are saved to cPGuard-OpsShield-SupplyChain-Hunt-sources/ for provenance re-verification. MITRE mitigation and CIS Linux Benchmark references in Section 12 are cited as control families only; those pages were not fetched and no indicator or campaign claim depends on them.