About 10 years ago I wanted to write a telnet client in PHP. My need was to login, execute some commands and logout. I was lazy to read the telnet protocol details. Hence I used the following approach to do it which worked for my purpose. Let your programming language be Java, C, C++, PHP, C# or anything else. It needs to have a TCP socket creation facility (TCP networking).
Here are the steps:
- Have the telnet server running.
- Use a network traffic monitoring tool running (or use a proxy).
- Use a commonly availale telnet client and login to the server.
- Stop the network traffic monitoring tool and identify the client/server conversation byte streams. Identify the different chunks of data sent to the server (by the client) and to the client (by the server).
- Write your telnet client (in your preferred programming language) to send the same chunks of bytes to the server and implement the conversation.
Special Note: This approach is not that great if you think of using your new telnet client with different telnet servers (which may have different telnet session negotiation mechanism) or if the telnet server software is updated and server configurations are changed where the same chunks of bytes may not work after such changes. If such a general purpose telnet client is needed and you want to write from the scratch, you need read the telnet protocol details well.
Here's the telnet client I wrote in PHP. Look at the connect() function. You see that a new TCP socket is created and chunks of bytes are sent to the server and read from the server. That is in fact the handshaking taking place. I didn't go about understanding the exact meaning of these messages. I just wanted to get the login prompt. And then I continuted with executing commands.
<?php class Telnet { var $host; var $port; var $login; var $connection; var $errno; var $errstr; var $bashPrompt = "apacheTelnetPrompt"; var $timeout = 30; // Set this to 30 function Telnet($host,$port) { $this->host = $host; $this->port = $port; } function connect() { $this->connection =fsockopen($this->host, $this->port, $this->errno, $this->errstr, 30); fread($this->connection, 3); $send = array(255,251,24); $this->sendBytes($send); fread($this->connection, 9); $send = array(255,251,32,255,252,35,255,251,39); $this->sendBytes($send); fread($this->connection, 18); $send = array(255, 250, 32, 0, 51, 56, 52, 48, 48, 44, 51, 56, 52, 48, 48, 255, 240, 255, 250, 39, 0, 255, 240, 255, 250, 24, 0, 76, 73, 78, 85, 88, 255, 240); $this->sendBytes($send); fread($this->connection, 15); $send = array(255, 253, 3, 255, 252, 1, 255, 251, 31, 255, 250, 31, 0, 80, 0, 25, 255, 240, 255, 253, 5, 255, 251, 33); $this->sendBytes($send); // Get the login prompt if($this->expect("login: ")) return TRUE; else return FALSE; } // Loing method // Accepts two strings (login name , password) function login($login, $password) { $this->login = $login; $send = array(255, 253, 1); $this->sendBytes($send); // Send the login name here $this->send($this->login."\r".chr(0)); // Expect the Password prompt and send the password if(!$this->expect("Password: ")) return FALSE; // Send the password $this->send($password."\r".chr(0)); // Expect the success in login if(!$this->expect("Last login: ")) return FALSE; // Set the prompt $this->send("export PS1=$this->bashPrompt\r".chr(0)); // Read the echoed output if(!$this->expect("export PS1=$this->bashPrompt\r\n")) return false; // Expect the changed prompt if(!$this->expect($this->bashPrompt)) return FALSE; // You are logged in, return true return true; } // Accepted: an array of integers function sendBytes($send) { for($i = 0; $i < sizeof($send); $i++) if(fwrite($this->connection, chr($send[$i])) == -1) return false; return true; } function expect($expected) { while(true) { $read = $this->getCharWithTimeout($this->timeout); if(!$read) return false; if($read != $expected[0]) continue; $index = 1; while($index < strlen($expected)) { $read = $this->getCharWithTimeout($this->timeout); if(!$read) return false; if($read == $expected[$index]) $index++; else if($read == $expected[0]) $index = 1; else continue 2; } return true; } } // Send a string to the socket // Accepted: the string to be sent function send($string) { if(fwrite($this->connection, $string) == -1) return false; else return true; } function getCharWithTimeout($timeout) { socket_set_blocking($this->connection, false); for($i = 0; $i < $timeout; $i++) { $read = fgetc($this->connection); // remove the line below //echo $read; if($read) { socket_set_blocking($this->connection, true); return $read; } else sleep(1); } socket_set_blocking($this->connection, true); return false; } function isBashPrompt($value) { if($value == $this->bashPrompt) return TRUE; else return FALSE; } function getExitStatus() { $this->flushInput(); $this->send("echo $?\r".chr(0)); $this->expect("echo $?\r\n"); // Read the echoed output $status = fgets($this->connection, 4096); return $status; } // Reads all the content present at the input function flushInput() { $all; $info = socket_get_status($this->connection); for($i = 0; $i < $info['unread_bytes']; $i++) $all .= fgetc($this->connection); return $all; } function logout() { $this->send("exit\r".chr(0)); usleep(25000); $this->flushInput(); fclose($this->connection); } } ?>
What follows is the usage of the telnet client. This was used as a form submission handling script to get a new Linux account created after initializing the telnet session. What's in bold text is the usage of the above Telnet class.
<?php /* Telnet to localhost and create a new account. Accepts the following HTTP POST variables: $adduserargs - arguments to adduser command $privilage_login - login name of the privilaged person who can create accounts $privilage_password $submit - form submitted */ $exitcode = 101; // Match the pattern of $adduserargs // Set $exitcode to 102 if cannot be matched $pattern = ".*[;&|><`].*"; if(ereg($pattern, $adduserargs)) $exitcode = 102; if($submit && ($exitcode == 101)) { include('Telnet.php'); // Telnet to the machine $t = new Telnet("localhost", 23); if($t->connect()) { if($t->login($privilage_login, $privilage_password)) { // Gain superuser privilage $t->send("sudo -v\r".chr(0)); if($t->expect("Password:")) { $t->send("$privilage_password\r".chr(0)); if($t->expect($t->bashPrompt)) { // Send the command $adduserargs = stripslashes($adduserargs); $t->send("sudo /usr/sbin/adduser $adduserargs\r".chr(0)); // If bash prompt cannot be seen, break the command if(!$t->expect($t->bashPrompt)) { $t->send(chr(3)); fread($t->connection,3); } } else { $t->send(chr(3)); fread($t->connection,3); } } else { $t->send(chr(3)); fread($t->connection,3); } $exitcode = (int) $t->getExitStatus(); // Kill the sudo session $t->send("sudo -K\r".chr(0)); $t->flushInput(); $t->logout(); } // if logged in ends } // if Connected ends } // if submitted ends echo $exitcode;