~ / writeups / cohort

Cohort

Hack The Box · Easy · Linux

Easy Linux User + Root owned

Machine Overview

Field Value
Name Cohort
Difficulty Easy
OS Linux (Ubuntu, nginx 1.24.0)
IP 10.129.146.191
Vhosts cohort.htb (portal), nb-1be3782a8afd3ad5.cohort.htb (internal Marimo)
Key components Flask "cohort-insights" API (127.0.0.1:5000), Marimo 0.20.4 (127.0.0.1:8888), PackageKit 1.2.8
Open ports 22 (SSH OpenSSH 9.6p1), 80/443 (nginx; *.cohort.htb wildcard cert)

Flags

Flag Path Value
User /home/marimo/user.txt [REDACTED]
Root /root/root.txt [REDACTED]

Attack Chain

1. Reconnaissance

  • nmap -p- --min-rate 5000 → 22 (SSH) + 80/443 (nginx). Domain cohort.htb, wildcard *.cohort.htb cert.
  • Directory enumeration → portal.html (a "validate source URL" feature), assets/app.js (obfuscated), /status and /assets/ return 403 externally.

2. SSRF via URL-validation feature

  • POST /api/validate with {"url": "...", "format": "json"} fetches a user-supplied URL server-side.
  • The loopback blocklist only matches localhost/127.0.0.1 by name. Bypassed with alternate representations:
  • decimal: http://2130706433/status (2130706433 = 0x7F000001)
  • hex: http://0x7F.0x00.0x00.0x01/
  • short: http://127.1/
  • Abused the SSRF to hit nginx's internal /status endpoint → leaked the upstream map, including an internal Marimo notebook vhost: nb-1be3782a8afd3ad5.cohort.htb → 127.0.0.1:8888.

3. Marimo enumeration

  • /api/version → Marimo 0.20.4.
  • Confirmed the vhost is also reachable externally (nginx reverse-proxies Host: nb-…cohort.htb to 127.0.0.1:8888).

4. Initial Access — CVE-2026-39987 (Marimo pre-auth WebSocket RCE)

  • Marimo's /terminal/ws WebSocket endpoint skips validate_auth() (other endpoints like /ws enforce it), so an unauthenticated client gets a full PTY shell as the marimo user.
  • Connected with websocket-client (wss://nb-1be3782a8afd3ad5.cohort.htb/terminal/ws, cert ignored + DNS override), dropped straight into a marimo@cohort shell.

5. User Flag

  • cat /home/marimo/user.txt → [REDACTED].

6. Privilege Escalation — CVE-2026-41651 (PackageKit "Pack2TheRoot" TOCTOU)

  • dpkg -l | grep packagekit → PackageKit 1.2.8 (vulnerable range 1.0.2–1.3.4).
  • PoC (mawussid/CVE-2026-41651-Python) abuses a TOCTOU in PackageKit's D-Bus transaction handling: 1. InstallFiles(SIMULATE, dummy.deb) — polkit bypassed, idle queued. 2. InstallFiles(NONE, payload.deb) — cached transaction overwritten. 3. GLib idle fires → pk_transaction_run reads the overwritten payload → postinst runs as root → drops a SUID bash.
  • Ran python3 cve-2026-41651.py --exec 'cat /root/root.txt' → root flag [REDACTED] (verified euid=0 via /tmp/.suid_bash -p -c id).

Techniques Demonstrated

  1. Directory/content enumeration → hidden portal
  2. SSRF with loopback-blocklist bypass (decimal/hex/short IP representations)
  3. Internal nginx status endpoint disclosure (upstream map → internal vhost)
  4. Reverse-proxy vhost pivoting (reaching a loopback-bound service over its vhost name)
  5. Pre-auth WebSocket RCE (CVE-2026-39987, Marimo /terminal/ws)
  6. PackageKit TOCTOU race → SUID binary (CVE-2026-41651, Pack2TheRoot)

Tools Used

nmap, curl, Python 3 (requests/websocket-client), cve-2026-39987 terminal WebSocket PoC, CVE-2026-41651-Python (Pack2TheRoot) PoC.

← all writeups