TryHackMe Writeup: Hygiene

Recently, I’ve been focusing on some courses over TryHackMe; but, I saw that Stunsnroses recently made a challenge room, called Hygiene, that sounded interesting. You should check Stunsnroses out on YouTube as well. He has excellent videos that focus on very specific aspects of ethical hacking, and common tools that are used. This writeup will seem short compared to my other ones, but that is because I’m sparing the details of everything that I tried. This one took some looking around, and attempts to identify common web app vulnerabilities that didn’t end up being present, like SQL injection or path traversal vulnerabilities. It’s still good practice to search for all vulnerabilities.
Concepts
This is a great TryHackMe room for practicing the following concepts:
- Service enumeration
- Web enumeration
- File enumeration
- Security hygiene
Suggested Tools
Here are some ‘recommended’ tools for this CTF:
- nmap (as usual)
- a web enumeration tool (I used Gobuster, but Dirbuster, Drib, Feroxbuster all work well)
- A web browser
- John the Ripper (or similar)
The Problem
Paraphrasing the TryHackMe instructions, our task is to enumerate all services that are running on the target machine to find “low hanging fruit.” Once we have gained access as a low privileged user, we need to try to root the machine. We are asked to answer some questions along the way, and there are a total of three flags that we need to find. A “user” flag, an “enumeration” flag, and a “root” flag.
My Solution
As usual on TryHackMe, we begin with the IP address of the target, which I like to store in an environment variable, TIP. We don’t need to perform a ping scan, since we already know the target IP, but we want to go ahead and run an nmap service detection scan.
# nmap -vv -p1-65535 -O -sV $TIP | tee os-service-nmap.log
Remember to scan all possible ports (we could have used -p-
instead of -p1-65535
, by the way), and to run as root. We can also go ahead and try to detect the OS, with -O
, as well. From this, we find three services running on the target:
Nmap scan report for <Target IP address>
Host is up, received timestamp-reply ttl 61 (0.12s latency).
Scanned at 2021-11-03 09:27:25 EDT for 516s
Not shown: 65532 closed ports
Reason: 65532 resets
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 61 OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
8080/tcp open http-proxy syn-ack ttl 61
37652/tcp open ftp syn-ack ttl 61 ProFTPD 1.3.5e
Nmap was not able to detect the OS directly, but we can see that the version of OpenSSH running on port 22 is associated with Ubuntu, so that it probably the correct OS.
We have 3 services that we need to investigate. FTP, SSH, and port 8080 that is usually associated with HTTP. We can start with port 8080 which we can access using a web browser.
We see an Apache Tomcat 9 test page, but with an additional note from Joe at the top of the page as well as a picture (see image below). There is nothing else that is out of the ordinary here, even when checking the page source code. At this point, I tried to see if I could traverse paths on the target machine by making GET requests for ‘/../../’, etc (with and without URL encoding, even trying double-encoding!), but not luck. I also tested for SQL injection in the header parameters. Again, no luck.

Next, we want to enumerate the directories and files that are accessible via the web server. I used Gobuster for this, but there are several alternatives that work as well.
$ gobuster dir -u http://$TIP:8080 -x php,py,html,js,gif,jpg,css,txt,xxa -w /usr/share/wordlists/dirb/common.txt | tee gobuster_dir.log
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://<Target IP here>:8080
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: php,html,js,gif,jpg,py,css,txt,xxa
[+] Timeout: 10s
===============================================================
2021/11/03 09:45:12 Starting gobuster in directory enumeration mode
===============================================================
/admin (Status: 302) [Size: 0] [--> /admin/]
/host-manager (Status: 302) [Size: 0] [--> /host-manager/]
/images (Status: 302) [Size: 0] [--> /images/]
/index.html (Status: 200) [Size: 2088]
/index.html (Status: 200) [Size: 2088]
/manager (Status: 302) [Size: 0] [--> /manager/]
===============================================================
2021/11/03 09:51:31 Finished
===============================================================
We identify 4 directories that exist, admin, host-manager, manager, and images. Gobuster will not search recursively, so we need to enumerate each directory that we found, as well as any subdirectories, by running Gobuster again. This is one place where a Stunsroses video on web enumeration was very helpful. We can use the same syntax as above to enumerate the subdirectories, we just need to update the URL to include the directory name. Here is an example of enumerating the ‘/admin/’ directory which turns out to be the only interesting directory that I found:
$ gobuster dir -u http://$TIP:8080/admin -w /usr/share/wordlists/dirb/common.txt -x txt,log,bak,old,html,gif,jpg | tee admin-enum.log
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://<Target IP here>:8080/admin
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: txt,log,bak,old,html,gif,jpg
[+] Timeout: 10s
===============================================================
2021/11/19 16:45:23 Starting gobuster in directory enumeration mode
===============================================================
/admin.html (Status: 200) [Size: 3456]
/staging (Status: 302) [Size: 0] [--> /admin/staging/]
===============================================================
2021/11/19 16:51:07 Finished
===============================================================
The ‘admin.html’ file only contains some basic html with no interesting content, but we found another directory ‘/admin/staging’ which we need to enumerate. Using Gobuster once more reveals another file:
$ gobuster dir -u http://$TIP:8080/admin/staging -w /usr/share/wordlists/dirb/common.txt -x txt,log,bak,old,html,gif,jpg | tee admin-staging-enum.log
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://<Target IP here>:8080/admin/staging
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: log,bak,old,html,gif,jpg,txt
[+] Timeout: 10s
===============================================================
2021/11/19 17:04:11 Starting gobuster in directory enumeration mode
===============================================================
/index1.html (Status: 200) [Size: 7491]
If we navigate to http://$TIP/admin/staging/index1.html
, we see the following:

Viewing the page source (with the developer tools, or right-click “view page source”), we see that a person named Sally is referenced. At this point, we have two possible usernames based on the names that we have seen, sally
, and joe
.
After enumerating the files that are accessible via the web server, we can try the FTP service which sometimes allows anonymous logins with no password. nmap can check this for you if you use the “aggresive” option, -A
, to scan an FTP port. If anonymous login is allowed, nmap will also list the contents of the FTP directory.
$ nmap -A -p22,8080,37652 $TIP | tee nmap-aggressive.log
Starting Nmap 7.91 ( https://nmap.org ) at 2021-11-11 18:58 EST
Nmap scan report for <Target IP here>
Host is up (0.13s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 a0:45:a9:03:7d:7d:a8:82:c9:40:1f:f8:91:de:71:14 (RSA)
| 256 01:2f:0b:3a:c9:c4:1b:b1:20:06:a7:4f:5e:d8:27:72 (ECDSA)
|_ 256 5e:cf:09:82:77:aa:54:ac:0e:3a:79:41:6b:82:d3:99 (ED25519)
8080/tcp closed http-proxy
37652/tcp open ftp ProFTPD 1.3.5e
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-r--r-- 1 1000 1000 118 Oct 29 02:21 memo.txt
Service Info: OSs: Linux, Unix; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 10.21 seconds
When I did the challenge, I manually checked for anonymous login without a password by simply attempting to log in that way. Both methods work, but it is nice to automate these simple checks. In any case, next, we need to log in via FTP with username “anonymous”. When prompted for a password, leave the field blank and hit ‘Enter’.
$ ftp $TIP 37652
Connected to <Target IP here>.
220 ProFTPD 1.3.5e Server (ftp) [<Target IP here>]
Name (<Our IP here>:kali): anonymous
331 Anonymous login ok, send your complete email address as your password
Password:
230 Anonymous access granted, restrictions apply
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pwd
257 "/" is the current directory
ftp> ls
200 PORT command successful
150 Opening ASCII mode data connection for file list
-rw-r--r-- 1 1000 1000 118 Oct 29 02:21 memo.txt
We see the memo.txt file that nmap was able to find. We can “get” a copy of the file with:
ftp> get memo.txt
local: memo.txt remote: memo.txt
200 PORT command successful
150 Opening BINARY mode data connection for memo.txt (118 bytes)
226 Transfer complete
118 bytes received in 0.00 secs (190.1557 kB/s)
Looking at the contents of the memo.txt file on our local machine (it should be in the same directory that you started the FTP session from), we see that it contains a note from Joe:
Hey,
I reset your password, but I wanted to tell you what it was "securely"
ac754< more hash data >a91
-Joe
Apparently Joe reset someone’s password, and wanted to “securely” tell them what their password is by giving them a password hash. This is very poor security hygiene since the memo is publicly accessible, and Joe clearly expects the password hash to be crackable; otherwise, Joe would not have attempted to give another user a password via a hash that is difficult to crack. So, we will go ahead and crack this hash ourselves using John the Ripper (or equivalent). It looks like this is an MD5 hash based on the length. We can try to crack this against the rockyou wordlist. I put the hash in a file called “mystery-hash.dat”.
$ john --wordlist=/usr/share/wordlists/rockyou.txt mystery-hash.dat --format=Raw-MD5
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=4
Press 'q' or Ctrl-C to abort, almost any other key for status
n< the user password >e (?)
And, success! At this point, we have a password and some possible usernames. We can try to log in via ssh using the credentials that we found. This could easily work in a realistic situation since many people reuse usernames and passwords (we don’t know for sure that either of these are ssh credentials until we try them). It turns out that we can ssh in to the target machine using the username ‘sally’, and the password that we just cracked.
$ ssh sally@$TIP
The authenticity of host '<Target IP Here> (<Target IP Here>)' can't be established.
ECDSA key fingerprint is SHA256:Q07Qey5yjaWQMtoSSb+DB0Q8waEWrRiX2osyfzsz2Ls.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '<Target IP Here>' (ECDSA) to the list of known hosts.
sally@<Target IP Here>'s password:
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-161-generic x86_64)
Next, we want to enumerate the users, and look for the user flag.
sally@hygiene:/$ cd /home/
sally@hygiene:/home$ ls -al
total 16
drwxr-xr-x 4 root root 4096 Oct 26 07:04 .
drwxr-xr-x 24 root root 4096 Oct 26 08:31 ..
drwxr-xr-x 18 joe joe 4096 Oct 29 02:13 joe
drwxr-xr-x 17 sally sally 4096 Oct 26 07:59 sally
It looks like only ‘joe’ and ‘sally’ are normal users on this machine. Next, we can look for the flag in /home/sally
.
sally@hygiene:/home/sally$ ls -al
total 140
drwxr-xr-x 17 sally sally 4096 Oct 26 07:59 .
drwxr-xr-x 4 root root 4096 Oct 26 07:04 ..
-rw------- 1 sally sally 144 Oct 26 09:03 .bash_history
-rw-r--r-- 1 sally sally 220 Oct 26 07:04 .bash_logout
-rw-r--r-- 1 sally sally 3771 Oct 26 07:04 .bashrc
drwxrwxr-x 8 sally sally 4096 Oct 26 08:01 .cache
drwxrwxr-x 8 sally sally 4096 Oct 26 08:55 .config
drwx------ 3 sally sally 4096 Oct 26 07:53 .dbus
drwxr-xr-x 2 sally sally 4096 Oct 26 22:45 Desktop
drwxr-xr-x 2 sally sally 4096 Oct 26 07:53 Documents
drwxr-xr-x 2 sally sally 4096 Oct 26 07:53 Downloads
drwx------ 2 sally sally 4096 Oct 26 07:53 .gconf
drwx------ 3 sally sally 4096 Oct 26 08:56 .gnupg
-rw-rw-r-- 1 sally sally 375 Oct 26 07:53 .gtkrc-2.0
drwxrwxr-x 3 sally sally 4096 Oct 26 07:53 .kde
drwxrwxr-x 3 sally sally 4096 Oct 26 07:53 .local
drwxr-xr-x 2 sally sally 4096 Oct 26 07:53 Music
drwxr-xr-x 2 sally sally 4096 Oct 26 07:53 Pictures
-rw-r--r-- 1 sally sally 807 Oct 26 07:04 .profile
drwxr-xr-x 2 sally sally 4096 Oct 26 07:53 Public
drwxr-xr-x 2 sally sally 4096 Oct 26 07:53 Templates
drwxr-xr-x 2 sally sally 4096 Oct 26 07:53 Videos
-rw------- 1 sally sally 52 Oct 26 07:53 .Xauthority
-rw------- 1 sally sally 45477 Oct 26 08:01 .xsession-errors
It isn’t here, but, looking through the subdirectories, we can ultimately find the flag in /home/sally/Desktop
.
sally@hygiene:/home/sally/Desktop$ ls
Home.desktop trash.desktop user.txt
sally@hygiene:/home/sally/Desktop$ cat user.txt
user_<rest of flag here>u
We want to continue looking for interesting files, and especially the enumeration flag. We can automate this using the find
command if we know what to look for. In this case, I manually searched through the subdirs in /home/sally
. We eventually find the flag in /home/sally/.local/share/Trash/files
.
sally@hygiene:/home/sally/.local/share/Trash/files$ ls -al
total 16
drwxrwxr-x 2 sally sally 4096 Oct 26 09:00 .
drwxrwxr-x 4 sally sally 4096 Oct 26 07:53 ..
-rw-r--r-- 1 root root 17 Oct 26 09:00 enum.txt
-rw-r--r-- 1 sally sally 200 Oct 26 07:59 joememo
sally@hygiene:/home/sally/.local/share/Trash/files$ cat enum.txt
trash_n<rest of flag here>A
Always remember to empty the trash! There can be all kinds of sensitive information there. In addition to the enumeration flag, we see a file called “joememo”. Let’s take a look at the file:
sally@hygiene:/home/sally/.local/share/Trash/files$ cat joememo
Hey sally, the developers need to get working on the website, but they are having issues connecting to it. Can you log into my account and restart the service?
b0<the rest of the hash>a28
-Joe
We see yet another password hash. Again, this is probably an easily crackable password hash or joe wouldn’t be able to reliably give out passwords this way. We can use John the Ripper again to crack the hash, after saving it to a file, ‘joe-hash.dat’
$ john --wordlist=/usr/share/wordlists/rockyou.txt joe-hash.dat --format=RAW-MD5
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=4
Press 'q' or Ctrl-C to abort, almost any other key for status
s< rest of password here >d (?)
We can try changing users to Joe using this password with $ su joe
which works. Since joe is apparently an admin based on the fact that he sets and distributes passwords, let’s see what he can run as root via the sudo command:
$ sudo -l
[sudo] password for joe:
Matching Defaults entries for joe on hygiene:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User joe may run the following commands on hygiene:
(ALL : ALL) ALL
Joe can run anything, and we have his password. We have effectively rooted the machine.
Next, we look for the root flag,
joe@hygiene:/$ sudo find / -type f -name root.txt
/root/root.txt
and retrieve it:
joe@hygiene:/$ sudo cat /root/root.txt
root_a<rest of flag here>Y
And that concludes the solution.
What I Got out of this Challenge
I really enjoyed this challenge, and thought it was a great opportunity to practice enumeration techniques. I got more familiar with gobuster. Specifically, I learned that it does not enumerate directories recursively, so you need to specifically run it for each directory that you find. Other tools do run recursively by default, but that is time consuming. This was also an excellent demonstration of the value of fully enumerating a target before attempting to exploit it. If a pen. tester fully enumerated the services running on this machine, as well as the files/directories which are accessible via the web app, then the rest of the process is fairly quick and easy. Poor security hygiene is very common so the focus on that theme in this challenge is very practical.
I hope you enjoyed this writeup. As always, If you did something differently, I’d love to hear about it. Connect with me on LinkedIn, and share your writeup!