============================================================================= CVE-2026-41940 - cPanel & WHM Authentication Bypass Exploit
Vulnerability: Session-file manipulation through CRLF injection in cpsrvd's HTTP Basic authentication handler CVE: CVE-2026-41940 CVSS: 9.8 (Critical) CWE: CWE-93 (CRLF Injection) / CWE-306 (Missing Authentication) Affected: cPanel & WHM versions prior to: 11.110.0.97, 11.118.0.63, 11.126.0.54, 11.132.0.29, 11.134.0.20, 11.136.0.5 Ports: 2082/2083 (cPanel), 2086/2087 (WHM), 2095/2096 (Webmail)
TECHNICAL ROOT CAUSE: cpsrvd's HTTP Basic auth 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.
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 treats the session as fully authenticated.
ATTACK CHAIN (4 HTTP requests):
- Pre-auth session mint: POST /login/?login_only=1 (failed login) -> creates session file, get whostmgrsession cookie
- CRLF injection: GET / with Authorization: Basic header containing CRLF payload, cookie WITHOUT obfuscation suffix -> password with embedded \r\n written to raw session file
- Regen trigger: GET /cpsess/scripts2/listaccts -> do_token_denied() fires, re-parses raw file, promotes injected keys
- Authenticated access: GET /cpsess/json-api/version -> session now has root privileges, bypasses password & 2FA
NOTE: There is a race condition due to Perl's randomized hash iteration order. The original (unset) cp_security_token may overwrite the injected one. Multiple retries make the chain reliable.
DISCLAIMER: This tool is provided for authorized security testing and research purposes only. Unauthorized use against systems you do not own or have explicit permission to test is illegal.