Repository: cowrie/cowrie

Issue:
HoneyPotTelnetAuthProtocol instance has no attribute 'telnet_Command'
Environment: Python 2.7.12, twistd 16.0.0

Log:

> 2016-12-21 13:30:48-0800 [cowrie.telnet.transport.HoneyPotTelnetFactory] New connection: xx.xxx.x.xxx:xxxxx (xxx.xx.x.x:2223) [session: TT130]
2016-12-21 13:30:48-0800 [CowrieTelnetTransport,130,xx.xxx.x.xxx] login attempt [/888888] failed
2016-12-21 13:30:48-0800 [CowrieTelnetTransport,130,xx.xxx.x.xxx] Warning: state changed and new state returned
2016-12-21 13:30:48-0800 [CowrieTelnetTransport,130,xx.xxx.x.xxx] login attempt [888888/shell] succeeded
2016-12-21 13:30:48-0800 [CowrieTelnetTransport,130,xx.xxx.x.xxx] Opening TTY Log: /opt/share/events/tty/20161221-133048-None-130i.log
2016-12-21 13:30:48-0800 [CowrieTelnetTransport,130,xx.xxx.x.xxx] Warning: state changed and new state returned
2016-12-21 13:30:48-0800 [CowrieTelnetTransport,130,xx.xxx.x.xxx] Unhandled Error
        Traceback (most recent call last):
          File "/usr/lib/python2.7/dist-packages/twisted/python/log.py", line 101, in callWithLogger
            return callWithContext({"system": lp}, func, *args, **kw)
          File "/usr/lib/python2.7/dist-packages/twisted/python/log.py", line 84, in callWithContext
            return context.call({ILogContext: newCtx}, func, *args, **kw)
          File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 118, in callWithContext
            return self.currentContext().callWithContext(ctx, func, *args, **kw)
          File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 81, in callWithContext
            return func(*args,**kw)
        --- <exception caught here> ---
          File "/usr/lib/python2.7/dist-packages/twisted/internet/posixbase.py", line 597, in _doReadOrWrite
            why = selectable.doRead()
          File "/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 209, in doRead
            return self._dataReceived(data)
          File "/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 215, in _dataReceived
            rval = self.protocol.dataReceived(data)
          File "/usr/lib/python2.7/dist-packages/twisted/conch/telnet.py", line 589, in dataReceived
            self.applicationDataReceived(''.join(appDataBuffer))
          File "/usr/lib/python2.7/dist-packages/twisted/conch/telnet.py", line 898, in applicationDataReceived
            self.protocol.dataReceived(bytes)
          File "/usr/lib/python2.7/dist-packages/twisted/protocols/basic.py", line 571, in dataReceived
            why = self.lineReceived(line)
          File "/usr/lib/python2.7/dist-packages/twisted/conch/telnet.py", line 1000, in lineReceived
            newState = getattr(self, "telnet_" + oldState)(line)
        exceptions.AttributeError: HoneyPotTelnetAuthProtocol instance has no attribute 'telnet_Command'
2016-12-21 13:30:48-0800 [CowrieTelnetTransport,130,xx.xxx.x.xxx] Closing TTY Log: /opt/share/events/tty/20161221-133048-None-130i.log after 0 seconds
2016-12-21 13:30:48-0800 [CowrieTelnetTransport,130,xx.xxx.x.xxx] honeypot terminal protocol connection lost [Failure instance: Traceback (failure with no frames): <type 'excepti
ons.AttributeError'>: HoneyPotTelnetAuthProtocol instance has no attribute 'telnet_Command'
        ]
2016-12-21 13:30:48-0800 [CowrieTelnetTransport,130,xx.xxx.x.xxx] Connection lost after 0 seconds


Discussion:
Can you tell me how to reproduce this?
I'm not sure how to reproduce it yet. The above log was copied from a cowrie instance which was exposed to Internet. The instance got over 7k errors in this type during last 10 days. 
I was able to reproduce this.  It appears to be due to having more than just the username and password in the data buffer when authenticating.  This can happen if a client tries to send the username, password, and commands all at the same time without waiting for the login to complete.  The following command will cause the exception:
```(printf 'root\npass\npwd\n' && cat) | telnet 127.0.0.1 2223```

Changing `self.state = 'Command'` to `self.state = 'Discard'` in `_cbLogin()` will cause the exception to go away, but any commands in the buffer will be lost.  

This appears to be a long-standing issue in Twisted's AuthenticatingTelnetProtocol.  The old, depreciated twisted.protocols.telnet module used telnet_Command for running commands, and the new implementation in twisted.conch still sets self.state to "Command."  This appears to have been intended to be used to handle command input, but that is not documented anywhere.

To fix this correctly, we would need to implement telnet_Command in HoneyPotTelnetAuthProtocol to forward the input to the user's session.  The following function appears to work correctly for me.
```
    def telnet_Command(self, command):
        self.transport.protocol.dataReceived(command+'\r')
        return "Command"
```

Resolving patch:
diff --git a/cowrie/telnet/transport.py b/cowrie/telnet/transport.py
--- a/cowrie/telnet/transport.py
+++ b/cowrie/telnet/transport.py
@@ -138,6 +138,10 @@ def login(ignored):
 
         return 'Discard'
 
+    def telnet_Command(self, command):
+        self.transport.protocol.dataReceived(command+'\r')
+        return "Command"
+
     def _cbLogin(self, ial):
         """
         Fired on a successful login


Test patch: