diff --git a/cowrie/core/ssh.py b/cowrie/core/ssh.py index fe23ce2..ac755e6 100644 --- a/cowrie/core/ssh.py +++ b/cowrie/core/ssh.py @@ -21,12 +21,10 @@ from twisted.conch.ssh.common import NS, getNS import ConfigParser import fs -import sshserver import auth import connection import honeypot import protocol -import sshserver import exceptions class HoneyPotSSHUserAuthServer(userauth.SSHUserAuthServer): @@ -211,11 +209,15 @@ class HoneyPotRealm: else: raise Exception("No supported interfaces found.") -class HoneyPotTransport(sshserver.CowrieSSHServerTransport): +class HoneyPotTransport(transport.SSHServerTransport): """ """ def connectionMade(self): + """ + Called when the connection is made from the other side. + We send our version, but wait with sending KEXINIT + """ self.transportId = uuid.uuid4().hex[:8] self.interactors = [] @@ -225,16 +227,49 @@ class HoneyPotTransport(sshserver.CowrieSSHServerTransport): dst_ip=self.transport.getHost().host, dst_port=self.transport.getHost().port, sessionno=self.transport.sessionno) - sshserver.CowrieSSHServerTransport.connectionMade(self) + self.transport.write('%s\r\n' % (self.ourVersionString,)) + self.currentEncryptions = transport.SSHCiphers('none', 'none', 'none', 'none') + self.currentEncryptions.setKeys('', '', '', '', '', '') def sendKexInit(self): # Don't send key exchange prematurely if not self.gotVersion: return - sshserver.CowrieSSHServerTransport.sendKexInit(self) + transport.SSHServerTransport.sendKexInit(self) def dataReceived(self, data): - sshserver.CowrieSSHServerTransport.dataReceived(self, data) + """ + First, check for the version string (SSH-2.0-*). After that has been + received, this method adds data to the buffer, and pulls out any + packets. + + @type data: C{str} + """ + self.buf = self.buf + data + if not self.gotVersion: + if not '\n' in self.buf: + return + self.otherVersionString = self.buf.strip() + if self.buf.startswith('SSH-'): + self.gotVersion = True + remoteVersion = self.buf.split('-')[1] + if remoteVersion not in self.supportedVersions: + self._unsupportedVersionReceived(remoteVersion) + return + i = self.buf.index('\n') + self.buf = self.buf[i+1:] + self.sendKexInit() + else: + self.transport.write('Protocol mismatch.\n') + log.msg('Bad protocol version identification: %s' % (self.otherVersionString)) + self.transport.loseConnection() + return + packet = self.getPacket() + while packet: + messageNum = ord(packet[0]) + self.dispatchMessage(messageNum, packet[1:]) + packet = self.getPacket() + # later versions seem to call sendKexInit again on their own if twisted.version.major < 11 and \ not self._hadVersion and self.gotVersion: @@ -256,7 +291,7 @@ class HoneyPotTransport(sshserver.CowrieSSHServerTransport): kexAlgs=kexAlgs, keyAlgs=keyAlgs, encCS=encCS, macCS=macCS, compCS=compCS, format='Remote SSH version: %(version)s') - return sshserver.CowrieSSHServerTransport.ssh_KEXINIT(self, packet) + return transport.SSHServerTransport.ssh_KEXINIT(self, packet) # this seems to be the only reliable place of catching lost connection def connectionLost(self, reason): @@ -264,9 +299,28 @@ class HoneyPotTransport(sshserver.CowrieSSHServerTransport): i.sessionClosed() if self.transport.sessionno in self.factory.sessions: del self.factory.sessions[self.transport.sessionno] - sshserver.CowrieSSHServerTransport.connectionLost(self, reason) + transport.SSHServerTransport.connectionLost(self, reason) log.msg(eventid='KIPP0011', format='Connection lost') + def sendDisconnect(self, reason, desc): + """ + http://kbyte.snowpenguin.org/portal/2013/04/30/kippo-protocol-mismatch-workaround/ + Workaround for the "bad packet length" error message. + + @param reason: the reason for the disconnect. Should be one of the + DISCONNECT_* values. + @type reason: C{int} + @param desc: a descrption of the reason for the disconnection. + @type desc: C{str} + """ + if not 'bad packet length' in desc: + transport.SSHServerTransport.sendDisconnect(self, reason, desc) + else: + self.transport.write('Packet corrupt\n') + log.msg('[SERVER] - Disconnecting with error, code %s\nreason: %s' % (reason, desc)) + self.transport.loseConnection() + + class HoneyPotSSHSession(session.SSHSession): def __init__(self, *args, **kw): diff --git a/cowrie/core/sshserver.py b/cowrie/core/sshserver.py deleted file mode 100644 index acca841..0000000 --- a/cowrie/core/sshserver.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) 2013 Thomas Nicholson -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The names of the author(s) may not be used to endorse or promote -# products derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. - -from twisted.conch.ssh import transport -from twisted.python import log - -class CowrieSSHServerTransport(transport.SSHServerTransport): - def connectionMade(self): - """ - Called when the connection is made to the other side. We sent our - version and the MSG_KEXINIT packet. - """ - self.transport.write('%s\r\n' % (self.ourVersionString,)) - self.currentEncryptions = transport.SSHCiphers('none', 'none', 'none', 'none') - self.currentEncryptions.setKeys('', '', '', '', '', '') - - def dataReceived(self, data): - """ - First, check for the version string (SSH-2.0-*). After that has been - received, this method adds data to the buffer, and pulls out any - packets. - - @type data: C{str} - """ - self.buf = self.buf + data - if not self.gotVersion: - if not '\n' in self.buf: - return - self.otherVersionString = self.buf.strip() - if self.buf.startswith('SSH-'): - self.gotVersion = True - remoteVersion = self.buf.split('-')[1] - if remoteVersion not in self.supportedVersions: - self._unsupportedVersionReceived(remoteVersion) - return - i = self.buf.index('\n') - self.buf = self.buf[i+1:] - self.sendKexInit() - else: - self.transport.write('Protocol mismatch.\n') - log.msg('Bad protocol version identification: %s' % (self.otherVersionString)) - self.transport.loseConnection() - return - packet = self.getPacket() - while packet: - messageNum = ord(packet[0]) - self.dispatchMessage(messageNum, packet[1:]) - packet = self.getPacket() - - def sendDisconnect(self, reason, desc): - """ - http://kbyte.snowpenguin.org/portal/2013/04/30/kippo-protocol-mismatch-workaround/ - Workaround for the "bad packet length" error message. - - @param reason: the reason for the disconnect. Should be one of the - DISCONNECT_* values. - @type reason: C{int} - @param desc: a descrption of the reason for the disconnection. - @type desc: C{str} - """ - if not 'bad packet length' in desc: - # With python >= 3 we can use super? - transport.SSHServerTransport.sendDisconnect(self, reason, desc) - else: - self.transport.write('Protocol mismatch.\n') - log.msg('[SERVER] - Disconnecting with error, code %s\nreason: %s' % (reason, desc)) - self.transport.loseConnection() -