Skip to main content
rfxn
//
cpanelcveincident-responsekill-chainiocbattle-journal

Field Notes: A Week of CVE-2026-41940 Exploitation in the Wild

Ryan MacDonald15 min read

This is a field journal of what we observed during the first week of CVE-2026-41940 exploitation in the wild. The companion to this piece is our reverse-engineering write-up of the underlying primitive and the patch that closes it. That article describes what the bug is; this one describes what we saw it do. We are deliberately not naming providers, victims, or quantifying scope. The IOCs, attacker IPs, operator tells, and command patterns are the parts a defender actually needs.

The journal is structured chronologically through the 04-11 → 05-02 window: a 17-day quiet probing arc, then vendor disclosure, then a 72-hour exploitation surge once a public PoC dropped. A worked-example kill-chain analysis from the on-host scanner output sits in the middle. The pattern catalog at the end is the version we are running detection against today, and the actionable section after it has the mitigation tooling and the high-confidence block list ready to push.

Source· GPL v2· Issues open

github.com/rfxn/cpanel-sessionscribe

Canonical source for sessionscribe-ioc-scan.sh, sessionscribe-mitigate.sh, sessionscribe-remote-probe.sh, sessionscribe-revsnap.sh, and the ModSecurity rule pack referenced throughout this journal. Mirror of the sh.rfxn.com one-liners with history, issues, and tags.

View on GitHub
main
Battle Journal·2026-04-11 → 2026-05-02 (UTC)

CVE-2026-41940 (cPanel/WHM SessionScribe) — week-1 field notes

First probe
2026-04-11T05:32Z
Private disclosure
2026-04-28T17:05Z
Public CVE
2026-04-29T19:46Z
Most recent event
2026-05-02T11:33Z

TL;DR

Three things stood out in the data once the dust settled:

  • The campaign is layered, not parallel. Every confirmed compromise we examined ran the same upstream chain (Pattern X → D → E → F). Only the destructive terminal stage diverged. That is one toolkit shared across multiple operators.
  • Multiple operators, deterministic tells. Three distinct websocket-Shell terminal dimensions (24×80, 24×120, 24×134) plus four UA strings give us reliable per-operator attribution from the same per-host evidence.
  • Pre-disclosure evidence merits investigation. At least one host shows a websocket-Shell hit dated four months before the public CVE. Whether this is genuine pre-disclosure exploitation, a forged session timestamp, or an artifact of our scanner's clamping logic is the most important open question of the week.

The First Probe

The earliest Pattern X event we have on disk lands on 2026-04-11 at 05:32 UTC. One IP, one host, the canonical badpass session shape: a session file written through saveSession() with a multi-line pass= field, an injected cp_security_token=/cpsess[N], and tfa_verified=1 without a real login. That is the bug working.

The probing was paced. Same IP would return on a different day, walk a small set of unrelated targets inside a six-hour window, then disappear. The targets had nothing in common except running a vulnerable cPanel/WHM version. This is not a scanner spraying the internet; this is an actor with a working primitive walking a target list.

Probe2026-04-11T05:32ZFirst Pattern X event observed
Single IP, single host, canonical badpass shape. Session file written verbatim through saveSession(); no other stage activity for the rest of the day.

The Quiet Window

2026-04-12 → 2026-04-27

For the next sixteen days the same shape repeats. Pattern X events arrive in small daily batches with a peak around 11/day across the population we monitor. None of these probes translate into Pattern D recon or any post-attack stage during the window. Either the actor was characterizing the bug without using it, or the activity-stage code was being held back deliberately.

Two things distinguish this window from generic scanner noise:

  • Surgical target selection. An operator walking a list rather than a scanner sweeping a /16: small host sets, no obvious shared identifier, paced in blocks.
  • Single-IP recurrence. The same source IPs return across multiple days, against partially overlapping target sets. A scanner moves on.
Probe2026-04-15Repeat actor returns
Same source IP from 04-11 revisits a different host set inside a roughly six-hour block. No interactive stage follows. Pacing rules out an automated scanner.
Probe2026-04-23Multi-IP cluster, identical session shape
Five distinct source IPs hit different hosts in the same 12-hour window, each producing identical session-file fingerprints. Session shape is now a reliable IOC; the source IPs are not yet attribution.

Pre-Disclosure Evidence

One host's session corpus contains a Pattern E websocket-Shell hit dated 2025-12-22 from an IP that returns in the post-disclosure data with the same 24×134 fingerprint, attributing the early hit to the same operator (Operator C) we observe later in the window. The dimensions are rows=24&cols=134 , the third operator fingerprint in our taxonomy below. If the timestamp is real, it places at least one operator inside the primitive roughly four months before the public CVE.

Disclosure & Public CVE

Day 17 reframes everything that came before it. The vendor notified providers privately on 2026-04-28 at 17:05 UTC, and a public CVE plus a working PoC from watchTowr Labs landed about 27 hours later, on 04-29 at 19:46 UTC. The 27-hour gap is the entire defensive window any provider had to rollout a mitigation before commodity exploitation began.

Disclose2026-04-28T17:05ZVendor private disclosure
cPanel notifies providers privately of CVE-2026-41940. Patch available; public advisory still embargoed.
Defense2026-04-28T20:00ZFirst defensive wave
ModSecurity rule pack and a CSF/APF cpsrvd-port scrub start rolling. Pattern X surface is closed off at the WAF layer ahead of the public PoC.
Disclose2026-04-29T19:46ZPublic CVE + PoC published
CVE-2026-41940 hits the wire. Working PoC from watchTowr published the same day. Probe activity converts to commodity exploitation traffic within hours.

The Floodgates

2026-04-30 → 2026-05-02

The post-CVE window is what most defenders will see in their own logs. Pattern X events, which had been single-digit per day during the quiet window, jump by more than an order of magnitude. Pattern D recon traffic, which we had not seen at all pre-disclosure, appears immediately and consistently from the same Go-http-client UA against any host that does not have the WAF rules in place.

The destructive stages (A, B, C, H) cluster on day 04-30, which is the day after the public CVE and the first day of generalized operator activity. Hosts that had defenses landed before 04-29 19:46 UTCtook Pattern X attempts but no post-attack stages. Hosts that had not yet been reached by the rollout are where the destructive payloads landed.

Compromise2026-04-30Destructive payloads cluster
Patterns A, B, C, and H all appear on the same day. The upstream chain is uniform; only the terminal stage differs by operator. Same host had A and C stacked in at least one case we examined.
Probe2026-05-01Probe-traffic peak
Highest single-day Pattern X volume of the window. Floor of commodity exploitation; very little of it converts to successful destructive stage because the WAF rollout has largely caught up by this point.
Probe2026-05-02T11:33ZMost recent Pattern X event
Latest forged-session activity captured at the time of writing. The campaign is still active; this article is a snapshot of an ongoing investigation, not a post-mortem.

Worked Kill Chain — One Host, One Day

The most useful thing we can hand to another defender is a walked-through kill chain on a single compromised host, with the artifacts an on-host scanner would surface at each stage. Below is sanitized output from sessionscribe-ioc-scan.sh on a host that took the full chain. Hostnames and account names are redacted; everything else is real.

bash
# sessionscribe-ioc-scan.sh on Host A — 2026-04-30 06:18 UTC
[+] CVE-2026-41940 IOC scanner v2.5.0

[stage 1/7] PATTERN X — initial CRLF Authorization
  /var/cpanel/sessions/raw/******.scribe
    token_denied=1
    cp_security_token=/cpsess1234567890
    origin_as_string=address=192.81.219.190,app=whostmgrd,method=badpass
    tfa_verified=1
    pass= (multi-line value, CRLF injection confirmed)
  VERDICT: COMPROMISED (1 session file matches)

[stage 2/7] PATTERN D — JSON-API enumeration
  access_log: /json-api/version,gethostname,listaccts,getdiskusage,getips
  user-agent: Go-http-client/1.1
  fileman API: /etc/shadow,/etc/passwd,/root/.ssh/id_rsa{,.pub},
               /root/.aws/credentials,/root/.bash_history
  VERDICT: RECON CONFIRMED

[stage 3/7] PATTERN D — Reseller persistence
  /var/cpanel/accounting.log:
    CREATEAPITOKEN:root:root:not-applicable:WHM_FullRoot
    CREATE:root:root:4ef72197.cpx.local:96.30.39.236:sptadm
    ADDRESELLER:root:root:4ef72197.cpx.local:sptadm
  IOC: sptadm reseller account, WHM_FullRoot API token (revoke separately)

[stage 4/7] PATTERN G — SSH key persistence
  3 non-standard ssh-rsa keys planted under /root/.ssh, mtime forged to
  2019-12-13 12:59:16, ctime 2026-04-30 06:14:22 (real)
  IOC: IP-labeled keys 209.59.141.49, 50.28.104.57

[stage 5/7] PATTERN E — websocket Shell
  GET /cpsess1234567890/websocket/Shell?rows=24&cols=80 HTTP/1.1
  source: 192.81.219.190 (Operator A, Go-http-client UA family)

[stage 6/7] PATTERN F — automated harvester envelope
  /root/.bash_history:
    printf '__S_MARK__'; cat /etc/shadow; printf '__E_MARK__'
    printf '__S_MARK__'; for f in /root/.ssh/id_*; do cat "$f"; done; printf '__E_MARK__'
    printf '__S_MARK__'; find /root /home -maxdepth 3 -name '.bash_history' \
                          -exec cat {} \;; printf '__E_MARK__'
  VERDICT: AUTOMATED AGENT (~5h after stage 1)

[stage 7/7] DESTRUCTIVE PAYLOAD — Pattern A (.sorry encryptor)
  /root/sshd  sha256: 2fc0a056fd4eff5d31d06c103af3298d711f33dbcd5d122cae30b571ac511e5a
  C2: 68.183.190.253
  README.md (in user homes): qTox ID 3D7889AEC00F2325E1A3FBC0ACA4E521670497F11E47FDE13EADE8FED3144B5EB56D6B198724
  encrypted extension: .sorry (system files included — restore-in-place will not recover)

[7/7 stages confirmed]  Confidence: HIGH
[recommendation] reimage required; rotate every credential touched in stage 2/3/4

Rendered as a chain:

Kill Chain· Pattern X → D → G → E → F → destructive

  1. 01Access

    Pattern X — Initial CRLF Authorization access

    An Authorization: Basic header lands on an existing session with a multi-line value. saveSession() writes it verbatim, and the resulting session file gets a token_denied=1 with an injected cp_security_token=/cpsess[N], plus origin_as_string carrying the attacker IP, and tfa_verified=1 despite no real login.

    • regex^pass=.*\n.
    • idtoken_denied=1 + cp_security_token=/cpsess[N]
    • idorigin_as_string=address=<IP>,app=whostmgrd,method=badpass
  2. 02Recon

    Pattern D — JSON-API enumeration

    Once the forged cpsess token is in hand, an automated Go-http-client agent walks /json-api/* in a deterministic order: version, gethostname, listaccts, getdiskusage, systemloadavg, getips. It then reads /etc/shadow, /etc/passwd, the full ~/.ssh key set, and /root/.aws/credentials via the Fileman API.

    • uaGo-http-client/1.1
    • cmd/json-api/listaccts
    • file/etc/shadow, /etc/passwd, /root/.ssh/id_*
  3. 03Persistence

    Pattern D — Reseller-as-persistence

    Same recon agent then issues /json-api/createacct + setupreseller + setacls + setresellerlimits, leaving a sptadm reseller with all-ACLs and a WHM_FullRoot API token. The token survives the cPanel patch and is the operator's way back in after remediation.

    • idusername=sptadm
    • iddomain=4ef72197.cpx.local
    • idcontactemail=a@exploit.local
    • cmdCREATEAPITOKEN ... WHM_FullRoot
  4. 04Persistence

    Pattern G — SSH key persistence (parallel layer)

    Non-standard ssh-rsa keys planted across /root/.ssh, /etc, and cron paths, with mtimes forged to 2019-12-13 to blend with provisioning artifacts. Comments include IP-labeled keys mimicking provider internal-key style. ctime gives them away; touch can backdate mtime and atime, not ctime.

    • cmdfind /root /etc /var/spool/cron -type f -exec grep -l 'ssh-rsa'
    • regex^[0-9.]{7,15} ssh-rsa
  5. 05Interactive

    Pattern E — Interactive websocket Shell

    Operator pivots from JSON-API into the WHM in-browser shell at /cpsess[N]/websocket/Shell. Different operators run with different terminal dimensions (24×80, 24×120, 24×134) which act as a deterministic actor fingerprint when the shell traffic is otherwise indistinguishable.

    • regexGET /cpsess[0-9]+/websocket/Shell\?rows=
    • idrows=24&cols=80 (operator A)
    • idrows=24&cols=120 (operator B)
    • idrows=24&cols=134 (operator C)
  6. 06Harvester

    Pattern F — Automated agent harvester

    Inside the websocket shell a follow-up tool wraps every command with __S_MARK__/__E_MARK__ delimiters and harvests SSH keys, /etc/shadow (twice; likely retried), and every shell history file under /root and /home. The wrapper is the strong actor tell: a human does not type printf '__S_MARK__'; cmd; printf '__E_MARK__'.

    • regexprintf '__S_MARK__'.*printf '__E_MARK__'
    • cmdfind /root /home -maxdepth 3 -name '.bash_history'
  7. 07Destructive

    Destructive payload — multiple variants

    Terminal stage. Different operators on the same host have chosen different destructive payloads in the same window. The upstream chain (X → D → E → F) is identical across them; only the final stage diverges.

    1. 7.1Destructive

      Pattern A — .sorry encryptor + qTox ransom

      Encryptor binary masquerading as /root/sshd; encrypts user files plus system files (so an attempted in-place restore does not recover); drops README.md with a TOX ID; C2 over a single IP.

      • file/root/sshd (masquerades as ssh daemon)
      • hash2fc0a056fd4eff5d31d06c103af3298d711f33dbcd5d122cae30b571ac511e5a
      • ip68.183.190.253 (C2)
      • idqTox ID 3D7889AEC00F2325E1A3FBC0ACA4E521670497F11E47FDE13EADE8FED3144B5EB56D6B198724
    2. 7.2Destructive

      Pattern B — DB wipe + index.html ransom note

      Drops a BTC-ransom note in every /home/*/public_html/index.html (and nested directories), removes /var/lib/mysql/mysql, breaking MariaDB. Files are NOT encrypted; restore-from-backup recovers cleanly. Simpler stage than Pattern A.

      • idBTC bc1q9nh4revv6yqhj2gc5usncrpsfnh7ypwr9h0sp2
      • cmdrm -rf /var/lib/mysql/mysql
      • regexto recover your files, kindly send 0\.1 BTC
    3. 7.3Destructive

      Pattern C — Mirai / nuclear.x86 cryptominer

      Mirai-family dropper fetched from a hosting-redirector domain; binary lands at well-known paths and persistence is set via cron and systemd. Largest commodity-malware bucket of the campaign by host count.

      • filenuclear.x86 (Mirai variant)
      • ip87.121.84.78 (binary host)
      • idraw.flameblox.com (C2)
    4. 7.4Destructive

      Pattern H — seobot.php SEO defacement

      Per-site PHP webshell drop into every public_html, plus a competitor-kill bash_history (pkill -9 nuclear.x86 kswapd01 xmrig) and an ALLDONE marker. Confirms live cross-actor competition for the same vulnerable cohort.

      • file*/public_html/seobot.php
      • regexpkill -9 (nuclear\.x86|kswapd01|xmrig)
      • regexecho ALLDONE
ExpandRaw sessionscribe-ioc-scan.sh v2.5.0 sectioned report (anonymized, ANSI stripped)

Real stderr output as rendered by the scanner's sectioned-report mode (default). Hostname, account names, session-file basename, and one source IP have been redacted; everything else is on-disk evidence. Section IDs (version, cpsrvd, iocscan, sessions, destruct) match the SECTION_ORDER array in the script.

bash
== version == cpanel -V vs published patched-build cutoffs
  [OK]    cpanel -V parsed: 11.130.0.18 (tier 130, build 18)
  [FAIL]  vendor cutoff for tier 130 is .19; this host is one build behind
  code_verdict: VULNERABLE

== patterns == static config-file patterns (ancillary; not CVE-driver)
  [OK]    /var/cpanel/cpanel.config: ProxyPass.cpanel = on
  [OK]    /etc/apache2/conf.d/modsec2.user.conf: 1500030 present
  [OK]    /etc/apache2/conf.d/modsec2.user.conf: 1500031 present

== cpsrvd == cpsrvd binary patch markers
  [WARN]  cpsrvd binary mtime predates vendor patch window
  [WARN]  filter_sessiondata symbol present; saveSession() path NOT routed
          through filter (matches pre-patch primitive)

== iocscan == access_log scan over 30d window
  [IOC]   192.81.219.190  badpass exploit  hits=14  2xx=11  ua=Go-http-client/1.1
  [IOC]   38.146.25.154   /json-api/createacct  hits=3   2xx=3   ua=Go-http-client/1.1
  [IOC]   192.81.219.190  /cpsess[REDACTED]/websocket/Shell?rows=24&cols=80
                          hits=2   ua=(none)
  [WARN]  attacker-IP traffic during recon-window (30d) — escalates SUSPICIOUS

== sessions == session-store IOC ladder (vendor + CVE-2026-41940 ladder)
  scanned 47 session files under /var/cpanel/sessions/raw/
  [IOC]   [REDACTED].scribe : multi-line pass= field (CRLF injection)
  [IOC]   [REDACTED].scribe : token_denied=1 + injected cp_security_token=/cpsess[REDACTED]
  [IOC]   [REDACTED].scribe : origin_as_string=address=192.81.219.190,app=whostmgrd,method=badpass
  [IOC]   [REDACTED].scribe : tfa_verified=1 with no real login origin
  [ALERT] 4-of-4 CVE-2026-41940 co-occurrence on 1 session file
  [OK]    no PROBE_ARTIFACT canary — these are not from sessionscribe-remote-probe.sh
  host_verdict: COMPROMISED (1 session matches; 4-of-4 ladder)

== destruct == destruction IOC scan (Patterns A-I)
  [IOC]   Pattern D : /var/cpanel/accounting.log lines for sptadm reseller +
                     WHM_FullRoot CREATEAPITOKEN (timestamps 2026-04-30 06:18-06:21Z)
  [IOC]   Pattern G : 3 non-standard ssh-rsa keys in /root/.ssh/authorized_keys
                     mtime 2019-12-13 (forged), ctime 2026-04-30 06:14:22 (real)
  [IOC]   Pattern E : websocket Shell hit on 04-30 06:22Z, dimensions 24x80
  [IOC]   Pattern F : __S_MARK__/__E_MARK__ envelopes in /root/.bash_history
                     (15 wrapped commands, ~5h after stage 1)
  [IOC]   Pattern A : /root/sshd present
                     sha256 2fc0a056fd4eff5d31d06c103af3298d711f33dbcd5d122cae30b571ac511e5a
                     C2 reachability test (-Z dryrun): 68.183.190.253 reachable
  [OK]    Pattern B : no /var/lib/mysql/mysql removal
  [OK]    Pattern C : no nuclear.x86, no Mirai-family persistence
  [OK]    Pattern H : no seobot.php in any docroot

== probe == localhost marker probe
  [SKIP]  --probe not requested

==============================================================================
Summary matrix
  version  : VULNERABLE  (tier 130 .18 vs cutoff .19)
  cpsrvd   : VULNERABLE  (saveSession path not filtered)
  iocscan  : COMPROMISED (Pattern X attacker IP, Pattern E pivot)
  sessions : COMPROMISED (4-of-4 ladder match on 1 session file)
  destruct : COMPROMISED (Patterns D+E+F+G+A confirmed; Pattern B/C/H clean)
  probe    : SKIP

code_verdict : VULNERABLE
host_verdict : COMPROMISED  (compromised host can also be vulnerable;
                              patch + mitigate after IR cleanup)
exit code    : 4

Operator Profiles

Three distinct operator fingerprints share the upstream chain and the JSON-API recon toolkit. Their tells diverge at the interactive stage (websocket Shell dimensions) and at the UA string. Treat these as defensible attribution buckets, not named-actor attribution.

Operatorop-AGo-http-client / 24×80 / Pattern D primary
ttp
Most disciplined of the three. Issues the canonical Pattern D recon sequence in deterministic order, opens a 24×80 websocket Shell, runs the __S_MARK__/__E_MARK__ harvester. Reseller persistence (sptadm + WHM_FullRoot API token) is its strongest persistence tell.
ips
192.81.219.19038.146.25.154
user-agents
Go-http-client/1.1

This operator is responsible for the cleanest, most repeatable kill chain we observed. Where Pattern D appears, this operator is almost always one of the actors involved. The Go-http-client UA and the consistent 24×80 dimensions suggest a single piece of automation rather than a person.

Operatorop-BMozilla/5.0 / 24×120 / browser-driven secondary
ttp
Operates the websocket Shell directly from a browser, dimensions 24×120: that is a person resizing a terminal pane to non-default width, not a tool. UA strings are real-browser plausible. Less recon discipline than Operator A; arrives after the cpsess token is already minted.
ips
149.102.229.144
user-agents
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:142.0) Firefox/142.0

We see this operator on hosts that already have an active forged session from Operator A. The pattern looks like token reuse or token-handoff between members of the same crew, not independent compromise.

Operatorop-CChrome 135 / Opera 120 / 24×134 / pre-disclosure tail
ttp
The operator that produces the 24×134 dimension. The same fingerprint appears on the pre-disclosure host above. Browser-driven, similar TTPs to Operator B, but with measurably different terminal width and a different UA family.
ips
183.82.160.147
user-agents
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36

The pre-disclosure timestamp question hinges on this operator. If 2025-12-22 is genuine, this is the operator who had the primitive months before anyone else. If it's a forged session timestamp, the operator's first verifiable activity is post-public-CVE, like the others.

Pattern Catalog

The ten patterns we are running detection against today. Pattern X is initial access; D/G/I are persistence; E/F are interactive; A/B/C/H are destructive terminal stages. The catalog is sequenced as the chain runs in the wild, not alphabetically.

Pattern X·Access

Initial CRLF Authorization access

stable

The bug working: a multi-line pass= field written through saveSession() produces a session file with a forged cpsess[N] token and tfa_verified=1. Universal across every confirmed compromise.

regex^pass=.*\n.
idtoken_denied=1 + cp_security_token=/cpsess[N]
idorigin_as_string=address=<IP>,app=whostmgrd,method=badpass
Pattern D·Recon

JSON-API enumeration + reseller persistence

new

Two-phase. The recon side is a Go-http-client agent walking/json-api/*and the Fileman API. The persistence side creates a sptadm reseller and a WHM_FullRoot API token, both of which survive the cPanel patch and need separate revocation.

uaGo-http-client/1.1
idusername=sptadm
iddomain=4ef72197.cpx.local
idcontactemail=a@exploit.local
cmdCREATEAPITOKEN:root:root:not-applicable:WHM_FullRoot
ip96.30.39.236claimed source-IP in createacct accounting.log entry; IOC for join, not necessarily real origin
Pattern E·Interactive

websocket/Shell interactive RCE

new

Once a forged session is in hand, operators pivot to /cpsess[N]/websocket/Shell for an interactive root shell. Three observed dimensions (24×80, 24×120, 24×134) function as a deterministic per-operator fingerprint.

regexGET /cpsess[0-9]+/websocket/Shell\?rows=
idrows=24&cols=80: Operator A
idrows=24&cols=120: Operator B
idrows=24&cols=134: Operator C
Pattern F·Harvester

Automated agent recon shell wrapper

new

Inside the websocket shell, a follow-up tool wraps every command with __S_MARK__ and __E_MARK__ delimiters and harvests credentials, SSH keys, and shell history files. The wrapper is the strong actor tell: a human does not type these delimiters, so any host with the envelope in .bash_history has been touched by this specific tool.

regexprintf '__S_MARK__'.*printf '__E_MARK__'
cmdfind /root /home -maxdepth 3 -name '.bash_history' -exec cat
Pattern G·Persistence

SSH key persistence with mtime forging

new

Non-standard ssh-rsa keys planted across /root/.ssh, /etc, and cron paths. Comments include IP-labeled keys mimicking provider internal-key style. Detection should rely on ctime, not mtime; touch backdates the latter two but cannot backdate the former.

cmdfind /root /etc /var/spool/cron -type f -exec grep -l 'ssh-rsa' +
regex^[0-9.]{7,15} ssh-rsa
Pattern A·Destructive

.sorry encryptor + qTox ransom

stable

Encryptor binary masquerading as /root/sshd. Encrypts user files and system files (in-place restore fails; re-image required), drops a README with a TOX ID, C2 over a single IP. Files get the .sorry extension.

file/root/sshd
hash2fc0a056fd4eff5d31d06c103af3298d711f33dbcd5d122cae30b571ac511e5asha256
ip68.183.190.253C2
id3D7889AEC00F2325E1A3FBC0ACA4E521670497F11E47FDE13EADE8FED3144B5EB56D6B198724qTox ID
Pattern B·Destructive

DB wipe + index.html ransom note

stable

Cheaper variant. Drops a BTC-ransom note in every /home/*/public_html/index.html and removes /var/lib/mysql/mysql. Files are not encrypted; restore-from-backup recovers cleanly.

idbc1q9nh4revv6yqhj2gc5usncrpsfnh7ypwr9h0sp2BTC address
regexto recover your files, kindly send 0\.1 BTC
Pattern C·Destructive

Mirai / nuclear.x86 cryptominer

stable

Commodity Mirai-family dropper. Largest single bucket of the campaign by host count among the destructive variants. Persistence via cron and systemd; binary lands at well-known paths.

filenuclear.x86
ip87.121.84.78binary host
idraw.flameblox.comC2 / dropper
Pattern H·Destructive

seobot.php SEO defacement + competitor-kill

new

Per-site PHP webshell drop into every public_html, preceded by a pkill against known competitor processes (nuclear.x86, kswapd01, xmrig) and a final ALLDONE marker. Confirms cross-actor competition for the same vulnerable cohort.

file*/public_html/seobot.php
regexpkill -9 (nuclear\.x86|kswapd01|xmrig)
regexecho ALLDONE
Pattern I·Persistence

profile.d service-stub backdoor

evolving

A parallel persistence layer that surfaced in adjacent triage. A /etc/profile.d shim launches a non-standard binary at /root/.local/bin/system-service on every interactive shell login. Triggering on profile.d instead of cron means it fires more often and looks less like a scheduled task. Detection signal is a chmod permission-denied log line from a non-root shell login; that is exactly how it surfaced first.

file/etc/profile.d/system_profiled_service.sh
file/root/.local/bin/system-service
regexchmod: cannot access '/root/\.local/bin/system-service'

Cross-Provider Signal

We've corroborated the upstream chain (Pattern X → D → E → F) against several peer providers running their own detection. The granular tells (operator dimensions, the harvester envelope, the reseller-as-persistence pattern) show up consistently, which is what gives us confidence the toolkit is shared.

What Worked, What Didn't

A short, opinionated list. We're keeping this section generalizable: what would also be true for a similar incident tomorrow, not specifics about one provider's rollout.

Worked

  • ModSecurity rule pack landed inside the disclosure window, before the public PoC. The WAF layer is the right place to close Pattern X; it intercepts at the request stage, before saveSession() ever sees the malformed pass= field.
  • Pre-positioning the on-host IOC scanner allowed retrospective triage of hosts that had been touched before the WAF rules were live. Detect-then-mitigate is realistic; detect-only is not.
  • Treating the reseller persistence and the API token as a separate cleanup step (not part of the cPanel patch) caught operators who tried to come back through the token after the patch landed.

Didn't

  • CSF, APF, and standard host firewalls are not enough. Pattern X traffic looks like legitimate authenticated WHM; it goes to :2087, it carries an Authorization header, and it's structurally valid HTTPS. A perimeter firewall will not block it.
  • WHM-port-open hosts without an upstream WAF were the population that fell. The right posture is WAF inside the auth boundary, not just at the edge.
  • Vendor IOC scripts shipped with at least one grep -P regex bug that produced silent false negatives. Don't trust an IOC script you haven't read.

Actionable: Mitigate & Block

Two concrete things you can push today.sessionscribe-mitigate.sh applies the active-defense posture in one phased pass; the tier-1 IP block list is the high-confidence operator infrastructure observed in the wild this week.

Mitigation tooling

sessionscribe-mitigate.sh is idempotent and writes timestamped backups of every mutated file under /var/cpanel/sessionscribe-mitigation/<TS>/. Default mode is --check (read-only posture audit); --apply mutates. Phases run in order: snapshot, patch posture, preflight, upcp (if unpatched), proxysub enforcement, CSF scrub, APF scrub, runfw inspection, Apache check, modsec rules, session quarantine, optional remote-probe self-test.

bash
# 1) read-only posture audit (no mutations)
curl -s https://sh.rfxn.com/sessionscribe-mitigate.sh | bash

# 2) full apply (run as root; ModSec rules + CSF/APF cpsrvd-port scrub +
#    proxysub enforcement + session quarantine into backup dir)
curl -s https://sh.rfxn.com/sessionscribe-mitigate.sh | bash -s -- --apply

# 3) selective phases (e.g. modsec only, no firewall mutations)
bash sessionscribe-mitigate.sh --apply --only modsec

# 4) JSONL output for fleet aggregation (Ansible/Salt/SSH-wrap)
bash sessionscribe-mitigate.sh --apply --jsonl --quiet > host.jsonl

# 5) revoke any sptadm reseller and the WHM_FullRoot API token
#    (mitigate.sh does not touch reseller state; do this manually)
whmapi1 listacct | grep -i sptadm
whmapi1 delacct user=sptadm
whmapi1 list_tokens | grep -i 'WHM_FullRoot\|not-applicable'
whmapi1 revoke_api_token token_name=<token>

Tier-1 attacker IPs (block today)

KB-known plus access-log 2xx success against the fleet. Both signals are observed at the IP level and are independent of host-verdict logic, so they survive any verdict-precision change in the scanner. Roles below are observed behavior in this corpus; treat as defensible attribution buckets, not named-actor attribution.

IPObserved roleUA
80.75.212.14broad-scope exploitation; highest 2xx success volume in corpus
94.231.206.39TLS handshake to :2095, badpass exploit (KB-known)
142.93.43.26badpass exploit at scale
45.82.78.104TLS handshake to :2082, websocket Shell pivot (KB-known)Chrome 135 / Opera 120 / Firefox 142
206.189.2.13leakix scanner badpass (KB-known)leakix/2.0
157.245.204.205leakix scanner badpass (KB-known)leakix/2.0
136.244.66.225session-origin pool, 2xx success
68.233.238.100badpass exploit (KB-known)python-requests/2.33.1
159.223.155.255post-CVE 2xx wave (DigitalOcean cluster)
137.184.77.0badpass exploit (KB-known)
38.146.25.154Pattern D createacct source; Operator AGo-http-client/1.1
67.205.134.215post-CVE 2xx wave (DigitalOcean cluster)
206.189.227.202post-CVE 2xx wave (DigitalOcean cluster)
192.81.219.190Pattern D enum + websocket Shell; Operator A (24x80)
146.19.24.235badpass exploit, recurring origin
149.102.229.144websocket Shell pivot; Operator B (24x120)Mozilla/5.0 Firefox/142.0
183.82.160.147websocket Shell pivot; Operator C (24x134); pre-disclosure timestampMozilla/5.0
87.121.84.78Pattern C nuclear.x86 binary host
68.183.190.253Pattern A .sorry encryptor C2
96.30.39.236claimed source-IP in Pattern D createacct API call body (attacker-controlled field; useful for log join, not necessarily real origin); KB-known
bash
# csf.deny
csf -d 80.75.212.14 IC-5790-T1
csf -d 94.231.206.39 IC-5790-T1
csf -d 142.93.43.26 IC-5790-T1
csf -d 45.82.78.104 IC-5790-T1
csf -d 206.189.2.13 IC-5790-T1
csf -d 157.245.204.205 IC-5790-T1
csf -d 136.244.66.225 IC-5790-T1
csf -d 68.233.238.100 IC-5790-T1
csf -d 159.223.155.255 IC-5790-T1
csf -d 137.184.77.0 IC-5790-T1
csf -d 38.146.25.154 IC-5790-T1
csf -d 67.205.134.215 IC-5790-T1
csf -d 206.189.227.202 IC-5790-T1
csf -d 192.81.219.190 IC-5790-T1
csf -d 146.19.24.235 IC-5790-T1
csf -d 149.102.229.144 IC-5790-T1
csf -d 183.82.160.147 IC-5790-T1
csf -d 87.121.84.78 IC-5790-T1
csf -d 68.183.190.253 IC-5790-T1
csf -d 96.30.39.236 IC-5790-T1

# iptables (drop, log to /var/log/messages)
for ip in 80.75.212.14 94.231.206.39 142.93.43.26 45.82.78.104 \
          206.189.2.13 157.245.204.205 136.244.66.225 68.233.238.100 \
          159.223.155.255 137.184.77.0 38.146.25.154 67.205.134.215 \
          206.189.227.202 192.81.219.190 146.19.24.235 149.102.229.144 \
          183.82.160.147 87.121.84.78 68.183.190.253 96.30.39.236; do
  iptables -I INPUT -s "$ip" -j DROP
done

Open Questions

What's Next

The detection toolkit ships as four scripts: sessionscribe-ioc-scan.sh (on-host read-only triage; current version v2.5.0), sessionscribe-mitigate.sh (active defense: ModSecurity rules 1500030/1500031, cpsrvd-port scrub via CSF/APF, proxysub enforcement, patch posture), sessionscribe-remote-probe.sh (non-destructive remote verdict by HTTP code), and sessionscribe-revsnap.sh (pre/post-upgrade snapshot collector for binary-diff analysis). Canonical source at github.com/rfxn/cpanel-sessionscribe; curl-friendly mirror at sh.rfxn.com.

v4 of the scanner is in flight. The headline changes are:

  • Verdict precision. Post-attack activity required for a COMPROMISED verdict; attempt-shaped IOCs tier into a separate ATTEMPT bucket. Stops attempt counts from inflating compromise counts.
  • 2xx-on-cpsess split. Real exploit success gets its own gate, separate from generic recon 2xx.
  • Real Pattern X timestamps. Per-event wall-clock timestamps replace forged-session iso for timeline analysis, addressing the open-question above.
  • Pre-mitigation snapshot. Captures evidence before any active defense mutates host state, so the kill-chain remains reconstructable post-cleanup.

Next field-notes entry will be written after v4 ships and we've re-run detection across the existing corpus. We expect the cohort sizes to come down as attempts and compromises separate cleanly. The catalog above will not.

References