GitHub Actions Abuse Powers Distributed cPanel & WHM Exploitation

Poisoned .github/workflows turn ephemeral GitHub-hosted runners into an internet-scale scanner for CVE-2026-41940, then harvest cloud, payment and source-control credentials from compromised hosting servers.
Threat
GitHub Actions Abuse / cPanel-WHM Campaign
Severity
EXPLOITED ITW
Type
CI/CD supply chain → mass exploitation → credential theft
Access
Unauthenticated internet (CVE-2026-41940, CVSS 9.8)
Version
v0.1 · 2026-07-31
Author
HuntPack
Confidence
High (vendor research + KEV + NVD)
01

Executive Summary

A threat actor gained push access to a legitimate PHP/DevOps maintainer's GitHub repositories and committed hundreds of malicious GitHub Actions workflow files into .github/workflows/. Between 2026-07-12 and 2026-07-13 Packagist auto-synchronised the poisoned development versions across all ten of that maintainer's packages: 583 malicious workflow files in total, 55 to 62 per package version. The PHP library code itself was never malicious. The execution layer is GitHub Actions.

Each workflow runs on push to any branch or on manual dispatch, spins up an ephemeral GitHub-hosted Ubuntu runner with a 350-minute timeout, detects the runner CPU architecture, downloads a matching Linux ELF payload from the actor-controlled host 43.228.157.68, writes it to /tmp/.svc, chmods it to 755 and launches it as an internet scanner. The scanner sweeps ports 80, 443, 8080, 8443 and the cPanel/WHM control-plane ports 2082, 2083, 2086 and 2087, attempts exploitation of CVE-2026-41940 (unauthenticated authentication bypass in the cPanel & WHM login flow, CVSS 3.1 base 9.8, CISA KEV since 2026-04-30, flagged for known ransomware use), and then harvests server-side secrets: AWS keys, GitHub and GitLab tokens, OpenAI and Google API credentials, Stripe keys, SendGrid and Mailgun credentials, database logins, SSH material, git remotes, environment files and configuration files.

Results leave in chunked HTTP POSTs to /api/github-results with a heartbeat to /api/github-heartbeat every 30 seconds. Fourteen recovered workflows also fired an nslookup at a unique DNSHook callback hostname, and a GitHub code search for that identifier returned roughly 6,100 matching workflow files across unrelated repositories, with broader searches on the C2 address, scanner arguments and exfiltration endpoints returning about 15,000 to 16,000 files. Those are file counts, not confirmed victims, but the reuse confirms the campaign is far larger than one maintainer.

The defensive problem splits cleanly in two. Victim side: any internet-facing cPanel or WHM server running an unpatched build is being sprayed by an unknown number of GitHub-hosted runners with clean, reputable source IPs that no threat-intel feed will block. Attacker-infrastructure side: any repository your organisation owns, and any self-hosted runner or developer workstation that executes a poisoned workflow, becomes part of the scanning fleet and leaks whatever secrets that pipeline can reach.

Defender priority: patch cPanel/WHM to the fixed build for your release tier first (the KEV due date was 2026-05-03 and is long past), then run Q9 and Q10 against every hosting server for post-exploitation evidence, and in parallel search your own GitHub org for workflow files referencing the C2 address, the DNSHook hostname, or the ipscan --exploit argument set. Source-IP blocking is not a control here: the scanning traffic originates from GitHub's own runner ranges.

02

Source Review & Web Hunter Notes

TierSourceKey findingCarry forward
1Socket threat research (2026-07-22)Authoritative technical writeup. Workflow structure, payload URLs, scanner argv, heartbeat/exfil endpoints, DNSHook hostname, AMD64 payload SHA-256, full IOC list, ATT&CK mapping.Yes: primary
1NVD: CVE-2026-41940 (API 2.0)CVSS 3.1 9.8 / CVSS 4.0 9.3, CWE-306 missing authentication for critical function. Full affected/fixed build matrix for cPanel, WHM and WP Squared.Yes: patch baseline
1CISA KEV catalog (catalog release 2026-07-29)Added 2026-04-30, remediation due 2026-05-03, knownRansomwareCampaignUse: Known. Confirms mass exploitation predates this campaign.Yes: urgency
2watchTowr Labs technical analysisRoot cause: CRLF injection into the cpsrvd session file via a Basic-auth password combined with an ob-less session cookie. Names the on-disk artefacts (/var/cpanel/sessions/raw/, /var/cpanel/sessions/cache/) that make victim-side triage possible.Partial: forensic artefacts only, exploitation mechanics deliberately not reproduced here
2The Hacker News (2026-07-23)Independent corroboration of the 583-file count, the July 12–13 window, the C2 address and the DNSHook identifier.Yes: corroboration
2Cyber Security News (2026-07-23)Reproduces the full IOC table including the /tmp/.svc drop path. Useful second attestation for every atomic indicator in this pack.Yes: corroboration
2SecurityOnline (2026-07)Scale framing (6,100 to 16,000 matching files) and the panel-port restriction guidance. Notes attribution confidence is low: nobody named or charged.Partial: scale + attribution caveat
n/acPanel vendor advisory (support.cpanel.net, 2026-04-28)Returned HTTP 403 to both direct fetch and the fetch proxy. No snapshot saved, therefore not cited as evidence. The fixed build numbers in this pack come from the NVD affected-version matrix instead, which is snapshotted.No: unfetchable

Web-hunter decisions

  • Every source in this pack was fetched, read and saved verbatim to GitHubActions-cPanel-CVE-2026-41940-Hunt-sources/ (7 snapshots). Every atomic indicator below traces back to at least one of them.
  • No injected instructions were found. All seven snapshots were scanned for text attempting to direct the research agent (fetch-this-instead, ignore-prior, claimed authorisation). Nothing matched. One source carries unrelated promotional interstitials, which is marketing noise, not an instruction, and is not disqualifying.
  • The compromised maintainer is a victim, not the actor. Socket states this explicitly. The maintainer handle and the ten package names are therefore listed as exposure-audit strings for dependency review, never as detect-or-block indicators, and the account has been suspended.
  • Package counts are not victim counts. The ~6,100 and ~15,000–16,000 figures are matching files in GitHub code search. Some hits are duplicate copies in one repo and some may be actor-controlled staging. This pack does not treat them as a victim population.
  • Exploit mechanics are summarised, not reproduced. The watchTowr writeup contains a working exploitation walkthrough and links to a public PoC. Only the defensive residue (session-file artefacts, service names, log locations) is carried into this pack; the PoC is not linked.
03

Hunt Brief & Attack Chain

Attack chain

#StepTelemetryHunt angle
1Actor gains push access to a legitimate maintainer's GitHub repositories and commits 55–62 workflow YAML files per repo under .github/workflows/GitHub audit log, Actions run history, commit historyNative hunt N1/N2: no EDR visibility on github.com
2Packagist auto-synchronises the poisoned dev-* branch versions across all ten packages (2026-07-12 → 07-13)Composer lockfiles, dependency inventoryNative hunt N5: dependency exposure audit
3A push (any branch) or a manual workflow_dispatch launches an ephemeral ubuntu-latest runner, timeout-minutes: 350GitHub Actions run log; on self-hosted runners, ProcessRollup2Q11: runner process anomaly
4Runner detects CPU architecture and pulls the matching ELF from /api/dl/{386,amd64,arm,arm64} using curl with a wget fallback, writing to /tmp/.svc, then chmod 755ProcessRollup2, ELFFileWritten, NetworkConnectIP4Q1, Q3, Q4, Q6, Q7
5Scanner runs: ipscan --source random,all --exploit CVE-2026-41940 --git --envdump --ports 80,443,8080,8443,2082,2083,2086,2087 --git-workers 20 --count 0 --no-reverseProcessRollup2 CommandLine, NetworkConnectIP4Q5, Q8
6Fourteen workflow variants also nslookup a unique DNSHook hostname to confirm execution out-of-bandDnsRequestQ2
7Internet-facing cPanel/WHM servers are exploited through the CVE-2026-41940 authentication bypass in the login flowcPanel login_log, /var/cpanel/sessions/, web/panel access logsNative hunts N3/N4; Q9 for the process residue
8On the compromised server the payload harvests AWS keys, GitHub/GitLab tokens, OpenAI and Google API keys, Stripe keys, SendGrid/Mailgun creds, DB credentials, SSH keys, git remotes, env and config filesProcessRollup2, FileOpenInfoQ9, Q10
9Exfiltration: chunked HTTP POST to /api/github-results (up to 2,000 new lines live, 5,000 in the final stage, offset-tracked) plus a 30-second heartbeat to /api/github-heartbeatNetworkConnectIP4, ProcessRollup2 CommandLineQ1, Q6
10Stolen source-control tokens feed further repository compromise, restarting the loopGitHub audit log, secret-scanning alertsNative hunt N2

Hunt hypotheses (ordered by fidelity)

#HypothesisATT&CKFalcon eventsExpected FPConf
H1A managed host has contacted the campaign C2 at 43.228.157.68 on any port, in either direction of the kill chain (payload pull or result exfil).T1105, T1041NetworkConnectIP4None credible. This is a single dedicated actor host.High
H2A managed host has resolved the unique DNSHook callback hostname, proving a poisoned workflow executed with that host's resolver.T1071.004DnsRequestSecurity researchers replaying the sample; DNS logging appliances doing passive resolution.High
H3The known AMD64 scanner ELF has executed or been written on a managed Linux host.T1105ProcessRollup2, ELFFileWrittenNone. Hash match is exact.High
H4A process has run with the scanner's distinctive argument set (ipscan plus --exploit / --envdump / --git-workers / --no-reverse), even if the binary name or hash rotated.T1595.002, T1190ProcessRollup2Sanctioned internal scanners using a coincidentally similar flag; rare.High
H5A dot-prefixed ELF has been dropped into /tmp and executed: the drop pattern, independent of the specific name .svc.T1105, T1059.004ProcessRollup2, ELFFileWrittenSome packaging and build tooling stages hidden temp binaries; container image builds.Medium-high
H6A host has produced outbound fan-out to the cPanel/WHM control-plane ports 2082/2083/2086/2087 across many distinct destinations, i.e. it is scanning, not administering.T1595.002, T1496.002NetworkConnectIP4Hosting-provider monitoring, backup agents, migration tooling, legitimate multi-server admin jumpboxes.Medium-high
H7A cPanel/WHM service process has spawned a shell, interpreter or download tool: the post-exploitation residue of the authentication bypass.T1190, T1059.004ProcessRollup2cPanel hooks, custom WHM plugins, cpanel-provided maintenance scripts.High
H8Credential material (~/.aws/credentials, .env, .git-credentials, id_rsa, .npmrc) is being enumerated or read en masse on a hosting server.T1552.001, T1005, T1119ProcessRollup2, FileOpenInfoBackup jobs, config-management agents, legitimate deploy pipelines. Noisy by design.Medium
H9A self-hosted CI runner or developer workstation has executed network tooling or a /tmp binary as a child of the runner process.T1195.001, T1584.006ProcessRollup2Legitimate CI jobs do download things. Requires per-org baselining.Medium
H10Workflow files under .github/workflows are being written or modified by a shell rather than by a developer's editor or an approved automation identity.T1195.001ProcessRollup2High. Normal development touches these paths constantly.Medium-low
04

Consolidated IOC Table

TypeValueConfActionContextExpiry
IPv443.228.157.68HighdetectC2: payload delivery, heartbeat and exfiltration, served on tcp/802027-01-31
URLhttp://43.228.157.68:80/api/dl/386Highdetect32-bit x86 Linux payload2027-01-31
URLhttp://43.228.157.68:80/api/dl/amd64Highdetect64-bit x86 Linux payload2027-01-31
URLhttp://43.228.157.68:80/api/dl/armHighdetect32-bit ARM Linux payload2027-01-31
URLhttp://43.228.157.68:80/api/dl/arm64Highdetect64-bit ARM Linux payload2027-01-31
URLhttp://43.228.157.68:80/api/github-heartbeatHighdetectExecution telemetry, posted every 30 seconds with repo name and last log line2027-01-31
URLhttp://43.228.157.68:80/api/github-resultsHighdetectChunked, offset-tracked exfiltration of harvested credentials2027-01-31
Domainf5b0b742-240a-4811-8a5b-b0ba6060685d.dnshook.siteHighdetectDNSHook out-of-band execution callback; the campaign's highest-fidelity pivot2027-01-31
SHA-25622f721fd3a81d2e27cbf90a122bb977f630c50b79daa98350f0e57b04dfa81f1HighdetectRecovered AMD64 Linux scanning and exploitation payload2027-01-31
Filename / path/tmp/.svcHighhuntDrop path on the runner. Trivially renameable, so hunt the pattern (Q4), do not block the literal string2026-10-31
Command signatureipscan --source random,all --exploit CVE-2026-41940 --git --envdump --no-reverseHighhuntScanner argv. Survives binary rename and hash rotation2026-10-31
URI path fragment/api/github-results, /api/github-heartbeat, /api/dl/HighhuntEndpoint layout. Survives an IP change if the actor redeploys the same panel2026-10-31
Ports2082, 2083, 2086, 2087MediumenrichcPanel/WHM control plane. Legitimate service ports, never block outright, use for fan-out scoring (Q8)2026-08-31
Maintainer handledinushchathuryaHighpivotVictim identifier, not attribution. Use only for Composer dependency exposure audit2026-08-31

Perishability: the C2 host and the DNSHook hostname are the only truly atomic indicators, and both are rotatable in minutes. The durable coverage in this pack is behavioural: Q4 (dot-prefixed /tmp ELF), Q5 (scanner argv), Q8 (panel-port fan-out) and Q9 (cpsrvd child process) all keep working after the actor moves infrastructure. Deploy those as the standing detections and treat the IP, URL and hash rows as a 90-day sweep.

05

Affected Surface & Telemetry Matrix

SurfaceExposureRequired telemetryPriorityGap risk
Internet-facing cPanel / WHM servers (Linux)Direct, unauthenticated exploitation of CVE-2026-41940; full credential harvest on successFalcon Linux sensor: ProcessRollup2, NetworkConnectIP4, ELFFileWritten; cPanel login_log; panel access logsCriticalLow, if the sensor is deployed. Many hosting fleets run cPanel boxes without EDR, which is the real gap
Self-hosted GitHub Actions runners (Linux)Executes the poisoned workflow with whatever secrets and network reach the runner hasProcessRollup2, NetworkConnectIP4, DnsRequest, ELFFileWrittenHighLow
GitHub-hosted runners (ubuntu-latest)Where the campaign actually ran. Ephemeral, third-party computeNone. No sensor placement is possibleHighTotal blind spot: must be covered by GitHub Actions run logs and org policy, not EDR
Developer workstations (Linux / macOS)Cloning a poisoned repo is safe; only push or manual dispatch triggers execution. Local risk is a developer running the workflow by handProcessRollup2, DnsRequestMediumMedium: macOS coverage of the ELF payload is not applicable, but the curl/DNS telemetry still lands
PHP / Composer projectsTen Packagist packages carried the workflows in vendor/. Not executed by Composer install, but they are contaminated artefacts in your lockfileSCA tooling, composer.lock inventoryMediumHigh: no EDR signal at all; needs a dependency audit
Cloud tenants reachable from the harvested keysSecond-stage risk: AWS keys, Stripe keys, SendGrid/Mailgun creds, OpenAI and Google API keys stolen from compromised serversCloudTrail / provider audit logs, Falcon Cloud Security if licensedHighMedium: depends on whether server-resident keys are inventoried at all
Source-control tenantsStolen GitHub/GitLab tokens enable the next round of repository compromiseGitHub audit log, secret-scanning alerts, OAuth app inventoryHighMedium
Windows estateEffectively none. The payload is Linux-only ELF for x86/x86-64/ARM/ARM64n/aLown/a, do not spend Windows detection budget here beyond the DNS and C2 IP sweeps, which are platform-agnostic
06

ATT&CK Mapping

TacticTechniqueObserved behaviourQuery / control
Resource DevelopmentT1584.006: Compromise Infrastructure: Web ServicesCompromised GitHub repositories and GitHub-hosted runners used as disposable attack infrastructureN1, N2 · Hardening H-N2
Initial AccessT1195.001: Supply Chain Compromise: Software Dependencies and Development Tools583 malicious workflow files pushed into a legitimate maintainer's repos; auto-synced to PackagistQ12, N5 · Hardening H-I1
Initial AccessT1190: Exploit Public-Facing ApplicationCVE-2026-41940 authentication bypass against internet-facing cPanel and WHM login flowsQ9, N3, N4 · Hardening H-I2
ExecutionT1059.004: Command and Scripting Interpreter: Unix ShellWorkflow run: steps execute curl/wget, chmod and the scanner under bash on an Ubuntu runnerQ5, Q7, Q11
Command and ControlT1105: Ingress Tool TransferArchitecture-matched ELF pulled from /api/dl/{386,amd64,arm,arm64} to /tmp/.svc, then chmod 755Q1, Q3, Q4, Q6, Q7
DiscoveryT1082: System Information DiscoveryRunner CPU architecture detection to select the correct payload buildQ11 (partial)
ReconnaissanceT1595.002: Active Scanning: Vulnerability Scanningipscan --source random,all --ports 80,443,8080,8443,2082,2083,2086,2087 across the public internetQ5, Q8
ImpactT1496.002: Resource Hijacking: Bandwidth HijackingFree GitHub Actions compute and egress consumed as scanning capacity, at the repo owner's expenseN1 · Hardening H-N1
Credential AccessT1552.001: Unsecured Credentials: Credentials in FilesHarvests AWS keys, GitHub/GitLab tokens, OpenAI and Google API keys, Stripe keys, SendGrid/Mailgun creds, DB creds, SSH materialQ10 · Hardening H-C1, H-C2
CollectionT1005: Data from Local SystemEnvironment files, configuration files and git remotes read off the compromised serverQ10
CollectionT1119: Automated Collection--envdump and --git with 20 git workers automate the harvest at scan speedQ5
CollectionT1074.001: Data Staged: Local Data StagingResults written to local output files on the runner, then read incrementally by the exfil loopQ6
Command and ControlT1071.001: Application Layer Protocol: Web ProtocolsPlain HTTP POST to the C2 API on tcp/80Q1, Q6
Command and ControlT1071.004: Application Layer Protocol: DNSnslookup of a unique DNSHook hostname as an out-of-band execution confirmation channelQ2
ExfiltrationT1020: Automated ExfiltrationOffset-tracked chunked upload: up to 2,000 new lines per live request, 5,000 in the final stage, running even when the scanner failsQ1, Q6
ExfiltrationT1041: Exfiltration Over C2 ChannelEverything leaves over the same HTTP API that delivered the payloadQ1, Q6
07

Native Audit-Log Hunts (non-CQL)

The most important part of this campaign happened on GitHub-hosted compute where no EDR sensor can exist. These checks are not optional extras: for step 3 of the attack chain they are the only coverage.

N1 · GitHub Actions run-history review (org-wide)

  • List every workflow run in the last 90 days with an unusually long duration. The campaign's workflows set timeout-minutes: 350, so a scanning job looks like a job that never finishes.
  • Flag any workflow triggered on push to branches: ['**'] combined with workflow_dispatch, particularly in repositories with no history of CI.
  • Flag repositories with an anomalous count of workflow files. Legitimate projects rarely carry 55 to 62 of them.
# GitHub CLI - inventory workflow files and long-running runs across the org
gh repo list YOUR_ORG --limit 1000 --json nameWithOwner --jq '.[].nameWithOwner' | while read -r R; do
  N=$(gh api "repos/$R/actions/workflows" --jq '.total_count' 2>/dev/null)
  [ -n "$N" ] && [ "$N" -gt 10 ] && echo "REVIEW  $R  workflows=$N"
done

# Runs that ran longer than an hour in the last 90 days
gh api "repos/YOUR_ORG/YOUR_REPO/actions/runs?per_page=100" \
  --jq '.workflow_runs[] | select(.conclusion != null)
        | {name, created_at, updated_at, event, actor:.actor.login, url:.html_url}'

N2 · GitHub code search across your own org

Search your organisation's repositories for the campaign's own markers. This is the same pivot the researchers used, pointed inward.

# Search YOUR org only. Each of these is a campaign marker, not a generic string.
gh search code --owner YOUR_ORG "43.228.157.68"
gh search code --owner YOUR_ORG "dnshook.site"
gh search code --owner YOUR_ORG "api/github-heartbeat"
gh search code --owner YOUR_ORG "api/github-results"
gh search code --owner YOUR_ORG "ipscan --exploit"
gh search code --owner YOUR_ORG "GOMEMLIMIT" --filename "*.yml"

# Local equivalent across every cloned repo on a build host
grep -rIl --include='*.yml' --include='*.yaml' \
  -e '43.228.157.68' -e 'dnshook' -e 'github-heartbeat' -e 'github-results' -e 'ipscan' \
  /path/to/repos/*/.github/workflows/ 2>/dev/null

N3 · cPanel / WHM login log review (victim side)

  • Review /usr/local/cpanel/logs/login_log for bursts of failed logins immediately followed by an apparently successful session from the same source. The bypass mints a pre-auth session by failing a login first, so a failure that is promptly followed by authenticated API activity from the same IP is the shape to look for.
  • Review /usr/local/cpanel/logs/access_log for requests carrying a cpsess token that has no matching successful authentication event.
  • Correlate against WHM's own Manage External Authentications and active-session list for sessions you cannot account for.

N4 · cPanel session-file artefact sweep (victim side)

The vulnerability writes attacker-controlled records into the on-disk session store. Two directories hold the evidence: /var/cpanel/sessions/raw/ (line-oriented key=value) and /var/cpanel/sessions/cache/ (JSON). A raw session file whose pass= record is followed by additional top-level records such as hasroot, tfa_verified or successful_internal_auth_with_timestamp is a forged session.

# Read-only triage. Run as root on a suspect cPanel/WHM host.
# Session files containing privilege records injected after the pass= line.
for F in /var/cpanel/sessions/raw/*; do
  awk '/^pass=/{seen=1;next} seen && /^(hasroot|tfa_verified|successful_internal_auth_with_timestamp)=/{print FILENAME": "$0}' "$F"
done

# Preserve before you clean. Hash and copy, do not edit in place.
tar -czf /root/cpanel-session-evidence-$(date +%F).tar.gz \
    /var/cpanel/sessions/raw /var/cpanel/sessions/cache \
    /usr/local/cpanel/logs/login_log /usr/local/cpanel/logs/access_log

# Confirm the installed build against the fixed versions in section 12.
/usr/local/cpanel/cpanel -V

cPanel also publishes an IOC detection script for servers that were exposed while unpatched. Run the vendor's current script from the official support article before declaring a host clean.

N5 · Composer / Packagist dependency exposure audit

Installing an affected package does not execute the workflows: Composer places them in vendor/ where GitHub ignores nested workflow files. But a contaminated artefact in your lockfile is still an artefact you need to replace, and it means your dependency review missed a compromised maintainer.

# Any of the ten affected packages present in a lockfile
grep -rIl --include='composer.lock' -e 'dinushchathurya/' /path/to/projects/ 2>/dev/null

# Any dependency pinned to a branch-based dev version rather than a stable release
grep -rIn --include='composer.json' -e '"dev-' -e '@dev' /path/to/projects/ 2>/dev/null

# Nested workflow files inside vendor/ - contamination marker, not execution
find /path/to/projects -path '*/vendor/*/.github/workflows/*' -name '*.yml' 2>/dev/null
08

CrowdStrike LogScale CQL Hunt Queries

Pick your tenant's cloud first — every "Open in Falcon" button below uses this selection.

No query below carries an in-query time filter. Set the lookback with the console's time picker; each card records the intended window as a // LOOKBACK: comment. Start at 90 days for the atomic sweeps (Q1–Q3) and 7 days for the behavioural detections.

Q1 · C2 contact: payload pull, heartbeat or exfiltration
CONF HIGHFP LOWCOST LOW

Looks for: any outbound connection from any managed host to the campaign C2. The same host serves payload delivery, the 30-second heartbeat and the chunked result upload, so a single hit covers three chain steps. Highest-value single query in the pack, run it first, at 90 days.

// HUNT: Outbound connection to GitHub-Actions-abuse campaign C2
// MITRE: T1105 | CONF: high  FP: low  COST: low
// REQUIRES: NetworkConnectIP4 (Linux, Windows, macOS)
// LOOKBACK: 90d (set in the console time picker)
// FALSE POSITIVES: none credible - dedicated actor-controlled host
#event_simpleName=NetworkConnectIP4
| RemoteAddressIP4="43.228.157.68"
| table([@timestamp, aid, ComputerName, UserName, ContextBaseFileName, LocalAddressIP4, RemoteAddressIP4, RemotePort])
| sort(@timestamp, order=desc)
Q2 · DNSHook out-of-band execution callback
CONF HIGHFP LOWCOST LOW

Looks for: resolution of the campaign's unique DNSHook hostname, or of any dnshook.site subdomain. Fourteen recovered workflows fire this nslookup to confirm execution out-of-band. Matching the parent domain rather than only the exact FQDN keeps the query alive if the actor mints a new callback GUID.

// HUNT: DNSHook callback resolution (campaign execution confirmation)
// MITRE: T1071.004 | CONF: high  FP: low  COST: low
// REQUIRES: DnsRequest
// LOOKBACK: 90d (set in the console time picker)
// FALSE POSITIVES: security researchers detonating the sample; a passive-DNS
//   appliance replaying queries. Both are identifiable by the requesting host.
#event_simpleName=/^(DnsRequest|SuspiciousDnsRequest)$/
| DomainName=/dnshook\.site$/i
| table([@timestamp, aid, ComputerName, UserName, DomainName, ContextBaseFileName, ContextProcessId_decimal])
| sort(@timestamp, order=desc)
Q3 · Known scanner payload hash: written or executed
CONF HIGHFP LOWCOST LOW

Looks for: the recovered AMD64 scanner ELF, either executing or landing on disk. Only the amd64 build was recovered and published, so the 386, arm and arm64 variants have no published hash: Q4 through Q7 are what cover those.

// HUNT: Campaign scanner payload by SHA-256 (amd64 build)
// MITRE: T1105 | CONF: high  FP: low  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2 / ELFFileWritten
// LOOKBACK: 90d (set in the console time picker)
// FALSE POSITIVES: none - exact hash match
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2|ELFFileWritten)$/
| SHA256HashData="22f721fd3a81d2e27cbf90a122bb977f630c50b79daa98350f0e57b04dfa81f1"
| table([@timestamp, aid, ComputerName, UserName, #event_simpleName, FileName, ImageFileName, TargetFileName, CommandLine, SHA256HashData])
| sort(@timestamp, order=desc)
Q4 · Dot-prefixed hidden binary executed from /tmp (Linux)
CONF HIGHFP MEDIUMCOST LOW

Looks for: the drop pattern rather than the name. The payload lands at /tmp/.svc, but a dot-prefixed short-named executable running out of a world-writable temp directory is the durable signal and survives a rename. FP: container image builds and some packaging tooling stage hidden helpers in /tmp; exclude your known build-agent images and any .X11-unix-style socket paths by name before promoting.

// HUNT: Hidden dot-prefixed ELF executed from a world-writable temp dir
// MITRE: T1105, T1059.004 | CONF: high  FP: medium  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2 (Linux)
// LOOKBACK: 7d (set in the console time picker)
// FALSE POSITIVES: build/packaging tooling staging hidden helper binaries in /tmp
// TUNING: exclude your build-agent host group by ComputerName, and exclude any
//   approved helper basenames by adding e.g. | FileName!=/^\.(build|cache)-/i
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| event_platform=Lin
| ImageFileName=*/tmp/.*
| FileName=/^\.[A-Za-z0-9_-]{1,12}$/
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine, SHA256HashData])
| sort(@timestamp, order=desc)
Q5 · Scanner argument signature (ipscan / --exploit / --envdump)
CONF HIGHFP LOWCOST LOW

Looks for: the scanner's distinctive command line, independent of binary name or hash. Requiring both the ipscan subcommand and at least one of the campaign-specific flags keeps this precise. This is the single best behavioural detection in the pack: promote it to a Custom IOA.

// HUNT: Distributed scanner invoked with the campaign's argument set
// MITRE: T1595.002, T1190, T1119 | CONF: high  FP: low  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// LOOKBACK: 30d (set in the console time picker)
// FALSE POSITIVES: a sanctioned internal scanner using a coincidentally similar
//   flag set. Verify the binary path and hash before closing as benign.
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| CommandLine=/\bipscan\b/i
| CommandLine=/--exploit|--envdump|--git-workers|--no-reverse|--source[ =]random/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine, SHA256HashData])
| sort(@timestamp, order=desc)
Q6 · C2 API endpoint paths in a command line
CONF HIGHFP LOWCOST LOW

Looks for: the campaign's endpoint layout in any process command line: the architecture download paths, the heartbeat, or the chunked-results upload. Because the workflows call these through curl with an explicit URL, the path survives an IP rotation as long as the actor redeploys the same panel software.

// HUNT: Campaign C2 API paths referenced on a command line
// MITRE: T1105, T1020, T1041 | CONF: high  FP: low  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// LOOKBACK: 30d (set in the console time picker)
// FALSE POSITIVES: none observed - these paths are campaign-specific
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| CommandLine=/\x2fapi\x2f(github-heartbeat|github-results|dl\x2f(386|amd64|arm64|arm))/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine])
| sort(@timestamp, order=desc)
Q7 · curl / wget pulling an executable from a bare IPv4 over plain HTTP
CONF MEDIUMFP MEDIUMCOST MEDIUM

Looks for: the generic tradecraft that survives every IOC rotation in this campaign: a download tool fetching from a raw IP address (no hostname, no TLS) and writing into a temp directory. Socket's own defensive guidance calls out exactly this: alert on payload downloads from raw IP addresses. FP: internal artifact mirrors and air-gapped repos are frequently addressed by IP; baseline your own mirror addresses out first.

// HUNT: Download tool fetching from a raw IPv4 over plaintext HTTP into temp
// MITRE: T1105 | CONF: medium  FP: medium  COST: medium
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// LOOKBACK: 7d (set in the console time picker)
// FALSE POSITIVES: internal package mirrors and artifact stores addressed by IP;
//   provisioning scripts that pull from a bootstrap host
// TUNING: exclude your own mirrors by adding a negative match on their octets,
//   e.g. | CommandLine!=/http:\x2f\x2f10\.20\.30\./, and exclude sanctioned
//   provisioning parents by ParentBaseFileName (cloud-init, ansible, chef-client)
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| FileName=/^(curl|wget)$/i
| CommandLine=/http:\x2f\x2f\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
| CommandLine=/-o[ =]\x2ftmp\x2f|-O[ =]\x2ftmp\x2f|\x2ftmp\x2f\./
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine])
| sort(@timestamp, order=desc)
Q8 · Outbound fan-out to cPanel / WHM control-plane ports
CONF HIGHFP MEDIUMCOST MEDIUM

Looks for: a managed host behaving like a scanner rather than an administrator: many distinct destination IPs on 2082/2083/2086/2087. Administering hosting servers touches a handful of known addresses; scanning touches hundreds. Threshold is deliberately conservative; lower it after you see your own baseline. FP: hosting-provider monitoring, migration tooling and backup agents legitimately fan out on these ports, exclude those specific hosts, not the ports.

// HUNT: Panel-port fan-out consistent with mass cPanel/WHM scanning
// MITRE: T1595.002, T1496.002 | CONF: high  FP: medium  COST: medium
// REQUIRES: NetworkConnectIP4
// LOOKBACK: 24h (set in the console time picker)
// FALSE POSITIVES: hosting-provider monitoring, server-migration tooling,
//   backup agents, and multi-tenant admin jumpboxes
// TUNING: exclude sanctioned management hosts by ComputerName before promoting,
//   and raise the threshold until your own admin jumpboxes fall below it
#event_simpleName=NetworkConnectIP4
| in(field=RemotePort, values=["2082", "2083", "2086", "2087"])
| groupBy([aid, ComputerName], function=[count(field=RemoteAddressIP4, distinct=true, as=distinct_targets), count(as=total_conns)])
| distinct_targets > 25
| sort(distinct_targets, order=desc)
Q9 · cPanel / WHM service process spawning a shell or download tool
CONF HIGHFP LOWCOST LOW

Looks for: the victim-side post-exploitation residue. CVE-2026-41940 gives the attacker an authenticated control-plane session; anything they do next runs as a child of the cPanel daemons. A panel daemon spawning bash, perl, python, curl, wget or a network tool is the highest-fidelity victim-side signal available in EDR, and it does not depend on any campaign IOC.

// HUNT: cPanel/WHM daemon spawning a shell, interpreter or download tool
// MITRE: T1190, T1059.004 | CONF: high  FP: low  COST: low
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2 (Linux, cPanel hosts)
// LOOKBACK: 30d (set in the console time picker)
// FALSE POSITIVES: cPanel hooks and custom WHM plugins legitimately shell out;
//   confirm the child command line before escalating
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| event_platform=Lin
| ParentBaseFileName=/^(cpsrvd|cpsrvd-ssl|whostmgrd|cpanellogd|queueprocd|cpdavd|dovecot)$/i
| FileName=/^(sh|bash|dash|zsh|ksh|perl|python|python3|php|curl|wget|nc|ncat|socat|chmod|chattr|base64|xxd)$/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine, SHA256HashData])
| sort(@timestamp, order=desc)
Q10 · Server-side credential material enumeration
CONF MEDIUMFP HIGHCOST MEDIUM

Looks for: the credential-harvest stage: command lines touching the exact secret stores the scanner targets. Run this scoped to hosting servers only, and correlate against Q9 hits from the same host and hour rather than triaging it standalone. FP: very high: backup jobs, config-management agents and deploy pipelines read these paths constantly. This is a correlation input, not an alert.

// HUNT: Bulk read/enumeration of cloud, git and SSH credential material
// MITRE: T1552.001, T1005, T1119 | CONF: medium  FP: high  COST: medium
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2 (Linux)
// LOOKBACK: 7d (set in the console time picker)
// FALSE POSITIVES: backup agents, ansible/chef/puppet runs, CI deploy steps,
//   and administrators legitimately reading their own dotfiles
// TUNING: exclude backup and config-management parents first --
//   | ParentBaseFileName!=/^(ansible|ansible-playbook|chef-client|puppet|restic|borg|rsync|duplicity)$/i
//   then require 2 or more distinct credential paths per host per hour before triage
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| event_platform=Lin
| FileName=/^(cat|grep|egrep|find|tar|zip|awk|sed|head|xargs|strings)$/i
| CommandLine=/\.aws\x2fcredentials|\.git-credentials|\.ssh\x2fid_(rsa|ed25519|ecdsa)|\.env\b|\.npmrc|\.pypirc|\.docker\x2fconfig\.json|\.netrc/i
| groupBy([aid, ComputerName, UserName, ParentBaseFileName], function=[count(as=hits), collect([CommandLine])])
| sort(hits, order=desc)
Q11 · Self-hosted CI runner executing network tooling or a temp binary
CONF MEDIUMFP MEDIUMCOST MEDIUM

Looks for: the campaign's execution shape on the one slice of CI you can instrument. GitHub-hosted runners are invisible to EDR, but a self-hosted runner is your host, and a poisoned workflow scheduled onto it behaves identically. FP: legitimate CI jobs download dependencies all day; the discriminator is a temp-directory binary or a raw-IP fetch, not the download itself.

// HUNT: Self-hosted Actions runner spawning download tooling or a /tmp binary
// MITRE: T1195.001, T1584.006, T1059.004 | CONF: medium  FP: medium  COST: medium
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2 on self-hosted runners
// LOOKBACK: 7d (set in the console time picker)
// FALSE POSITIVES: normal CI dependency fetches and toolchain installers
// TUNING: baseline your pipelines first, then exclude approved build steps by
//   ParentBaseFileName, and keep only rows where ImageFileName sits under /tmp
//   or the command line targets a raw IPv4 host
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| ParentBaseFileName=/^(Runner\.Worker|Runner\.Listener|run-helper\.sh|runsvc\.sh|actions-runner)$/i
| FileName=/^(curl|wget|nc|ncat|socat|chmod|nslookup|dig|host)$/i
| table([@timestamp, aid, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine])
| sort(@timestamp, order=desc)
Q12 · Workflow files under .github/workflows touched by a shell
CONF MEDIUMFP HIGHCOST MEDIUM

Looks for: bulk creation or rewriting of workflow YAML from a shell or scripting interpreter rather than an editor or IDE: the local footprint of the actor's 55-to-62-files-per-repo push, if it happened on a host you instrument. FP: high by nature. Developers and scaffolding tools legitimately generate workflow files; the signal is volume from a non-interactive parent, so triage on the grouped count, not on individual rows.

// HUNT: Bulk workflow-file manipulation from a shell or scripting interpreter
// MITRE: T1195.001 | CONF: medium  FP: high  COST: medium
// REQUIRES: ProcessRollup2 / SyntheticProcessRollup2
// LOOKBACK: 30d (set in the console time picker)
// FALSE POSITIVES: developers editing CI, cookiecutter/yeoman-style scaffolding,
//   IaC generators, and repo-templating scripts
// TUNING: exclude interactive developer sessions -- drop rows whose
//   ParentBaseFileName is an IDE or terminal, and only review hosts where the
//   grouped count exceeds 20 workflow-file operations in one hour
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/
| CommandLine=/\.github\x2fworkflows/i
| FileName=/^(sh|bash|dash|zsh|perl|python|python3|curl|wget|cp|mv|tee|git)$/i
| groupBy([aid, ComputerName, UserName, ParentBaseFileName], function=[count(as=ops), collect([CommandLine])])
| sort(ops, order=desc)

CQL quality-review notes

  • All twelve queries use documented Falcon data-model fields. event_platform is a standard sensor field but is tenant-visible only where Linux sensors are deployed: ⚠ validate in tenant that Linux telemetry is actually flowing before trusting an empty result from Q4, Q9, Q10 or Q12.
  • No query carries an in-query time filter. An expression on the right of a comparison does not parse in CQL, so the window is set in the console and recorded as a // LOOKBACK: comment on each card.
  • Forward slashes inside every regex literal are written as \x2f so no regex closes early. Basenames are matched through FileName, never through a path regex.
  • Every card rated FP medium or higher carries a // TUNING: line inside the query block, so the exclusion travels with the query when an analyst copies it.
  • Q3 covers only the amd64 build. The 386, arm and arm64 payloads were distributed but no hashes were published for them, so they are covered behaviourally by Q4–Q7 rather than by a fabricated hash.
09

CrowdStrike Custom IOA Recommendations

Three queries are clean enough to promote to blocking or high-severity Custom IOAs. The rest stay Investigate-only.

IOASourcePlatform / typePatternActionExclusions
IOA-1: Distributed scanner argument setQ5Linux · Process CreationCommand line matches .*\bipscan\b.* AND matches .*(--exploit|--envdump|--git-workers|--no-reverse).*Block execution · CriticalNone recommended. If you operate a sanctioned scanner with a colliding flag set, exclude it by full image path, never by flag.
IOA-2: cPanel daemon spawns a shellQ9Linux · Process CreationParent image ends with cpsrvd, cpsrvd-ssl, whostmgrd or cpdavd AND child image basename matches a shell, interpreter or download toolDetect · High (move to Block after two clean weeks)Exclude specific cPanel hook scripts by full child path after you have baselined them. Do not exclude by parent: that removes the whole detection.
IOA-3: Hidden temp binary executionQ4Linux · Process CreationImage path matches .*\x2ftmp\x2f\..{1,12}$ AND the file is executableDetect · HighExclude your build-agent host group; exclude approved helper basenames individually.

Custom IOC entries (Falcon IOC Management, not IOA)

  • 43.228.157.68: Block, Critical. Single-purpose actor host; there is no legitimate reason for any managed asset to reach it.
  • f5b0b742-240a-4811-8a5b-b0ba6060685d.dnshook.site: Block, Critical. Detonation-only hostname.
  • 22f721fd3a81d2e27cbf90a122bb977f630c50b79daa98350f0e57b04dfa81f1: Prevent, Critical.

Investigate-only (no alert email)

Q7, Q8, Q10, Q11 and Q12 are hunt queries. They earn their keep as scheduled searches feeding a weekly review queue and as correlation inputs behind IOA-1 through IOA-3, not as pageable alerts. Q1, Q2, Q3 and Q6 are covered by the IOC entries above and need no separate IOA.

Alert package: IOA-1 (the one that should page)

  • Scheduled Search name: DETECT: Distributed cPanel Scanner Execution (GitHub Actions Abuse)
  • Description: Fires when a process executes with the internet-scanner argument set used by the GitHub Actions abuse campaign to mass-exploit CVE-2026-41940 in cPanel and WHM. The host is either a compromised CI runner or an asset the actor is staging from.
  • Email subject: [CRITICAL] Detection: distributed cPanel/WHM scanner executing on HOST-01
  • Body: what fired (scanner argv match), what it means (this host is running mass-exploitation tooling against the public internet and harvesting credentials from anything it lands on), immediate actions (network-contain the host, capture the binary and its hash, pull the parent process tree, identify the triggering workflow or job), escalation (CI/platform owner plus incident commander, this is an outbound-attack event with third-party liability, not only an infection).

Triage checklist (L1, in order)

  1. Confirm the process is not a sanctioned internal scanner: check full image path and SHA-256 against your approved-tooling list.
  2. Network-contain the host in Falcon before further analysis. The harm is ongoing and outbound.
  3. Run Q1 scoped to that aid for C2 contact; run Q2 for the DNSHook callback. Either confirms campaign attribution.
  4. Pull the parent process tree. If the parent is a CI runner, identify the repository, workflow file and triggering commit.
  5. Run Q10 scoped to that host to determine what credential material was reachable.
  6. Treat every secret that host could read as compromised and start rotation, do not wait for confirmation of exfiltration.
  7. If the host is a cPanel/WHM server, run Q9 and native hunts N3/N4, and verify the installed build against section 12.
  8. Search your GitHub org for the campaign markers (native hunt N2) to find the source of the poisoned workflow.

Pivot queries

  • Host-scoped: take Q1 and append | aid="THE_AID"; run over 90 days to establish first contact.
  • Environment-wide: run Q6 with no other filter to find every host that has ever referenced the campaign's API paths, including ones where the binary was cleaned up.
  • Blast radius: run Q8 to find any other host in the estate exhibiting the same panel-port fan-out.
10

Machine-Readable IOC Appendix

Falcon IOC Management CSVbulk import
type,value,action,severity,expiration,description,tags
ipv4,43.228.157.68,prevent,critical,2027-01-31,GitHub Actions abuse campaign C2 - payload delivery + heartbeat + exfil,campaign:GHActions-cPanel
domain,f5b0b742-240a-4811-8a5b-b0ba6060685d.dnshook.site,prevent,critical,2027-01-31,DNSHook out-of-band execution callback,campaign:GHActions-cPanel
sha256,22f721fd3a81d2e27cbf90a122bb977f630c50b79daa98350f0e57b04dfa81f1,prevent,critical,2027-01-31,AMD64 Linux scanner and exploitation payload,campaign:GHActions-cPanel
url,http://43.228.157.68:80/api/dl/386,detect,critical,2027-01-31,32-bit x86 payload delivery path,campaign:GHActions-cPanel
url,http://43.228.157.68:80/api/dl/amd64,detect,critical,2027-01-31,64-bit x86 payload delivery path,campaign:GHActions-cPanel
url,http://43.228.157.68:80/api/dl/arm,detect,critical,2027-01-31,32-bit ARM payload delivery path,campaign:GHActions-cPanel
url,http://43.228.157.68:80/api/dl/arm64,detect,critical,2027-01-31,64-bit ARM payload delivery path,campaign:GHActions-cPanel
url,http://43.228.157.68:80/api/github-heartbeat,detect,critical,2027-01-31,execution telemetry endpoint - 30 second interval,campaign:GHActions-cPanel
url,http://43.228.157.68:80/api/github-results,detect,critical,2027-01-31,chunked credential exfiltration endpoint,campaign:GHActions-cPanel
filename,/tmp/.svc,detect,high,2026-10-31,scanner drop path on the runner - rename-prone, prefer Q4,campaign:GHActions-cPanel
# The 386 / arm / arm64 payload builds were distributed but no hashes were published.
# Do NOT invent them - Q4 through Q7 cover those builds behaviourally.
Behavioural signaturessurvives IOC rotation
SCANNER_ARGV       ipscan + (--exploit | --envdump | --git-workers | --no-reverse | --source random)
DROP_PATTERN       dot-prefixed executable, 1-12 char basename, running from /tmp
DOWNLOAD_PATTERN   curl or wget -> raw IPv4 host, plaintext http, output into /tmp
C2_PATH_LAYOUT     /api/dl/{386,amd64,arm,arm64} + /api/github-heartbeat + /api/github-results
BEACON_CADENCE     HTTP POST every ~30s carrying repo identifier + latest scanner log line
EXFIL_SHAPE        offset-tracked chunked POST, up to 2000 lines live / 5000 final
PANEL_FANOUT       one source host -> many distinct destinations on 2082/2083/2086/2087
PORTSET            80,443,8080,8443,2082,2083,2086,2087 in a single scan invocation
VICTIM_RESIDUE     cpsrvd / whostmgrd / cpdavd parent spawning sh, perl, python, curl, wget
WORKFLOW_TRIGGER   on: push branches ['**'] + workflow_dispatch, timeout-minutes: 350
RUNNER_ENV         GOMEMLIMIT set alongside a PANEL_URL pointing at a raw IPv4
Dependency exposure auditvictim identifiers, not block rules
# The maintainer is a VICTIM. These strings are for dependency review only.
# Do not block, do not treat as attribution. The account has been suspended.
# Affected: branch-based dev versions synced 2026-07-12 / 2026-07-13.
dinushchathurya/nationality-list
dinushchathurya/srilankan-divisional-secretariats
dinushchathurya/srilankan-gn-divisions
dinushchathurya/srilankan-local-authorities
dinushchathurya/srilankan-mobile-number-validator
dinushchathurya/srilankan-state-hospitals
dinushchathurya/srilankan-universities
dinushchathurya/uk-mobile-number-validator
dinushchathurya/uk-post-code
dinushchathurya/websmslk

# Action: remove the dev version, repin composer.lock to a stable release or a
# known-good commit reference predating 2026-07-12, then re-resolve.
composer show --installed | grep dinushchathurya
composer why dinushchathurya/PACKAGE
Patch & exposure auditCVE-2026-41940
# CVE-2026-41940 - CWE-306 missing authentication for critical function
# CVSS 3.1 base 9.8 CRITICAL / CVSS 4.0 base 9.3 CRITICAL
# CISA KEV: added 2026-04-30, due 2026-05-03, knownRansomwareCampaignUse = Known
#
# Fixed builds per release tier (NVD affected-version matrix). Upgrade to at
# least the build listed for your tier:
#   11.40.x  -> 11.86.0.41
#   11.88.x  -> 11.94.0.28
#   11.96.x  -> 11.102.0.39
#   11.104.x -> 11.110.0.97
#   11.112.x -> 11.118.0.63
#   11.120.x -> 11.124.0.35
#   11.126.x -> 11.126.0.54
#   11.128.x -> 11.130.0.19
#   11.132.x -> 11.132.0.29
#   11.134.x -> 11.134.0.20
#   11.136.x -> 11.136.0.5
# WP Squared (WP2) is affected from 11.136.1.7 - follow the vendor changelog.

# 1. What build is actually installed?
/usr/local/cpanel/cpanel -V

# 2. Which panel ports are exposed to the whole internet?
ss -ltnp | grep -E ':(2082|2083|2086|2087|2095|2096)\b'

# 3. Confirm the upcp updater is enabled and on a release tier that gets fixes
grep -E '^(CPANEL|RPMUP|SARULESUP|UPDATES)=' /etc/cpupdate.conf

# 4. Run the vendor IOC detection script on any host that was exposed unpatched.
11

Detection Validation Gates

Gate 1: Telemetry ready

  • Confirm Falcon Linux sensors are installed and reporting on every cPanel/WHM host and every self-hosted Actions runner. Run #event_simpleName=ProcessRollup2 | event_platform=Lin | groupBy([ComputerName]) and reconcile the result against your asset inventory. A silent host is not a clean host.
  • Confirm DnsRequest events are present for Linux servers. Many fleets have DNS telemetry only from workstations, which would make Q2 silently useless where it matters most.
  • Confirm NetworkConnectIP4 is not being sampled or dropped on high-volume hosting servers.

Gate 2: Benign baseline

  • Run Q4, Q7, Q8, Q10, Q11 and Q12 over 30 days with no exclusions and record the volume per host group. Any query returning more than ~50 rows per day per host group needs its TUNING line applied before promotion.
  • Explicitly enumerate: your build-agent host group (Q4, Q11), your internal package mirrors by IP (Q7), your sanctioned hosting-management jumpboxes (Q8), and your backup/config-management parents (Q10).

Gate 3: Positive tests (safe, defensive only)

  • Q1: from an isolated test host, initiate a TCP connection to the C2 address on tcp/80 and confirm the event lands. Do not send data.
  • Q2: resolve a benign sub-hostname under the callback domain from a test host and confirm DnsRequest fires.
  • Q4: copy /bin/true to /tmp/.tst, chmod 755, execute it once, confirm the row appears, then delete it.
  • Q5: create a harmless script named ipscan that only echoes its arguments, invoke it with --envdump --no-reverse, and confirm the match. No scanning, no exploitation.
  • Q9: on a lab cPanel host only, confirm the parent/child mapping fires by having a test cPanel hook invoke /bin/sh -c true.

Gate 4: Promotion criteria

  • Promote Q5 to IOA-1 (block) immediately: the FP surface is effectively nil.
  • Promote Q9 to IOA-2 at Detect severity; move to Block only after two consecutive clean weeks with your cPanel hooks baselined.
  • Promote Q4 to IOA-3 at Detect after the build-agent exclusion is in place.
  • Q7, Q8, Q10, Q11, Q12 stay as scheduled searches feeding a review queue. Do not page on them.
12

Hardening: Tiered

Immediate: deploy this week
  • H-I2 · Patch cPanel & WHM to the fixed build for your release tier. This is the whole ballgame. CVE-2026-41940 is CVSS 9.8, CWE-306, on the CISA KEV catalog since 2026-04-30 with a remediation due date of 2026-05-03 and a Known ransomware-use flag. Fixed builds are listed in the Patch & Exposure Audit block in section 10 and in playbook P1. MITRE M1051 (Update Software) · CISA KEV binding directive · vendor security update 2026-04-28.
  • H-N1 · Restrict the cPanel/WHM control-plane ports (2082, 2083, 2086, 2087) to trusted networks. The scanner explicitly targets this port set. A control plane that is not internet-reachable cannot be mass-exploited even while you finish patching. MITRE M1030 (Network Segmentation) · MITRE M1035 (Limit Access to Resource Over Network) · CIS Controls v8 4.4 / 4.5.
  • H-D1 · Block the campaign C2 and callback hostname at the egress proxy and in Falcon IOC Management. Both are single-purpose actor infrastructure. MITRE M1037 (Filter Network Traffic).
  • H-I1 · Require review for any change under .github/workflows. Enable branch protection with required reviewers and add a CODEOWNERS entry covering the workflows path on every repository. The campaign's entire initial access was an unreviewed push of workflow files. MITRE M1045 (Code Signing) / M1047 (Audit) · GitHub hardening guidance for Actions.
  • H-C1 · Rotate every secret reachable from any host that matched Q1, Q2, Q3, Q5 or Q9. Assume exfiltration; the payload targets AWS keys, GitHub/GitLab tokens, OpenAI and Google API keys, Stripe keys, SendGrid/Mailgun credentials, DB credentials and SSH material. MITRE M1027 (Password Policies) · M1026 (Privileged Account Management).
Near term: 1 to 4 weeks, pilot first
  • H-N2 · Set the organisation-wide default GITHUB_TOKEN permission to read-only and require workflows to opt in to write scopes explicitly. This caps what a poisoned workflow can do to your repos even when it runs. MITRE M1018 (User Account Management) · GitHub Actions security hardening guidance.
  • H-N3 · Restrict which Actions and reusable workflows may run in the org (allow-list local plus verified-creator actions only) and disable Actions entirely on repositories that have no CI. Fewer repositories that can run workflows means fewer that can be weaponised. MITRE M1042 (Disable or Remove Feature or Program).
  • H-N4 · Put self-hosted runners behind an egress allow-list and never attach them to public repositories. A self-hosted runner with unrestricted egress is a scanner waiting for a job. MITRE M1030 (Network Segmentation) · M1037 (Filter Network Traffic).
  • H-C2 · Remove long-lived static credentials from hosting servers. Move to short-lived, workload-scoped credentials (OIDC federation for cloud, deploy keys scoped to one repo, per-service DB users). The harvest only pays off because the secrets are static and long-lived. MITRE M1026 (Privileged Account Management) · M1041 (Encrypt Sensitive Information) · CIS Controls v8 5.2 / 6.5.
  • H-N5 · Mount /tmp with noexec,nosuid,nodev on servers and CI runners. The payload is dropped to /tmp and chmod-ed to 755; a noexec mount breaks that step outright. Pilot carefully: some package managers and installers use /tmp for executable scratch space. MITRE M1022 (Restrict File and Directory Permissions) · CIS Distribution Independent Linux Benchmark §1.1 (separate partition with noexec/nosuid/nodev for /tmp).
  • H-N6 · Enable GitHub secret scanning with push protection across the org, so a token harvested from a server and reused in a commit is caught. MITRE M1047 (Audit).
Strategic: 1 to 3 months
  • H-S1 · Treat CI configuration as production code. Workflow files execute with real access to build systems and secrets; put them under the same review, testing, change-control and monitoring you apply to application code. This is the structural lesson of the campaign, not an optional refinement. MITRE M1047 (Audit) · NIST SSDF PO.3 / PW.4 / PS.1 · SLSA build-integrity levels.
  • H-S2 · Pin dependencies to immutable commit references and stop consuming branch-based dev versions. The poisoned artefacts were dev-* branch versions that Packagist synced automatically; a lockfile pinned to a reviewed commit would not have moved. Apply the same rule to third-party Actions (pin to a full commit SHA, not a tag). MITRE M1051 (Update Software) · NIST SSDF PW.4.1 · SLSA provenance.
  • H-S3 · Move the hosting control plane behind a VPN or identity-aware proxy. cPanel and especially WHM are administrative control planes for many customer environments; they should not answer unauthenticated requests from the internet at all. MITRE M1030 (Network Segmentation) · M1032 (Multi-factor Authentication) · CIS Controls v8 12.7 / 13.4.
  • H-S4 · Build a CI egress monitoring capability. Route all runner traffic through a logged proxy and alert on connections to raw IP addresses, on newly-seen destinations, and on any egress from a job whose declared purpose is a build. MITRE M1037 (Filter Network Traffic) · M1047 (Audit). ⚠ best-practice, no formal benchmark
  • H-S5 · Inventory every secret that lives on a hosting server and drive that count toward zero via a secrets manager with short-lived leases. You cannot scope the blast radius of a server compromise you cannot enumerate. MITRE M1041 (Encrypt Sensitive Information) · CIS Controls v8 3.11.

What hardening cannot fix here: the scanning traffic originates from GitHub-hosted runner address space. Blocking source IPs would mean blocking GitHub, and the actor loses nothing when a runner is torn down. Patch state and control-plane exposure are the only variables you actually control on the victim side.

13

Deployable Playbooks

P1 · Patch and verify cPanel / WHM (Linux, root)

Prerequisites: root on the cPanel/WHM host; a current full backup or a VM snapshot; a maintenance window (upcp restarts panel services and briefly interrupts panel access); knowledge of your current release tier. Reboot required: no: upcp restarts cpsrvd and related services, but no host reboot is needed unless the update also pulls a kernel. Rollback: cPanel does not support downgrading across a release tier. Roll back by restoring the pre-update VM snapshot or backup taken in step 1: that snapshot is the rollback plan, so do not skip it.

# 1. Snapshot / back up FIRST. This is the only rollback path.
/usr/local/cpanel/scripts/pkgacct --skipbwdata USERNAME   # per-account, or take a VM snapshot

# 2. Record the current build so you can prove what changed.
/usr/local/cpanel/cpanel -V | tee /root/cpanel-version-before.txt

# 3. Confirm the update tier. RELEASE or STABLE both receive the fix; a frozen
#    or manually pinned tier will NOT.
grep -E '^(CPANEL|UPDATES)=' /etc/cpupdate.conf

# 4. Update.
/usr/local/cpanel/scripts/upcp --force

# 5. Verify against the fixed build for your tier (see section 10 audit block).
/usr/local/cpanel/cpanel -V | tee /root/cpanel-version-after.txt

# 6. Confirm the panel services came back.
systemctl status cpanel;  ss -ltnp | grep -E ':(2083|2087)\b'

P2 · Restrict the cPanel / WHM control plane to trusted networks (firewalld)

Prerequisites: root; firewalld active; the exact CIDR ranges your administrators connect from, confirmed in advance. Reboot required: no. Rollback: every command below has an explicit undo in the rollback block, run it to restore the previous state. Test from a second, already-open session before you close the first: locking yourself out of a remote host is the realistic failure mode here.

# Replace 198.51.100.0/24 with YOUR administrative range before running anything.
ADMIN_NET="198.51.100.0/24"

# 1. Create a trusted source zone and put the panel ports in it.
firewall-cmd --permanent --new-zone=cpanel-admin
firewall-cmd --permanent --zone=cpanel-admin --add-source="$ADMIN_NET"
for P in 2082 2083 2086 2087 2095 2096; do
  firewall-cmd --permanent --zone=cpanel-admin --add-port=${P}/tcp
done

# 2. Remove those ports from the public zone.
for P in 2082 2083 2086 2087 2095 2096; do
  firewall-cmd --permanent --zone=public --remove-port=${P}/tcp
done

# 3. Apply, then VERIFY FROM A SECOND SESSION before closing this one.
firewall-cmd --reload
firewall-cmd --zone=cpanel-admin --list-all
firewall-cmd --zone=public --list-ports

# --- ROLLBACK ---------------------------------------------------------------
# for P in 2082 2083 2086 2087 2095 2096; do
#   firewall-cmd --permanent --zone=public --add-port=${P}/tcp
# done
# firewall-cmd --permanent --delete-zone=cpanel-admin
# firewall-cmd --reload

P3 · Harden /tmp against payload execution (Linux servers and CI runners)

Prerequisites: root; /tmp already on its own filesystem, or willingness to move it to a tmpfs mount; a pilot ring: some package managers and installers execute from /tmp and will break. Reboot required: no if you remount live; yes to prove the fstab change survives a boot, which you should do in the pilot ring. Rollback: restore the saved /etc/fstab copy and remount with exec, shown in the rollback block.

# 1. Back up fstab. This is the rollback artefact.
cp -a /etc/fstab /root/fstab.bak-$(date +%F)

# 2. Check whether /tmp is already a separate filesystem.
findmnt /tmp

# 3a. If /tmp IS a separate mount, add the options.
sed -i.huntpack -E 's|^(\S+\s+/tmp\s+\S+\s+)([^ ]+)|\1\2,noexec,nosuid,nodev|' /etc/fstab

# 3b. If /tmp is NOT separate, mount it as tmpfs instead.
# echo 'tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev,size=2G 0 0' >> /etc/fstab

# 4. Apply live and verify.
mount -o remount /tmp
findmnt -no OPTIONS /tmp     # expect noexec,nosuid,nodev present

# 5. Prove the control works: this must now FAIL.
cp /bin/true /tmp/.hp-test && chmod 755 /tmp/.hp-test && /tmp/.hp-test; echo "exit=$?"
rm -f /tmp/.hp-test

# --- ROLLBACK ---------------------------------------------------------------
# cp -a /root/fstab.bak-YYYY-MM-DD /etc/fstab
# mount -o remount,exec /tmp
# findmnt -no OPTIONS /tmp

P4 · Lock down GitHub Actions at the organisation level

Prerequisites: org owner rights; gh authenticated with admin:org scope; agreement from engineering leads, because read-only default token permissions will break pipelines that silently relied on write access. Reboot required: none: these are API-side settings, effective on the next workflow run. Rollback: each setting has an inverse API call in the rollback block; capture the current values in step 0 first so you can restore exactly what was there.

ORG="YOUR_ORG"

# 0. CAPTURE CURRENT STATE FIRST - this is your rollback record.
gh api "orgs/$ORG/actions/permissions"                  | tee /tmp/gh-actions-before.json
gh api "orgs/$ORG/actions/permissions/workflow"         | tee -a /tmp/gh-actions-before.json

# 1. Default GITHUB_TOKEN permissions to read-only; workflows must opt in.
gh api -X PUT "orgs/$ORG/actions/permissions/workflow" \
  -f default_workflow_permissions=read \
  -F can_approve_pull_request_reviews=false

# 2. Allow only local + verified-creator actions.
gh api -X PUT "orgs/$ORG/actions/permissions" \
  -f enabled_repositories=all -f allowed_actions=selected
gh api -X PUT "orgs/$ORG/actions/permissions/selected-actions" \
  -F github_owned_allowed=true -F verified_allowed=true -f 'patterns_allowed[]='

# 3. Require review on the workflows path in each repo (CODEOWNERS).
#    Commit this to .github/CODEOWNERS on the default branch:
#    /.github/workflows/  @YOUR_ORG/ci-reviewers

# 4. Verify.
gh api "orgs/$ORG/actions/permissions/workflow"

# --- ROLLBACK ---------------------------------------------------------------
# Replay the values captured in /tmp/gh-actions-before.json:
# gh api -X PUT "orgs/$ORG/actions/permissions/workflow" \
#   -f default_workflow_permissions=write -F can_approve_pull_request_reviews=true
# gh api -X PUT "orgs/$ORG/actions/permissions" -f allowed_actions=all

P5 · Falcon-side blocking (IOC Management + Custom IOA)

Prerequisites: Falcon role with IOC Management write and Custom IOA authoring; a chosen host group for the IOA rule group; agreement that prevention (not just detection) is wanted. Reboot required: none. Rollback: disable the IOA rule group (do not delete it: you lose the tuning history) and set the IOC actions back to no_action; both undos are shown below.

# 1. Import the atomic indicators (section 10 CSV) via
#    Endpoint security > IOC management > Add indicators > Upload CSV.
#    Set: ipv4 + domain + sha256 -> Prevent/Block, severity Critical.
#    Set: url rows -> Detect (URL matching depends on module licensing).

# 2. Create the IOA rule group.
#    Endpoint security > Custom IOA rule groups > Create rule group
#      Platform: Linux    Name: GHActions-cPanel-Campaign
#
#    IOA-1  Process Creation  ACTION: Block Execution   SEVERITY: Critical
#      Command Line.*\bipscan\b.*
#      Command Line.*(--exploit|--envdump|--git-workers|--no-reverse).*
#
#    IOA-2  Process Creation  ACTION: Detect            SEVERITY: High
#      Parent Image Filename.*\x2f(cpsrvd|cpsrvd-ssl|whostmgrd|cpdavd)$
#      Image Filename.*\x2f(sh|bash|dash|perl|python3?|curl|wget|nc|socat)$
#
#    IOA-3  Process Creation  ACTION: Detect            SEVERITY: High
#      Image Filename.*\x2ftmp\x2f\.[A-Za-z0-9_-]{1,12}$

# 3. Assign the rule group to a PILOT host group first. Promote to the full
#    Linux group only after Gate 2 baselining in section 11 is clean.

# --- ROLLBACK ---------------------------------------------------------------
# Custom IOA rule group -> toggle Enabled = off (keep the group; deleting it
#   discards every exclusion you tuned).
# IOC management -> select the campaign tag -> set action to "No action".
14

Containment Runbook

PhaseActionsOwnerEvidence to capture
0 · Triage
0–30 min
Confirm the hit is campaign-related: run Q1 and Q2 scoped to the host. Determine the host's role: cPanel/WHM server, self-hosted runner, or developer workstation, because the response diverges immediately after this step. SOC L2 Falcon detection ID, aid, full process tree, matching command lines
1 · Isolate
0–1 hr
Network-contain the host in Falcon. If it is a self-hosted runner, deregister it from the org and drain its job queue. If it is a cPanel/WHM server, restrict the panel ports to admin ranges (playbook P2) rather than taking hosting offline, unless the compromise is confirmed. Suspend any GitHub repository actively running the poisoned workflow. SOC L2 + platform owner Containment timestamp, runner registration record, repo and workflow-run URLs
2 · Scope
1–4 hrs
Run Q1 and Q6 estate-wide over 90 days to find every other host that touched the C2 or its API paths. Run Q8 for other hosts exhibiting panel-port fan-out. Run native hunt N2 across your GitHub org to find every poisoned workflow file. Run N5 to find every project carrying an affected package version. Threat hunting Host list with first/last contact times; list of repos and workflow files; lockfile inventory
3 · Eradicate
4–24 hrs
Delete the malicious workflow files and the branches that carried them, but preserve commits and Actions run logs first, deleting them destroys the only record of what ran. Remove the dropped binary and capture its hash before deletion. On cPanel/WHM hosts: patch (playbook P1), then run the vendor IOC detection script, then invalidate every panel session and purge /var/cpanel/sessions/ after evidence capture. IR + platform owner Binary sample and SHA-256, workflow file contents, Actions run logs, session-file archive from native hunt N4
4 · Rotate
24–72 hrs
Rotate every credential the affected host or pipeline could reach, on the assumption they left: AWS keys, GitHub/GitLab tokens (including any PAT or deploy key on the host), OpenAI and Google API keys, Stripe keys, SendGrid/Mailgun credentials, database credentials, SSH private keys and their authorized_keys entries. Review OAuth apps and GitHub App installations for grants you did not make. Revoke, then reissue, do not merely reissue. IAM + cloud + app owners Rotation ledger with per-credential timestamps; OAuth/App grant diff
5 · Recover
3–7 days
Rebuild self-hosted runners from a known-good image rather than cleaning them; an ephemeral runner is cheaper to replace than to trust. Repin composer.lock to stable releases or commit references predating 2026-07-12 and re-resolve. Restore panel-port access only after patch verification. Return contained hosts to production behind the near-term hardening in section 12. Platform + engineering Rebuild records, lockfile diffs, post-patch cpanel -V output
6 · Watch
30 days
Keep Q1, Q2, Q5, Q6 and Q9 running as scheduled searches. Treat the operation as ongoing: the maintainer account was suspended but forks, mirrors, cached snapshots, stolen credentials and the C2 may all remain live. Re-run native hunt N2 weekly against your org. SOC Weekly hunt results, scheduled-search hit counts

Do not skip evidence capture to move faster. Deleting a poisoned workflow file and its Actions run log is the single most common way an organisation loses the ability to answer "what secrets did it read". Capture first, then eradicate.

15

Detection Coverage Map

TechniqueBehaviourCQLIOA / IOCCoverage
T1584.006Compromised repos + hosted runners as attack infrastructurenonenoneGAP: no EDR reach into github.com; covered only by native hunts N1/N2
T1195.001Malicious workflow files pushed into .github/workflowsQ12nonePartial, only where the write happens on an instrumented host
T1195.001Poisoned Packagist dev versions in a lockfilenoneAudit blockPartial: no telemetry; dependency audit N5 only
T1082Runner CPU architecture detectionQ11nonePartial, self-hosted runners only
T1105ELF payload download from the C2Q1, Q6, Q7IOC (ipv4, url)Good
T1105Payload written to /tmp/.svc and chmod 755Q3, Q4IOA-3, IOC (sha256)Good, amd64 by hash, all builds behaviourally
T1059.004Unix shell execution inside the workflow run stepQ5, Q7, Q11IOA-1Good on instrumented hosts; GAP on GitHub-hosted runners
T1595.002Internet-wide vulnerability scanningQ5, Q8IOA-1Good
T1190CVE-2026-41940 exploitation against cPanel/WHMQ9IOA-2Partial: the bypass itself is invisible to EDR; only the post-exploitation child process and the cPanel logs (N3/N4) show it
T1496.002Bandwidth/compute hijacking of Actions runnersQ8nonePartial, inferred from fan-out, not measured directly
T1552.001 / T1005 / T1119Credential and config file harvestingQ10nonePartial, high FP, correlation input only
T1074.001Results staged in local output files on the runnernonenoneGAP: staging happens on ephemeral hosted runners
T1071.001HTTP POST C2 on tcp/80Q1, Q6IOC (ipv4, url)Good
T1071.004DNSHook out-of-band callbackQ2IOC (domain)Good
T1020 / T1041Chunked, offset-tracked exfiltration over the C2 channelQ1, Q6IOC (url)Good

Known gaps and what to do about them

  • GitHub-hosted runners are unreachable. Three chain steps (3, 4 and part of 8) execute on third-party ephemeral compute. Nothing in Falcon will ever see them. Native hunts N1 and N2 plus the org-level controls in playbook P4 are the entire coverage, and they are preventative and retrospective rather than detective.
  • The authentication bypass leaves no EDR signal. CVE-2026-41940 is exploited inside the cpsrvd process; nothing spawns until the attacker acts. Q9 catches what they do next, and native hunts N3/N4 catch the on-disk residue. If the attacker only reads data through the panel API, neither will fire, which is why patching (H-I2) is not optional and cannot be substituted with detection.
  • Only one payload hash is public. Three of four architecture builds have no published hash. Do not fill that gap with an invented hash; Q4 through Q7 cover them behaviourally.
  • Data staging on the runner is unobservable. Accepted gap. Exfiltration is still caught at the network layer by Q1 and Q6.

Validation gates for all of the above are in section 11: confirm Linux telemetry is actually flowing (Gate 1) before reading any empty result as a clean result, baseline the six noisy queries for 30 days (Gate 2), run the safe positive tests (Gate 3), then promote Q5, Q9 and Q4 in that order (Gate 4).

16

Hunt Summary Ticket

TITLE:      GitHub Actions Abuse - Distributed cPanel/WHM Exploitation (CVE-2026-41940)
SEVERITY:   Critical - CVSS 3.1 9.8, CISA KEV since 2026-04-30, known ransomware use

SCOPE:      1) Every internet-facing cPanel or WHM server (victim side)
            2) Every self-hosted GitHub Actions runner (attacker-infrastructure side)
            3) Every GitHub repository owned by the org (poisoned workflow search)
            4) Every PHP/Composer project with a branch-based dev dependency

HYPOTHESIS: Poisoned .github/workflows files execute on ephemeral runners, pull an
            architecture-matched Linux ELF from 43.228.157.68 to /tmp/.svc, scan the
            internet for cPanel/WHM on 2082/2083/2086/2087, exploit CVE-2026-41940,
            harvest cloud + payment + source-control credentials, and exfiltrate them
            in chunked HTTP POSTs with a 30-second heartbeat.

QUERIES:    Q1  C2 contact 43.228.157.68                      CONF high  FP low
            Q2  DNSHook callback resolution                    CONF high  FP low
            Q3  Payload SHA-256 (amd64 build)                  CONF high  FP low
            Q4  Dot-prefixed binary executed from /tmp         CONF high  FP med
            Q5  Scanner argv (ipscan --exploit --envdump)      CONF high  FP low
            Q6  C2 API paths on a command line                 CONF high  FP low
            Q7  curl/wget from raw IPv4 over http into /tmp    CONF med   FP med
            Q8  Panel-port fan-out (2082/2083/2086/2087)       CONF high  FP med
            Q9  cpsrvd/whostmgrd spawning a shell              CONF high  FP low
            Q10 Credential material enumeration                CONF med   FP high
            Q11 Self-hosted runner spawning network tooling    CONF med   FP med
            Q12 Bulk .github/workflows manipulation           CONF med   FP high

DO FIRST:   1. Patch cPanel/WHM to the fixed build for your tier (playbook P1).
               KEV due date was 2026-05-03 and is long past.
            2. Run Q1 and Q2 estate-wide at 90 days.
            3. Run Q9 across every hosting server at 30 days.
            4. Search the GitHub org for campaign markers (native hunt N2).
            5. Restrict panel ports 2082/2083/2086/2087 (playbook P2).

FINDINGS:   [ ] Hosts contacting the C2:            ____
            [ ] Hosts resolving the DNSHook host:   ____
            [ ] Payload hash sightings:             ____
            [ ] cPanel servers below the fixed build: ____
            [ ] Poisoned workflow files in-org:     ____
            [ ] Projects carrying an affected dev version: ____

GAPS:       - GitHub-hosted runners: no EDR coverage possible, ever.
            - The auth bypass itself is invisible to EDR; only post-exploitation
              child processes and cPanel session/login logs show it.
            - Only the amd64 payload hash is public; 386/arm/arm64 covered
              behaviourally by Q4-Q7. No hash was invented for them.
            - Data staging on the runner is unobservable. Accepted.

ACTIONS:    - Promote Q5 to a blocking Custom IOA (IOA-1) immediately.
            - Promote Q9 to IOA-2 at Detect; Block after two clean weeks.
            - Promote Q4 to IOA-3 after excluding build agents.
            - Block the C2 IP, callback hostname and payload hash in IOC Management.
            - Set org default GITHUB_TOKEN permissions to read-only (playbook P4).
            - Rotate every secret reachable from any matching host. Assume exfil.

OWNER:      SOC threat hunting / platform engineering (joint)
VERSION:    v0.1 - 2026-07-31 - HuntPack
17

Changelog

v0.12026-07-31Initial pack. Seven source snapshots saved and scanned for injected instructions (none found). Twelve CQL queries covering both the victim side (cPanel/WHM exploitation and post-exploitation) and the attacker-infrastructure side (poisoned CI runners). Ten atomic indicators, every one traced to a saved snapshot. Three Custom IOA candidates. Five deployable playbooks with prerequisites, reboot and rollback. The cPanel vendor advisory returned HTTP 403 and was therefore dropped as a cited source; fixed build numbers come from the snapshotted NVD affected-version matrix instead.
18

References

TierSourceUsed forAccessed
1Socket: Large-Scale GitHub Actions Abuse Powers a Distributed cPanel and WHM Exploitation Campaign (2026-07-22)Primary technical writeup: workflow structure, payload URLs, scanner argv, heartbeat and exfil endpoints, DNSHook hostname, payload hash, ATT&CK mapping, full IOC set2026-07-31
1NVD: CVE-2026-41940CVSS 3.1 9.8 / CVSS 4.0 9.3, CWE-306, affected and fixed build matrix for cPanel, WHM and WP Squared2026-07-31
1CISA Known Exploited Vulnerabilities CatalogKEV entry: added 2026-04-30, due 2026-05-03, known ransomware campaign use2026-07-31
2watchTowr Labs: cPanel / WHM authentication bypass analysisRoot-cause mechanism and the on-disk forensic artefacts used in native hunts N3/N4. Exploitation steps and the linked PoC are deliberately not reproduced in this pack2026-07-31
2The Hacker News: Attackers weaponize GitHub Actions (2026-07-23)Independent corroboration of file counts, timeline, C2 address and DNSHook identifier2026-07-31
2Cyber Security News: Hackers abuse GitHub Actions to exploit cPanel and WHM servers (2026-07-23)Second attestation of the complete IOC table including the drop path2026-07-31
2SecurityOnline: GitHub Actions abuse, cPanel and WHM exploitationCampaign scale framing, attribution caveat, panel-port restriction guidance2026-07-31
2cPanel & WHM Security Update 04-28-2026 (support.cpanel.net)Not cited as evidence: returned HTTP 403 to every fetch attempt, so no snapshot exists. Listed only so the reader knows the vendor advisory exists and should be consulted directly for the current IOC detection script2026-07-31 (unfetchable)
RefMITRE ATT&CK: T1195.001, T1584.006, T1059.004, T1105, T1082, T1496.002, T1595.002, T1190, T1552.001, T1005, T1119, T1074.001, T1071.001, T1071.004, T1020, T1041 and mitigations M1018, M1022, M1026, M1027, M1030, M1032, M1035, M1037, M1041, M1042, M1045, M1047, M1051Technique and mitigation mapping2026-07-31

Provenance: the fetched plain text of every cited source above is saved verbatim in GitHubActions-cPanel-CVE-2026-41940-Hunt-sources/ (7 files). Every atomic indicator in section 10 traces back to at least one of those snapshots. Nothing in this pack is an indicator that no source published.