Telnet


Telnet Protocol
Purpose Remote shell access.
Standard RFC 854
Runs atop TCP/IP
Port number 23
Libraries pexpect, telnetlib
Exceptions socket.error, socket.gaierror, EOFError, select.error

Telnet overview

“Telnet is an application layer protocol used on the Internet or local area networks to provide a bidirectional interactive text-oriented communication facility using a virtual terminal connection.”

For more perspective on command-line & interactive protocols: www.cryptonomicon.com/beginning.html

Why NOT to use telnet?

  • Telnet is a terribly insecure protocol. It’s a plain-text protocol, anyone watching your Telnet packets on the wire will see your username, password, and everything you do on the remote system.
  • A lot of Telnet have no authentication that would ensure communication is carried out between the two desired hosts and not intercepted in the middle.
  • Several critical vulnerabilities have been discovered over the years in commonly used Telnet daemons.
  • Telnet is inefficient when it comes to anything other than remote shell access, like file transfer, authentication management etc.
  • For the above reasons Telnet is largely replaced by SSH and ‘r’-family of protocols(rlogin, rsh, and rcp).

Why telnet?

  • Telnet is fairly prevalent in networking devices(routers/modems) that sit deep inside a well-firewalled corporate network.
  • Telnet is still a popular way to access remote shell in many embedded(IoT) devices.

A study on state of telnet in embedded systems (2016) - Telnet is not dead

Scripting Telnet

  • Telnet is an interactive protocol that provides a virtual terminal for interaction. We’ll try to automate the interaction part using python scripting.
  • Telnet is so hard to script because it’s lacks serious standardization with regards to username and password prompts, error messages or responses. One script might not fit all the telnet implementations.
  • Scripting Telnet is like playing a purely textual game. You recieve some text and based on it you reply with something intelligible to the server.

expect

Expect is a tool for automating interactive applications such as telnet, ftp, passwd, fsck, ssh, etc.

  • Expect allows you to turn a normally interactive-only process in to a completely non-interactive, automated task.

Expect expect commands used to automate any interactive process are:

  • spawn – to start the command
  • expect – wait for the specific string from the process
  • send – to send the strings to the process
# Assume $remote_server, $my_user_id, $my_password, and $my_command were read in earlier
# in the script.
# Open a telnet session to a remote server, and wait for a username prompt.
spawn telnet $remote_server
expect "username:"
# Send the username, and then wait for a password prompt.
send "$my_user_id\r"
expect "password:"
# Send the password, and then wait for a shell prompt.
send "$my_password\r"
expect "%"
# Send the prebuilt command, and then wait for another shell prompt.
send "$my_command\r"
expect "%"
# Capture the results of the command into a variable. This can be displayed, or written to disk.
set results $expect_out(buffer)
# Exit the telnet session, and wait for a special end-of-file character.
send "exit\r"
expect eof

For more on expect utility: “Exploring Expect” by O’Reilly

pexpect

  • pexpect is pure Python implementation of expect.
  • Like expect pexpect is technically not a network aware program, but it can be used to automate interactive programs like Telnet and SSH.
# Script that connects to a telnet server and run a command.

import pexpect
child = pexpect.spawn('ftp ftp.openbsd.org')
child.expect('Name .*: ')
child.sendline('anonymous')
child.expect('Password:')
child.sendline('noah@example.com')
child.expect('ftp> ')
child.sendline('lcd /tmp')
child.expect('ftp> ')
child.sendline('cd pub/OpenBSD')
child.expect('ftp> ')
child.sendline('get README')
child.expect('ftp> ')
child.sendline('bye')

When not to use expect(pexpect)?

  • Expect(or pexpect) is an amazing tool, but can be complicated to use and very fragile.
  • The basic premise of expect is to set it up to look for certain strings of text (“expect” them), and when it sees certain text, respond in a certain way. Eeven a slight change in the expected string(mostly it’s case sensitive) will break the script.

telnetlib

  • telnetlib is similar to pexpect but crafted for automating Telnet protocol.
  • telnetlib not only has basic methods for sending and receiving data but also a few routines that will watch and wait for a particular string to arrive from the remote system.
import sys, getpass, telnetlib

def telnet_connect(hostname, username, password):
    t = telnetlib.Telnet(hostname)            # actively connects to a telnet server
    # t.set_debuglevel(1)                     # uncomment to get debug messages
    t.read_until(b'login:', 10)               # waits until it recieves a string 'login:'
    t.write(username.encode('utf-8'))         # sends username to the server
    t.write(b'\r')                            # sends return character to the server
    t.read_until(b'Password:', 10)            # waits until it recieves a string 'Password:'
    t.write(password.encode('utf-8'))         # sends password to the server
    t.write(b'\r')                            # sends return character to the server
    n, match, previous_text = t.expect([br'Login incorrect', br'\$'], 10)
    if n == 0:
        print('Username and password failed - giving up')
    else:
        t.write(b'exec ps aux\r')             # sends a command to the server
        t.write(b'exec exit\r')
        print(t.read_all().decode('utf-8'))  # read until socket closes
    t.close()

if __name__ == '__main__':
    if len(sys.argv) < 2: 
        print "Usage: python telnet_login.py hostname username"
    hostname = sys.argv[1]
    username = sys.argv[2]
    password = getpass.getpass('Password: ')
    telnet_connect(hostname, username, password)

telnetlib objects

Telnet.interact()

  • Interaction function, emulates a very dumb Telnet client.

Telnet.expect(list[, timeout])

  • Read until one from a list of a regular expressions matches.

Telnet.read_until(expected[, timeout])

  • Read until a given string, expected, is encountered or until timeout seconds have passed.

Telnet.read_all()

  • Read all data until EOF; block until connection closed.

Telnet.read_very_eager()

  • Read everything that can be without blocking in I/O (eager).

Telnet.read_eager()

  • Read readily available data.

Telnet.set_debuglevel(debuglevel)

  • Set the debug level. The higher the value of debuglevel, the more debug output you get (on sys.stdout).

Telnet.close()

  • Close the connection.