Page cover

Caption

Synopsis

Caption is a hard linux machine created by MrR3bootarrow-up-right. We will get the margo credentials in GitBucket running in port 8080 to login into Caption Portal running in port 80. The machine uses varnish and haproxy services which is vulnerable to CVE-2021-26740 varnish h2c smuggling, XSS and varnish cache poisoning. We can chain the vulnerabilities to get the admin cookie for caption.htb domain and read the logs which is restricted to margo user. The enumeration to log reveals the use of copypartarrow-up-right which is vulnerable to CVE-2023-37474 path travasel and we will get the margos id_ecdsa. The root is running the thrift arrow-up-rightin port 9090 and server.go which can be leveraged to get the shell as root by crafting malicious log file and creating script which will connect to thrift and read the log file.

OS
Difficulty
Points
Release Date
Retired Date

Linux

Hard

40

14-09-2024

25-01-2025


Enumeration

Nmap

Started the nmap scan and found the ssh and http services running.

nmap -p- -Pn -sC -sV --min-rate=1000 10.10.11.33
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-21 20:39 EST
Warning: 10.10.11.33 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.11.33
Host is up (6.6s latency).
Not shown: 47141 closed tcp ports (reset), 18391 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: 
|   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
|_http-title: Did not follow redirect to http://caption.htb
| fingerprint-strings: 
|   DNSStatusRequestTCP, DNSVersionBindReqTCP, Help, RPCCheck, RTSPRequest, X11Probe: 
|     HTTP/1.1 400 Bad request
|     Content-length: 90
|     Cache-Control: no-cache
|     Connection: close
|     Content-Type: text/html
|     <html><body><h1>400 Bad request</h1>
|     Your browser sent an invalid request.
|     </body></html>
|   FourOhFourRequest, GetRequest, HTTPOptions: 
|     HTTP/1.1 301 Moved Permanently
|     content-length: 0
|     location: http://caption.htb
|_    connection: close
8080/tcp open  http-proxy
|_http-title: GitBucket
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.1 404 Not Found
|     Date: Wed, 22 Jan 2025 01:51:03 GMT
|     Set-Cookie: JSESSIONID=node01re4rwzjxaips1slx525zcd2y074.node0; Path=/; HttpOnly
|     Expires: Thu, 01 Jan 1970 00:00:00 GMT
|     Content-Type: text/html;charset=utf-8
|     Content-Length: 5916
|     <!DOCTYPE html>
|     <html prefix="og: http://ogp.me/ns#" lang="en">
|     <head>
|     <meta charset="UTF-8" />
|     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
|     <meta http-equiv="X-UA-Compatible" content="IE=edge" />
|     <title>Error</title>
|     <meta property="og:title" content="Error" />
|     <meta property="og:type" content="object" />
|     <meta property="og:url" content="http://10.10.11.33:8080/nice%20ports%2C/Tri%6Eity.txt%2ebak" />
|     <meta property="og:image" content="http://10.10.11.33:8080/assets/common/images/gitbucket_ogp.png" />
|     <link rel="icon" href="/assets/common/images/
|   GetRequest: 
|     HTTP/1.1 200 OK
|     Date: Wed, 22 Jan 2025 01:50:54 GMT
|     Set-Cookie: JSESSIONID=node0xzxhhmrrps8t141p5ws0tcz0x72.node0; Path=/; HttpOnly
|     Expires: Thu, 01 Jan 1970 00:00:00 GMT
|     Content-Type: text/html;charset=utf-8
|     Content-Length: 8628
|     <!DOCTYPE html>
|     <html prefix="og: http://ogp.me/ns#" lang="en">
|     <head>
|     <meta charset="UTF-8" />
|     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
|     <meta http-equiv="X-UA-Compatible" content="IE=edge" />
|     <title>GitBucket</title>
|     <meta property="og:title" content="GitBucket" />
|     <meta property="og:type" content="object" />
|     <meta property="og:url" content="http://10.10.11.33:8080/" />
|     <meta property="og:image" content="http://10.10.11.33:8080/assets/common/images/gitbucket_ogp.png" />
|     <link rel="icon" href="/assets/common/images/gitbucket.png?20250122011355" type=
|   HTTPOptions: 
|     HTTP/1.1 200 OK
|     Date: Wed, 22 Jan 2025 01:50:58 GMT
|     Set-Cookie: JSESSIONID=node01ofk192uti5h5aq8klj4ri7z273.node0; Path=/; HttpOnly
|     Expires: Thu, 01 Jan 1970 00:00:00 GMT
|     Content-Type: text/html;charset=utf-8
|     Allow: GET,HEAD,POST,OPTIONS
|     Content-Length: 0
|   RTSPRequest: 
|     HTTP/1.1 505 HTTP Version Not Supported
|     Content-Type: text/html;charset=iso-8859-1
|     Content-Length: 58
|     Connection: close
|_    <h1>Bad Message 505</h1><pre>reason: Unknown Version</pre>
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port80-TCP:V=7.94SVN%I=7%D=1/21%Time=67904EFD%P=x86_64-pc-linux-gnu%r(G
SF:etRequest,66,"HTTP/1\.1\x20301\x20Moved\x20Permanently\r\ncontent-lengt
SF:h:\x200\r\nlocation:\x20http://caption\.htb\r\nconnection:\x20close\r\n
SF:\r\n")%r(HTTPOptions,66,"HTTP/1\.1\x20301\x20Moved\x20Permanently\r\nco
SF:ntent-length:\x200\r\nlocation:\x20http://caption\.htb\r\nconnection:\x
SF:20close\r\n\r\n")%r(RTSPRequest,CF,"HTTP/1\.1\x20400\x20Bad\x20request\
SF:r\nContent-length:\x2090\r\nCache-Control:\x20no-cache\r\nConnection:\x
SF:20close\r\nContent-Type:\x20text/html\r\n\r\n<html><body><h1>400\x20Bad
SF:\x20request</h1>\nYour\x20browser\x20sent\x20an\x20invalid\x20request\.
SF:\n</body></html>\n")%r(X11Probe,CF,"HTTP/1\.1\x20400\x20Bad\x20request\
SF:r\nContent-length:\x2090\r\nCache-Control:\x20no-cache\r\nConnection:\x
SF:20close\r\nContent-Type:\x20text/html\r\n\r\n<html><body><h1>400\x20Bad
SF:\x20request</h1>\nYour\x20browser\x20sent\x20an\x20invalid\x20request\.
SF:\n</body></html>\n")%r(FourOhFourRequest,66,"HTTP/1\.1\x20301\x20Moved\
SF:x20Permanently\r\ncontent-length:\x200\r\nlocation:\x20http://caption\.
SF:htb\r\nconnection:\x20close\r\n\r\n")%r(RPCCheck,CF,"HTTP/1\.1\x20400\x
SF:20Bad\x20request\r\nContent-length:\x2090\r\nCache-Control:\x20no-cache
SF:\r\nConnection:\x20close\r\nContent-Type:\x20text/html\r\n\r\n<html><bo
SF:dy><h1>400\x20Bad\x20request</h1>\nYour\x20browser\x20sent\x20an\x20inv
SF:alid\x20request\.\n</body></html>\n")%r(DNSVersionBindReqTCP,CF,"HTTP/1
SF:\.1\x20400\x20Bad\x20request\r\nContent-length:\x2090\r\nCache-Control:
SF:\x20no-cache\r\nConnection:\x20close\r\nContent-Type:\x20text/html\r\n\
SF:r\n<html><body><h1>400\x20Bad\x20request</h1>\nYour\x20browser\x20sent\
SF:x20an\x20invalid\x20request\.\n</body></html>\n")%r(DNSStatusRequestTCP
SF:,CF,"HTTP/1\.1\x20400\x20Bad\x20request\r\nContent-length:\x2090\r\nCac
SF:he-Control:\x20no-cache\r\nConnection:\x20close\r\nContent-Type:\x20tex
SF:t/html\r\n\r\n<html><body><h1>400\x20Bad\x20request</h1>\nYour\x20brows
SF:er\x20sent\x20an\x20invalid\x20request\.\n</body></html>\n")%r(Help,CF,
SF:"HTTP/1\.1\x20400\x20Bad\x20request\r\nContent-length:\x2090\r\nCache-C
SF:ontrol:\x20no-cache\r\nConnection:\x20close\r\nContent-Type:\x20text/ht
SF:ml\r\n\r\n<html><body><h1>400\x20Bad\x20request</h1>\nYour\x20browser\x
SF:20sent\x20an\x20invalid\x20request\.\n</body></html>\n");
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port8080-TCP:V=7.94SVN%I=7%D=1/21%Time=67904EFD%P=x86_64-pc-linux-gnu%r
SF:(GetRequest,19E6,"HTTP/1\.1\x20200\x20OK\r\nDate:\x20Wed,\x2022\x20Jan\
SF:x202025\x2001:50:54\x20GMT\r\nSet-Cookie:\x20JSESSIONID=node0xzxhhmrrps
SF:8t141p5ws0tcz0x72\.node0;\x20Path=/;\x20HttpOnly\r\nExpires:\x20Thu,\x2
SF:001\x20Jan\x201970\x2000:00:00\x20GMT\r\nContent-Type:\x20text/html;cha
SF:rset=utf-8\r\nContent-Length:\x208628\r\n\r\n<!DOCTYPE\x20html>\n<html\
SF:x20prefix=\"og:\x20http://ogp\.me/ns#\"\x20lang=\"en\">\n\x20\x20<head>
SF:\n\x20\x20\x20\x20<meta\x20charset=\"UTF-8\"\x20/>\n\x20\x20\x20\x20<me
SF:ta\x20name=\"viewport\"\x20content=\"width=device-width,\x20initial-sca
SF:le=1\.0,\x20maximum-scale=5\.0\"\x20/>\n\x20\x20\x20\x20<meta\x20http-e
SF:quiv=\"X-UA-Compatible\"\x20content=\"IE=edge\"\x20/>\n\x20\x20\x20\x20
SF:<title>GitBucket</title>\n\x20\x20\x20\x20<meta\x20property=\"og:title\
SF:"\x20content=\"GitBucket\"\x20/>\n\x20\x20\x20\x20<meta\x20property=\"o
SF:g:type\"\x20content=\"object\"\x20/>\n\x20\x20\x20\x20<meta\x20property
SF:=\"og:url\"\x20content=\"http://10\.10\.11\.33:8080/\"\x20/>\n\x20\x20\
SF:x20\x20\n\x20\x20\x20\x20\x20\x20<meta\x20property=\"og:image\"\x20cont
SF:ent=\"http://10\.10\.11\.33:8080/assets/common/images/gitbucket_ogp\.pn
SF:g\"\x20/>\n\x20\x20\x20\x20\n\x20\x20\x20\x20\n\x20\x20\x20\x20<link\x2
SF:0rel=\"icon\"\x20href=\"/assets/common/images/gitbucket\.png\?202501220
SF:11355\"\x20type=")%r(HTTPOptions,109,"HTTP/1\.1\x20200\x20OK\r\nDate:\x
SF:20Wed,\x2022\x20Jan\x202025\x2001:50:58\x20GMT\r\nSet-Cookie:\x20JSESSI
SF:ONID=node01ofk192uti5h5aq8klj4ri7z273\.node0;\x20Path=/;\x20HttpOnly\r\
SF:nExpires:\x20Thu,\x2001\x20Jan\x201970\x2000:00:00\x20GMT\r\nContent-Ty
SF:pe:\x20text/html;charset=utf-8\r\nAllow:\x20GET,HEAD,POST,OPTIONS\r\nCo
SF:ntent-Length:\x200\r\n\r\n")%r(RTSPRequest,B8,"HTTP/1\.1\x20505\x20HTTP
SF:\x20Version\x20Not\x20Supported\r\nContent-Type:\x20text/html;charset=i
SF:so-8859-1\r\nContent-Length:\x2058\r\nConnection:\x20close\r\n\r\n<h1>B
SF:ad\x20Message\x20505</h1><pre>reason:\x20Unknown\x20Version</pre>")%r(F
SF:ourOhFourRequest,1812,"HTTP/1\.1\x20404\x20Not\x20Found\r\nDate:\x20Wed
SF:,\x2022\x20Jan\x202025\x2001:51:03\x20GMT\r\nSet-Cookie:\x20JSESSIONID=
SF:node01re4rwzjxaips1slx525zcd2y074\.node0;\x20Path=/;\x20HttpOnly\r\nExp
SF:ires:\x20Thu,\x2001\x20Jan\x201970\x2000:00:00\x20GMT\r\nContent-Type:\
SF:x20text/html;charset=utf-8\r\nContent-Length:\x205916\r\n\r\n<!DOCTYPE\
SF:x20html>\n<html\x20prefix=\"og:\x20http://ogp\.me/ns#\"\x20lang=\"en\">
SF:\n\x20\x20<head>\n\x20\x20\x20\x20<meta\x20charset=\"UTF-8\"\x20/>\n\x2
SF:0\x20\x20\x20<meta\x20name=\"viewport\"\x20content=\"width=device-width
SF:,\x20initial-scale=1\.0,\x20maximum-scale=5\.0\"\x20/>\n\x20\x20\x20\x2
SF:0<meta\x20http-equiv=\"X-UA-Compatible\"\x20content=\"IE=edge\"\x20/>\n
SF:\x20\x20\x20\x20<title>Error</title>\n\x20\x20\x20\x20<meta\x20property
SF:=\"og:title\"\x20content=\"Error\"\x20/>\n\x20\x20\x20\x20<meta\x20prop
SF:erty=\"og:type\"\x20content=\"object\"\x20/>\n\x20\x20\x20\x20<meta\x20
SF:property=\"og:url\"\x20content=\"http://10\.10\.11\.33:8080/nice%20port
SF:s%2C/Tri%6Eity\.txt%2ebak\"\x20/>\n\x20\x20\x20\x20\n\x20\x20\x20\x20\x
SF:20\x20<meta\x20property=\"og:image\"\x20content=\"http://10\.10\.11\.33
SF::8080/assets/common/images/gitbucket_ogp\.png\"\x20/>\n\x20\x20\x20\x20
SF:\n\x20\x20\x20\x20\n\x20\x20\x20\x20<link\x20rel=\"icon\"\x20href=\"/as
SF:sets/common/images/");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 800.71 seconds

Add the caption.htb domain in /etc/hosts file.

http - port 80 [ caption.htb ]

The website is presented with caption portal login.

Using the credentials to login found in Caption-Portal repository commit form here.

Credentials: margo / vFr&cS2#0!

The /logs gives us the 403 forbidden and /firewall gives us page without any endpoints.

http - port 8080 [ caption.htb ]

The port 8080 is used to host the website for gitbucket service.

The gitbucket has two repository the Caption-Portal and Logservice.

Repository - Caption-Portal

The repository Caption-Portal uses the haproxy and varnish services.

HAProxy is a free and open-source software that acts as a high availability load balancer and proxy for TCP and HTTP-based applications.

Repository - Logservice

The repository Logservice uses the thrift.

Thrift provides a remote procedure call (RPC) framework and includes a code generation engine that can produce clients and servers in different languages from a single interface definition file. This allows developers to build interoperable services that can communicate seamlessly across different programming environments.

The Caption-Portal previous commits contains the margo credentials for http port 80 in haproxy.cfg.

Commit where margo credentials is found
/root/Caption-Portal/blob/98f2c36131078270dd3e11865f7a559d488666a1/config/haproxy/haproxy.cfg

Foothold

Varnish - h2c smuggling [ http port 80 ]

The varnish cache 6.6 is vulnerable to CVE-2021-26740 h2c smuggling while upgrading to h2c. The h2c smuggling is a technique that exploits the HTTP/1.1 to HTTP/2 cleartext (h2c) upgrade mechanism to bypass security controls. For more details, you can visit to this blog arrow-up-rightpost.

1

Testing h2c smuggling

The BishopFox has created script for h2c smuggling in this github repositoryarrow-up-right.

Testing the h2c smuggling gives us positive result.

2

Trying to access /logs

Using h2c smuggling for accessing /logs redirects us to the login page.

3

The margo JWT in session cookie redirects us to /?err=role_error.

1

Intercepting the /firewall endpoint.

The /firewall endpoint in burpsuite reveals us the x-varnish header which caches the previously made request on firewall page. The source code includes the none existence JavaScript file, which includes the utm_source parameter. The utm_source has http://internal-proxy.local value, which could be used to alter the source using proxy configuration.

2

Adding X-Forwarded-Host

Adding the X-Forwarded-Host in /firewall request to see whether the utm_source value changes.

The value of utm_source changes from http://internal.proxy.local to http://10.10.16.28. The parameter is also vulnerable to XSS payloads, which we can use for getting administrator cookie in 120 seconds because of varnish cache max-age set to 120.

3

Getting Admin JWT

Intercept the /firewall request in burpsuite and add the following payload in request header.

Send the request to repeater Ctrl + R, send the request from the repeater.

Open the nc listener in port 8443, wait for 120 seconds and refresh the /firewall endpoint.

Shell - marcus [ Copypart Path Traversal ]

Analyzing the log reveals the download endpoint with url parameter and services or application running in 127.0.0.1:3923.

Downloading the http://127.0.0.1:3923 reveals that the copypart is running which we can get from this github repositoryarrow-up-right.

CVE-2023-37474

The copypart is vulnerable to CVE-2023-37474 path traversal attack through /.cpr subfolder. More details about it is found herearrow-up-right.

While trying to fetch some of the files in .ssh folder in margo home directory I got the id_ecdsa file by which we will get the ssh shell as margo.

Copy the private key in the file and change the permission to 400 using chmod.

circle-info

The user.txt file contains the user flag 👏


Privilege Escalation

Pillaging - margo [ user ]

The service and process enumeration reveals that the root is running server.go process and 9090 port is open, which is running Logservice which we get to know from Gitbucket Logservice repository.

Shell - root [ Thrift Exploitation ]

The server.go file form Gitbucket Logservice repository. The code is used to open the file and compile it and output the file. The line 43 contains the bash system commands which could be exploited.

1

Port forward the 9090 port via SSH

2

Get the Logservice repository which is present in Gitbucket

3

Install the thrift-compiler in local machine

4

Create python virtual environment, install thrift and change directory to Logservice

5

Generate program using thrift-compiler

The gen-py folder is generated

6

Create the malicious log file in margo's shell /tmp folder

7

Change directory into gen-py and create the malicious python script to interact with the thrift

Execute the created python script.

8

In margo's shell execute the following commands

circle-info

The root.txt file is present in /root directory which contains the root flag 🎉


Proof of Concept

The below video provides the PoC of Caption machine.

Last updated