Caption is a hard-difficulty linux machine from Hack The Box dealing initially with abusing an XSS through request smuggling bypassing Haproxy to get an admin cookie that’ll allow us to exploit CVE-2023-37474 (a CopyParty Directory Traversal) to later exploit a thrift /Go log handler to inject malicious commands and get root access. Caption-info-card
Reconnaissance
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
PS C:\Users\0xkujen> nmap -A-Pn10.129.230.251 Starting Nmap 7.95 ( https://nmap.org ) at 2025-01-2518:01 W. Central Africa Standard Time Nmap scan report for10.129.230.251 Host is up (0.16s latency). Not shown: 997 filtered tcp ports (no-response) PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 2563e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA) |_ 25664: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-title: Did not follow redirect to http://caption.htb |_http-open-proxy: Proxy might be redirecting requests 8080/tcp open http Jetty |_http-title: GitBucket Service Info: OS: Linux; Device: load balancer; CPE: cpe:/o:linux:linux_kernel
We can see that we have our typical ssh 22 port, along with a web application deployed on port 80 that’s redirecting us to caption.htb (that we’ll add to our /etc/hosts file) and what looks like an instance of Gitbucket running on port 8080.
This is a Bitbucket instance. We can see that there are 1 repos on it.
Caption Portal Repository
We can see that it has an app and config folders with a README.md file:
Bitbucket
Inside of the config folder, we can find a haproxy, service and varnish folders.
Also checking the commit history which seems interesting:
Bitbucket
Reading the Update Access Control commit; I can see that there are creds for a user named margo that have been deleted: Bitbucket
margo:vFr&cS2#0!
H2C Smuggling
We try those creds to ssh into the machine but no use. But using them we can login to the main first web app: Caption Networks
Trying to go to /logs returns a 43 forbidden, which should be generated from HAProxy. Also, Looking at the main page we see this sentence: Note: Services are currently undergoing maintenance. Admins are actively addressing some issues with this feature which indicates the existance of an admin account (XSS?)
While inspecting the config files from GitBucket I saw that Varnish allows the usage of the HTTP2. Which allows us to perform an HTTP/2 to Cleartext attack to get a hold to the logs and extract endpoints behind HAProxy. H2C smuggling exploits HTTP/1.1 to HTTP/2 cleartext (h2c) upgrade mechanism in order to exploit and bypass security controls in reverse proxies, web app, WAFs. By launching a h2c upgrade, an attacker can establish a direct HTTP/2 connection to the server,getting a tunnel not checked by the proxy. Doing some ninja searches, I stumbled upon this article by Bishopfox where they highlight that Haproxy can be vulnerable to the attack.
They also create this tool that helps us with pwning our box.
First I start by testing our endpoint and its a success:
1 2 3
PS C:\Users\0xkujen\Desktop\HackThebox\HTB_Machines\Caption\h2csmuggler> python3 .\h2csmuggler.py -x http://caption.htb --test [INFO] h2c stream established successfully. [INFO] Success! http://caption.htb can be used for tunneling
<!doctype html> <html lang=en> <title>Redirecting...</title> <h1>Redirecting...</h1> <p>You should be redirected automatically to the target URL: <a href="/">/</a>. If not, click the link.
And this time I get a 302 instead of a 403. Which seems good so far. Logically means that we bypassed HAproxy now since we are no longer forbidden.
Now I’ll do it again but adding my user cookie and I get this message: <p>You should be redirected automatically to the target URL: <a href="/?err=role_error">/?err=role_error</a>. If not, click the link. We have a role error while using our user cookie. Now I’m 100% sure that there is an XSS to get the admin cookie.
XSS to get admin cookie
First I’ll inspect the requests in Caido. There is some interesting config related to varnish when navigating to firewalls endpoint:
32872 means our current request ID, while 107 means the ID of a previous request that was cached. Meaning that the reponse we got came from varnish (it was cached previously). The age header indicates in seconds how long the response has been cached in varnish since it was first gotten from the backend.
I also noticed that this endpoing: /static/js/lib.js?utm_source=http://internal-proxy.local is being requested whenever I want to access the firewalls endpoint. If I could alter the source, I could trigger and XSS payload. And I successfully manage to do that using X-Forwarded-Host: XSS
I also managed to trigger an alert using "><script> alert(1)</script>
Now the issue is in delivering and XSS payload to the admins in order to get their cookie. First I’ll create this xss payload on my local machine:
First I’ll have to wait for varnish to be expected to trigger a cache miss (which is a new request coming from the web app, if age=0 then there was a cache miss ). By checking teh age header, The biggest number I got was 121 before going back to 0 which is indicates that the cache time is around 2 minutes.
I’ll first set intercept on in caido, then navigate to /firewalls endpoint and add my js code, and I get a het from the server:
<!-- nav bar --> <navclass="navbar navbar-expand-lg bg-body-tertiary p-4"> <divclass="container"> <!-- o usuário escolher o modo dark ou light --> <buttonclass="btn btn-secondary me-4"id="alterarTemaSite"onclick="alterarTemaSite()"><i class="bi bi-brightness-high-fill"></i> </button>
The interesting part was referral links to a download endpoint which accepts a url param pointing to an internal app on 127.0.0.1:3923
Looking at the response we get when requesting 127.0.0.1:3923 we have this title: 💾🎉 which when checking it on https://grep.app , I find that it belongs to CopyParty which is a portable file server.