Critical unauthenticated RCE in cPanel & WHM. Four HTTP requests forge a root session via CRLF injection into the password field of a preauth session. No auth, no preconditions, every supported tier affected. Disclosed 2026-04-28 by Sina Kheirkhah / watchTowr Labs.
Quickstart · ioc-scan · mitigate · remote-probe · Affected builds · Priority order
Important
Tiers 112, 114, 116, 120, 122, 128 have no vendor patch. Every
build on those tiers is vulnerable; upgrade or migrate is the only
durable fix. Until then: firewall TCP/2082, 2083, 2086, 2087, 2095, 2096
to management CIDRs (mitigate.sh --apply does this) and front the
remaining surface with the ModSec rule pack.
Three oneliners, in operator priority order:
# 1. Are we already compromised? (on-host IOC scan, fast triage)
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-ioc-scan.sh | bash
# 2. Close the window (idempotent; firewalls cpsrvd ports, deploys ModSec, etc.)
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-mitigate.sh | bash -s -- --apply
# 3. Sweep the fleet from anywhere (non-destructive remote verdict)
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-remote-probe.sh | bash -s -- --target HOSTExit codes are designed for fleet automation: ioc-scan exits 4 on
COMPROMISED, 1 on VULNERABLE; mitigate exits 0 clean / 1 applied
/ 2 manual / 3 tool error; remote-probe exits 2 if any target is
VULN.
In order of operational priority. Every artifact emits structured output
(--json / --jsonl / --csv) keyed on host, os, cpanel_version,
ts for fleet roll-up.
| Tool | Role | Where it runs |
|---|---|---|
sessionscribe-ioc-scan.sh |
First-class triage. IOC ladder, code-state + host-state verdicts, kill-chain reconstruction, IR bundle. | on the cPanel host |
sessionscribe-mitigate.sh |
Close the window. Phased mitigation: patch check, firewall, proxysub, ModSec. | on the cPanel host |
sessionscribe-remote-probe.sh |
Supporting collateral. Non-destructive 4-stage probe → VULN/SAFE per host. | anywhere with curl |
modsec-sessionscribe.conf |
ModSec rule pack deployed by mitigate. |
Apache front-end |
sessionscribe-revsnap.sh |
Per-tier RE snapshot collector for binary diffing. | on the cPanel host, around upcp |
GPL v2. All artifacts curl-ready via the raw URLs above.
Run this first. Detection-only by default (fast, fleet-friendly);
add --full to run the forensic phases inline (defense timeline, offense
ingest, reconcile, kill-chain renderer, IR bundle).
# fast triage (detection only)
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-ioc-scan.sh | bash
# full kill-chain reconstruction inline
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-ioc-scan.sh | bash -s -- --full
# full + ship IR bundle to intake
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-ioc-scan.sh | bash -s -- --full --upload
# JSONL for SIEM ingest
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-ioc-scan.sh | bash -s -- --jsonl --quiet > host.jsonl
# host IOCs only - periodic post-patch sweep, last 7 days
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-ioc-scan.sh | bash -s -- --ioc-only --since 7
# replay forensic phases against a saved envelope (no re-scan)
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-ioc-scan.sh | bash -s -- --replay /var/cpanel/sessionscribe-ioc/<run_id>.jsonTwo axes report independently. code_verdict (PATCHED /
VULNERABLE / INCONCLUSIVE) comes from version, Perl source patterns,
and cpsrvd binary fingerprint. host_verdict (CLEAN /
SUSPICIOUS / COMPROMISED) comes from the session-file IOC ladder,
access-log scan, and Patterns A–G destruction probes. A patched host
can still exit 4 if prior exploitation left IOCs on disk.
| Exit | Code-state | Host-state | Triage action |
|---|---|---|---|
| 0 | CLEAN/PATCHED | CLEAN | none |
| 1 | VULNERABLE | (any) | patch cpsrvd |
| 2 | INCONCLUSIVE | (any) | manual code-state review (also: tool error) |
| 3 | (any) | SUSPICIOUS | review session/access logs |
| 4 | (any) | COMPROMISED | full IR; bundle + upload |
Sessions tagged with nxesec_canary_<nonce> (left by the remote probe)
bucket as PROBE_ARTIFACT and do not escalate to COMPROMISED.
--full collates every IOC against defense activations and classifies
each as PRE-DEFENSE, POST-DEFENSE, POST-PARTIAL, or
UNDEFENDED, then summarises with verdict + defense-lag headline.
PRE-DEFENSE = host was open to the exploit when the indicator landed;
POST-DEFENSE = collateral or pre-mitigation noise.
+-- CVE-2026-41940 / IC-5790 --------------------------------------------
| host cpanel.example.com ()
| cpanel unknown os unknown
| verdict COMPROMISED score 315 ioc-scan v2.5.0
| defenses patch x absent modsec + up csf + clean mitigate + ran
+------------------------------------------------------------------------
| -- PRE-DEFENSE (32 events) --
| 2026-03-25T09:43:19Z ! pattern X ioc_attacker_ip_2xx_on_cpsess 57 hit(s) (last 90d) from IC-5790 IPs returned 2xx on /cpsess<N>/ paths - real exploitation
| 2026-04-28T14:35:56Z ! pattern X ioc_cve_2026_41940_crlf_access_chain 15 CRLF-bypass chain(s) — POST /login 401 then GET /cpsess<N> 2xx as root within 2s
| 2026-04-28T16:38:45Z ! pattern E ioc_pattern_e_websocket_shell_hits 45 external IP(s) reached /cpsess*/websocket/Shell with 2xx
| 2026-04-29T08:41:22Z ! pattern F ioc_pattern_f_smark_envelope __S_MARK__/__E_MARK__ harvester envelope in /root/.bash_history
| 2026-04-29T16:41:24Z ! pattern A ioc_pattern_a_ransom_readme /home/user1/README.md
| … (22 more Pattern A ransom_readme events across customer homedirs)
| 2026-04-29T16:42:09Z ! pattern A ioc_pattern_a_sorry_files_present 608 .sorry-encrypted files present
| 2026-04-29T17:52:58Z ! pattern D ioc_pattern_d_acctlog_encrypted /var/cpanel/accounting.log.sorry
| -- DEFENSES --
| 2026-04-29T23:48:21Z + DEFENSE mitigate_first sessionscribe-mitigate.sh first run
| 2026-04-29T23:48:21Z + DEFENSE csf csf.conf cpsrvd ports stripped
| 2026-04-29T23:48:46Z + DEFENSE modsec modsec rule 1500030 installed
| -- POST-PARTIAL (1 event) --
| 2026-04-30T12:23:42Z ! pattern E ioc_pattern_e_handoff_burst_present 3 distinct external IPs each minted cpsess + reached websocket Shell within 15-min window
| HEADLINE
| verdict COMPROMISED (score 315)
| defense lag 37d 9h LATE (first IOC 2026-03-25T09:43:19Z, defense up 37d 9h later)
Checks reference + forensic phases + bundle layout (click to expand)
| Check | What it does |
|---|---|
version |
cpanel -V vs the published patched-build list - drives code_verdict |
static-pattern |
Greps Cpanel/Session/*.pm for post-patch sentinel patterns (no-ob: decode branch) |
cpsrvd-fingerprint |
cpsrvd binary inspection against patched-build signatures |
access-log |
Apache + cpsrvd logs for exploitation traffic shapes (--no-logs to skip) |
session-store |
/var/cpanel/sessions/raw/ walk: vendor IOCs + 4-way co-occurrence + forged-timestamp heuristic (--no-sessions to skip) |
destruction |
Patterns A–G probes: /root/sshd encryptor, mysql-wipe, BTC index, nuclear.x86, sptadm reseller, __S_MARK__ harvester, suspect SSH keys (--no-destruction-iocs to skip) |
probe (opt-in) |
Single marker GET to 127.0.0.1:2087 - confirms cpsrvd is responsive. Does not attempt the bypass |
--full forensic phases (run inline after detection):
| Phase | What it does |
|---|---|
defense |
Timestamps every defense layer that landed: cpanel patch, cpsrvd restart-after-patch, mitigate runs, ModSec rules, CSF/APF port closures, proxysub, upcp summary |
offense |
Timestamps every observed compromise indicator (Patterns A–G + Pattern X CRLF-bypass chain) |
reconcile |
Per-indicator: was the relevant defense active when it first appeared? PRE-DEFENSE / POST-DEFENSE / POST-PARTIAL / UNDEFENDED + time delta |
bundle |
Tarball of raw artifacts under /root/.ic5790-forensic/<TS>-<RUN_ID>/, mode 0700 |
Bundle layout (/root/.ic5790-forensic/<TS>-<RUN_ID>/):
manifest.txt host/uid/cpv/run_id/window/cap
sessions.tgz /var/cpanel/sessions/{raw,preauth} (filtered)
access-logs.tgz cpsrvd access + incoming_http_requests + error_log
+ global Apache access/error (NO domlogs)
system-logs.tgz /var/log/{secure,messages,audit/audit.log,auth.log}*
cpanel-state.tgz accounting.log + resellers + cpanel.config + api_tokens_v2
cpanel-users.tgz /var/cpanel/users/ (split out, per-account state)
persistence.tgz ssh keys + all cron tiers + systemd/init.d/profile.d
+ rc.local + root histories + passwd/group + sudoers
defense-state.tgz mitigate backups + csf/apf/modsec configs + updatelogs
ps.txt / connections.txt / iptables.txt
pattern-a-binary-metadata.txt only if /root/sshd present (metadata; binary NOT bundled)
user-histories/ per-user .bash_history (gated on --no-history)
Typical bundle on a busy host with the 90-day window: ~250 MB – 2 GB compressed.
Per-tarball 2 GB cap (--max-bundle-mb) drops oversize candidates individually.
A run ledger is written to /var/cpanel/sessionscribe-ioc/ by default
(--no-ledger to disable). --chain-forensic / --chain-on-critical /
--chain-upload are preserved as v1.x back-compat aliases.
Snapshot-testing overrides for offline forensics on extracted tarballs:
--root DIR, --version-string S, --cpsrvd-path P. See --help for
the full flag list.
Read-only by default (--check). Add --apply to mutate state.
Idempotent: re-running on a healthy host is a no-op. Mutations write
timestamped backups under /var/cpanel/sessionscribe-mitigation/
before touching any file.
# read-only audit (default)
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-mitigate.sh | bash
# full remediation
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-mitigate.sh | bash -s -- --apply
# narrow scope
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-mitigate.sh | bash -s -- --apply --only modsec --probe
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-mitigate.sh | bash -s -- --only patch,preflight # pre-upcp gate
# fleet roll-up - one CSV row per host
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-mitigate.sh | bash -s -- --csv > host.csv
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-mitigate.sh | bash -s -- --jsonl > host.jsonl| Phase | What it does |
|---|---|
patch |
cpanel -V vs the published patched-build list (incl. EL6 11.86.0.41, EL6/CL6 110.0.103, tier 124, WP² 136.1.7) |
preflight |
Removes /etc/yum.repos.d/threatdown.repo; ensures epel-release; disables broken non-base repos so upcp doesn't die mid-flight |
upcp |
If unpatched, kicks off /scripts/upcp --force --bg |
proxysub |
Enables proxysubdomains + new-account variant; rebuilds httpd conf |
csf / apf / runfw |
Strips cpsrvd ports (2082/2083/2086/2087/2095/2096) from TCP_IN/TCP6_IN/IG_TCP_CPORTS; verifies live iptables INPUT chain |
apache |
httpd running + security2_module loaded |
modsec |
modsec2.user.conf contains rules 1500030 + 1500031; deploy if missing (timestamped backup, httpd -t validation, graceful reload) |
probe (opt-in) |
Runs sessionscribe-remote-probe.sh against 127.0.0.1 to confirm denials in practice |
CentOS / Alma / Rocky base/appstream/extras/updates/powertools repos
are never disabled by preflight, even if currently unreachable.
| Exit | Meaning |
|---|---|
| 0 | clean - patched + posture ok, no action needed |
| 1 | remediation applied successfully (--apply made changes) |
| 2 | manual intervention required (warns in --check, fails in --apply) |
| 3 | tool error (bad args, missing dependencies, not root for --apply) |
Phase selection: --only LIST, --no-PHASE, --no-fw (shorthand for
--no-csf --no-apf --no-runfw). Output: --json / --jsonl / --csv,
-o FILE. See --help for the full flag list.
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-mitigate.sh | bash -s -- --list-phases # surface the phase API
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-mitigate.sh | bash -s -- --check # safe read-only audit
echo "exit=$?" # 0 on a non-cPanel hostThe orchestrator detects a non-cPanel host and exits clean — proving idempotency without needing a lab.
Supporting collateral. Runs the four-stage chain non-destructively
against a target: mint preauth → CRLF inject → propagate raw→cache →
verify via /json-api/version, then actively logs out. Verdict-
determining signal is the HTTP code at stage 4: 200, or 5xx with a
license body, is VULN; 401 or 403 is SAFE.
Every test session is tagged with an nxesec_canary_<nonce> attribute
for forensic cleanup, and no state-changing API calls are made.
Forged sessions are root-equivalent for ~1–3s between stage 3 and the
stage 5 logout — see the script header for the full safety model.
# single host, default WHM-SSL ports
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-remote-probe.sh | bash -s -- --target 1.2.3.4
# Apache proxy test - whm./cpanel./webmail.example.com via 443 + 80
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-remote-probe.sh | bash -s -- --target 1.2.3.4 --proxy example.com
# fleet - CSV across many targets, exit 2 on any VULN
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-remote-probe.sh \
| bash -s -- --csv $(awk '{print "--target "$1}' fleet.txt) > fleet.csv
# fast scoping - banner-only fingerprint, no session minted
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-remote-probe.sh | bash -s -- --target 1.2.3.4 --fingerprint-only
# clean canary sessions on a target after a run
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-remote-probe.sh | bash -s -- --cleanupOutput modes: pretty (default), -q/--quiet, --oneline, --csv,
--json. Exit codes: 0 no VULN found, 1 inconclusive only, 2
one or more VULN. See --help for full flag list including
--auto-host-discover, --all, --fingerprint-only semantics, and
the stage-2-only --no-verify mode (legacy, produces FPs on patched
hosts).
Deployed automatically by mitigate.sh --apply --only modsec. Manual
install:
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/modsec-sessionscribe.conf \
| sudo tee /etc/apache2/conf.d/modsec/modsec2.user.conf >/dev/null
sudo apachectl -t && sudo /usr/local/cpanel/scripts/restartsrv_httpd| Rule | Surface | Action |
|---|---|---|
1500030 |
CRLF inside Authorization: Basic decoded payload |
deny, all sources, all paths |
1500031 |
whostmgrsession cookie missing valid ,OBHEX suffix |
deny (defense-in-depth) |
1500010 |
Authorization: WHM on /json-api/, /execute/, /acctxfer*/ |
deny when source not in trust list |
1500020 |
Authorization: WHM on WebSocket dispatch family |
deny when source not in trust list |
1500021 |
Authorization: WHM on SSE dispatch path |
deny when source not in trust list |
ID range reserved: 1500000–1500099. WHM-token rules use @ipMatch
against an operator-defined trust list — edit the CIDRs at the top of
the file before deploying.
Important
These rules run inside Apache. cpsrvd listens directly on
2082/2083/2086/2087/2095/2096 and is reachable independent of Apache.
Pair the rule pack with cpsrvd-port firewalling to management CIDRs.
Captures a per-tier tarball (binaries, strings, dynsym, disasm, Perl
modules, runtime layout) for binary diffing across cPanel upgrades.
Built around upcp to capture pre/post-patch pairs.
# capture current tier (writes to /var/cpanel/sessionscribe-revsnap/)
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-revsnap.sh | bash
# upgrade and capture next tier - RE diff workflow
/scripts/upcp --force
curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-revsnap.sh | bashGeneralises beyond SessionScribe: every future cpsrvd CVE will land in roughly the same surface, and a tarball pair for the pre-patch and patched build is the difference between hours and days of analysis. See the research article for the full RE walkthrough.
Tip
Every artifact emits structured output (--json, --jsonl, --csv)
with host, os, cpanel_version, ts on every record. Designed
for pdsh | jq or ansible -m script roll-up across hundreds of
hosts in one pass.
Same curl-piped-bash primitive — let each host fetch and exec the latest script directly:
IOC=https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-ioc-scan.sh
MIT=https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-mitigate.sh
PRB=https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-remote-probe.sh
# IOC scan across fleet, JSONL to SIEM
for h in $(cat fleet.txt); do
ssh "$h" "curl -fsSL $IOC | bash -s -- --jsonl --quiet"
done | jq -c '.' > fleet-ioc.jsonl
# kill-chain reconciliation across fleet (no bundles on broad sweep)
ansible -i hosts cpanel -m shell \
-a "curl -fsSL $IOC | bash -s -- --full --no-bundle --jsonl" > fleet-forensic.jsonl
jq -r 'select(.phase=="summary" and .key=="verdict"
and .note=="COMPROMISED_PRE_DEFENSE") | .host' \
fleet-forensic.jsonl > pre-defense-hosts.txt
# bundle collection on the pre-defense subset
ansible -i pre-defense-hosts.txt all -m shell \
-a "curl -fsSL $IOC | bash -s -- --full --jsonl --bundle-dir /root/.ic5790-forensic"
# mitigation posture roll-up
pdsh -w cpanel-fleet "curl -fsSL $MIT | bash -s -- --jsonl --quiet" \
| jq -c 'select(.severity != "info")' > fleet-mitigate.jsonl
# remote probe sweep - exit 2 on any VULN (run from operator workstation)
curl -fsSL $PRB | bash -s -- --csv --quiet \
$(awk '{print "--target "$1}' fleet.txt) > fleet-probe.csvThe probe is independently fleet-safe (canary-tagged sessions, active
logout, no state-changing API calls). The on-host scripts respect
--quiet + structured-output flags so stdout is parser-clean.
Four HTTP requests, no auth, no preconditions:
sequenceDiagram
autonumber
actor A as attacker
participant C as cpsrvd
participant S as session file
A->>C: POST /login/?login_only=1 with user=root, pass=wrong
C-->>A: Set-Cookie · whostmgrsession=NAME,OBHEX
A->>C: GET / · Authorization Basic b64(root:x + CRLF payload) · Cookie minus OBHEX
C->>S: writes pass=x, user=root, hasroot=1, ... (CRLFs land verbatim)
C-->>A: HTTP 307 · Location /cpsess[10digits]/
A->>C: GET /scripts2/listaccts · cookie only
C->>S: propagate raw to cache · forged keys now readable
C-->>A: 401 token denied (side-effect already done)
A->>C: GET /cpsess[token]/json-api/version
C-->>A: 200 OK means VULN · 403 means SAFE
Verdict is the HTTP code at request 4. The on-disk session file at
/var/cpanel/sessions/raw/<sessname> is the only post-hoc forensic
artifact. The full primitive (the two composing asymmetries —
filter_sessiondata not on every write path, encoder short-circuits
on missing ob_part) and the architectural argument for proxy-endpoint
enforcement are in the
research article.
Forged session-file shape (/var/cpanel/sessions/raw/<sessname> after
exploitation):
local_port=2087
hasroot=1
hulk_registered=1
pass=x
origin_as_string=address=127.0.0.1,app=whostmgrd,method=badpass
token_denied=1
local_ip_address=127.0.0.1
external_validation_token=cS9C19OfV0hCA4uD
cp_security_token=/cpsess6844364556
ip_address=127.0.0.1
user=root
tfa_verified=1
successful_internal_auth_with_timestamp=9999999999
port=39040
login_theme=cpanel
A normal preauth session never contains pass=, hasroot=1,
user=root, tfa_verified=1, or
successful_internal_auth_with_timestamp=. Any of those combined with
origin_as_string=…method=badpass is diagnostic. A forged-timestamp
value beyond now+365d (e.g. 9999999999) is independently diagnostic.
# quick scan for IOC0 (vendor-published shape) - ioc-scan does this + much more
for f in /var/cpanel/sessions/raw/*; do
[ -f "$f" ] || continue
if grep -q '^token_denied=' "$f" \
&& grep -q '^cp_security_token=' "$f" \
&& grep -q '^origin_as_string=.*method=badpass' "$f"; then
echo "IOC0 hit: $f"
fi
doneAccess-log signal: successful 200/302/307 responses on
/json-api/, /execute/, or /scripts2/ paths from non-baseline
source IPs without a preceding /login/ 200 in the same session
window.
11.86.0.41 (EL6/CL7) 11.110.0.97 11.118.0.63 11.124.0.35
11.126.0.54 11.130.0.19 11.132.0.29 11.134.0.20
11.136.0.5 110.0.103 (EL6/CL6 from .50)
WP Squared: 136.1.7
Tiers excluded from the vendor patch list have no in-place fix: 112, 114, 116, 120, 122, 128. Hosts on those tiers must be upgraded to a patched major series, migrated, or have their cpsrvd listeners firewalled until they are.
11.86.0.41 (EL6/CL7) was added in the 04/29 advisory revision; 11.130
was bumped from .18 to .19 in the same revision. A subsequent
revision added 11.124.0.35 (closing the prior gap on tier 124) and
110.0.103 as a direct upgrade target for EL6/CL6 hosts still on
v110.0.50.
Immediate
- Run
sessionscribe-ioc-scan.shfleet-wide. A patched host can still be compromised. - Patch to the build for your tier (above).
- If the tier has no patch, firewall TCP/2082, 2083, 2086, 2087, 2095, 2096 to management CIDRs immediately. Plan an upgrade or migration.
Forward
- Enable proxy subdomains so cPanel/WHM/Webmail are reachable through Apache on 80/443.
- Deploy
modsec-sessionscribe.confintomodsec2.user.confwith the@ipMatchtrust list set. - Firewall TCP/2082, 2083, 2086, 2087, 2095, 2096 to management CIDRs only. Apache + ModSecurity becomes the sole public ingress.
- Standardise this proxy-endpoint posture as the default. The next cpsrvd advisory will land on the same six ports.
The architectural case for proxy-endpoint enforcement is the closing third of the research article.
- Not a vendor patch. Does not modify
cpsrvd,cpsrvd.so, orCpanel/Session/*.pm. The cpanel-issued back-port for your tier is the real fix. - Not a fix for tiers 112, 114, 116, 120, 122, 128. No vendor patch
exists. The orchestrator's
proxysub+ firewall phases plus the ModSec rule pack reduce blast radius; upgrade or migration is the only durable answer. - Not a replacement for port lockdown. ModSec rules fire only on traffic that traverses Apache. Pair the rule pack with cpsrvd-port firewalling.
- Not exploit code. The remote probe issues no state-changing API
calls, tags every test session with an
nxesec_canary_<nonce>attribute for cleanup, and actively logs out. - Not an IR substitute.
ioc-scanfinds artifacts of prior exploitation; it does not remediate them. Treat itsCOMPROMISEDverdict as a trigger for full IR, not a conclusion.
Tip
Found a bug, missed IOC, false positive, or have ops feedback? Open a GitHub issue - bug reports, IOC variants seen in the wild, detection misses on patched/unpatched hosts, ModSec rule false positives, and general operator feedback are all welcome.
Sensitive disclosures (live exploitation evidence, customer data, novel exploit chains not yet public) should go via Keybase or email, not GH Issues.
- Research article (full writeup): rfxn.com/research/cpanel-sessionscribe-cve-2026-41940
- Vendor advisory: cPanel KB 40073787579671
- Researcher writeup: watchTowr Labs
- Public PoC: watchtowrlabs/watchTowr-vs-cPanel-WHM-AuthBypass-to-RCE.py
- Source: github.com/rfxn/cpanel-sessionscribe
GPL v2. See individual file headers.
Forged during the SessionScribe incident response - Ryan MacDonald, R-fx Networks.