From dfc6174122ae650f0d24c1f7b09168d8a7f4b392 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 3 Feb 2015 10:50:21 +0000 Subject: [PATCH 01/30] dblog : work in progress : formatted log messages --- kippo/core/dblog.py | 122 +++++++++++++++++++++++++------------------- kippo/core/ssh.py | 10 ++++ 2 files changed, 80 insertions(+), 52 deletions(-) diff --git a/kippo/core/dblog.py b/kippo/core/dblog.py index 9580374f..968260ec 100644 --- a/kippo/core/dblog.py +++ b/kippo/core/dblog.py @@ -3,55 +3,57 @@ import re import time +import abc + +# KIPP0001 : create session +# KIPP0002 : succesful login +# KIPP0003 : failed login +# KIPP0004 : TTY log opened +# KIPP0005 : handle command +# KIPP0006 : handle unknown command +# KIPP0007 : file download +# KIPP0008 : INPUT +# KIPP0009 : SSH Version +# KIPP0010 : Terminal Size +# KIPP0011 : Connection Lost class DBLogger(object): + def __init__(self, cfg): self.cfg = cfg self.sessions = {} self.ttylogs = {} - self.re_connected = re.compile( - '^New connection: ([0-9.]+):([0-9]+) \(([0-9.]+):([0-9]+)\) ' + \ - '\[session: ([0-9]+)\]$') self.re_sessionlog = re.compile( '.*HoneyPotTransport,([0-9]+),[0-9.]+$') - # :dispatch: means the message has been delivered directly via - # logDispatch, instead of relying on the twisted logging, which breaks - # on scope changes. - self.re_map = [(re.compile(x[0]), x[1]) for x in ( - ('^connection lost$', - self._connectionLost), - ('^login attempt \[(?P.*)/(?P.*)\] failed', - self.handleLoginFailed), - ('^login attempt \[(?P.*)/(?P.*)\] succeeded', - self.handleLoginSucceeded), - ('^Opening TTY log: (?P.*)$', - self.handleTTYLogOpened), - ('^:dispatch: Command found: (?P.*)$', - self.handleCommand), - ('^:dispatch: Command not found: (?P.*)$', - self.handleUnknownCommand), - ('^:dispatch: Saving URL \((?P.*)\) to (?P.*)$', - self.handleFileDownload), - ('^INPUT \((?P[a-zA-Z0-9]+)\): (?P.*)$', - self.handleInput), - ('^Terminal size: (?P[0-9]+) (?P[0-9]+)$', - self.handleTerminalSize), - ('^Remote SSH version: (?P.*)$', - self.handleClientVersion), - )] + # KIPP0001 is special since it kicks off new logging event, + # and is not handled here + self.events = { + 'KIPP0002': self.handleLoginSucceeded, + 'KIPP0003': self.handleLoginFailed, + 'KIPP0004': self.handleTTYLogOpened, + 'KIPP0005': self.handleCommand, + 'KIPP0006': self.handleUnknownCommand, + 'KIPP0007': self.handleFileDownload, + 'KIPP0008': self.handleInput, + 'KIPP0009': self.handleClientVersion, + 'KIPP0010': self.handleTerminalSize, + 'KIPP0011': self._connectionLost, + } + self.start(cfg) + # use logDispatch when the HoneypotTransport prefix is not available. + # here you can explicitly set the sessionIds to tie the sessions together def logDispatch(self, sessionid, msg): - if sessionid not in self.sessions.keys(): - return - for regex, func in self.re_map: - match = regex.match(msg) - if match: - func(self.sessions[sessionid], match.groupdict()) - break + if isinstance( msg, dict ): + msg['sessionid'] = sessionid + return self.emit( msg ) + elif isinstance( msg, str ): + return self.emit( { 'message':msg, 'sessionid':sessionid } ) def start(): + """Hook that can be used to set up connections in dbloggers""" pass def getSensor(self): @@ -64,28 +66,43 @@ class DBLogger(object): return int(time.mktime(time.gmtime()[:-1] + (-1,))) def emit(self, ev): - if not len(ev['message']): + # ignore stdout and stderr + if 'printed' in ev: return - match = self.re_connected.match(ev['message'][0]) - if match: - sessionid = int(match.groups()[4]) + + # ignore anything without eventid + if not 'eventid' in ev: + return + + # DEBUG: REMOVE ME + # print "emitting: %s" % repr( ev ) + + # connection event is special. adds to list + if ev['eventid'] == 'KIPP0001': + sessionid = ev['sessionno'] self.sessions[sessionid] = \ self.createSession( - match.groups()[0], int(match.groups()[1]), - match.groups()[2], int(match.groups()[3])) + ev['src_ip'], ev['src_port'], ev['dst_ip'], ev['dst_port'] ) return - match = self.re_sessionlog.match(ev['system']) - if not match: - return - sessionid = int(match.groups()[0]) + + # extract session id from the twisted log prefix + if 'system' in ev: + match = self.re_sessionlog.match(ev['system']) + if not match: + return + sessionid = int(match.groups()[0]) + elif 'sessionid' in ev: + sessionid = ev['sessionid'] + if sessionid not in self.sessions.keys(): return - message = ev['message'][0] - for regex, func in self.re_map: - match = regex.match(message) - if match: - func(self.sessions[sessionid], match.groupdict()) - break + + if 'eventid' in ev: + if ev['eventid'] in self.events: + self.events[ev['eventid']]( self.sessions[sessionid], ev ) + return + + print "error, can't dblog %s" % repr(ev) def _connectionLost(self, session, args): self.handleConnectionLost(session, args) @@ -102,7 +119,8 @@ class DBLogger(object): f.close() return ttylog - # We have to return an unique ID + # We have to return a unique ID + @abc.abstractmethod def createSession(self, peerIP, peerPort, hostIP, hostPort): return 0 diff --git a/kippo/core/ssh.py b/kippo/core/ssh.py index 60591994..0aefa408 100644 --- a/kippo/core/ssh.py +++ b/kippo/core/ssh.py @@ -177,6 +177,12 @@ class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport): self.transport.getHost().host, self.transport.getHost().port, self.transport.sessionno) ) + log.msg( eventid='KIPP0001', + format='New connection: %(src_ip)s:%(src_port)s (%(dst_ip)s:%(dst_port)s) [session: %(sessionno)s]', + src_ip=self.transport.getPeer().host, src_port=self.transport.getPeer().port, + dst_ip=self.transport.getHost().host, dst_port=self.transport.getHost().port, + sessionno=self.transport.sessionno ) + kippo.core.sshserver.KippoSSHServerTransport.connectionMade(self) def sendKexInit(self): @@ -204,6 +210,7 @@ class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport): log.msg('KEXINIT: client supported compression: %s' % compCS ) log.msg('KEXINIT: client supported lang: %s' % langCS ) log.msg( 'Remote SSH version: %s' % self.otherVersionString,) + log.msg( eventid='KIPP0009', version=self.otherVersionString, format='Remote SSH version: %(version)s' ) return kippo.core.sshserver.KippoSSHServerTransport.ssh_KEXINIT(self, packet) def lastlogExit(self): @@ -287,6 +294,9 @@ class HoneyPotAvatar(avatar.ConchUser): def getPty(self, terminal, windowSize, attrs): log.msg( 'Terminal size: %s %s' % windowSize[0:2] ) + log.msg( eventid='KIPP0010', width=windowSize[0], height=windowSize[1], + format='Terminal Size: %(width)s %(height)s' ) + self.windowSize = windowSize return None From 27321853ecd1df48735f7265f83a66ab9006f043 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 3 Feb 2015 11:04:56 +0000 Subject: [PATCH 02/30] all messages migrated to new formated messages --- kippo/commands/base.py | 6 ++++++ kippo/commands/wget.py | 3 +++ kippo/core/auth.py | 6 ++++++ kippo/core/honeypot.py | 3 +++ kippo/core/protocol.py | 5 ++++- 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/kippo/commands/base.py b/kippo/commands/base.py index 2a2b96b9..775ff5c2 100644 --- a/kippo/commands/base.py +++ b/kippo/commands/base.py @@ -231,6 +231,8 @@ class command_passwd(HoneyPotCommand): def lineReceived(self, line): log.msg( 'INPUT (passwd):', line ) + log.msg( eventid='KIPP0008', realm='passwd', input=line, + format='INPUT (%(realm)s): %(input)s' ) self.password = line.strip() self.callbacks.pop(0)(line) commands['/usr/bin/passwd'] = command_passwd @@ -429,6 +431,8 @@ class command_perl(HoneyPotCommand): def lineReceived(self, line): log.msg( 'INPUT (perl):', line ) + log.msg( eventid='KIPP0008', realm='perl', input=line, + format='INPUT (%(realm)s): %(input)s' ) commands['/usr/bin/perl'] = command_perl @@ -493,6 +497,8 @@ class command_php(HoneyPotCommand): def lineReceived(self, line): log.msg( 'INPUT (php):', line ) + log.msg( eventid='KIPP0008', realm='php', input=line, + format='INPUT (%(realm)s): %(input)s' ) commands['/usr/bin/php'] = command_php diff --git a/kippo/commands/wget.py b/kippo/commands/wget.py index c5617566..9d6b7de4 100644 --- a/kippo/commands/wget.py +++ b/kippo/commands/wget.py @@ -199,6 +199,9 @@ class HTTPProgressDownloader(client.HTTPDownloader): self.nomore = True else: msg = 'Saving URL (%s) to %s' % (self.wget.url, self.fileName) + msg = { 'eventid':'KIPP0007', 'url':self.wget.url, 'outfile':self.fileName, + 'format':'Saving URL (%(url)s) to %(outfile)s' } + # we're in a completely different class here. need to use logDispatch self.wget.honeypot.logDispatch(msg) log.msg( msg ) self.wget.writeln('Saving to: `%s' % self.fakeoutfile) diff --git a/kippo/core/auth.py b/kippo/core/auth.py index dfb77f4b..f1411f00 100644 --- a/kippo/core/auth.py +++ b/kippo/core/auth.py @@ -156,9 +156,15 @@ class HoneypotPasswordChecker: def checkUserPass(self, username, password): if UserDB().checklogin(username, password): log.msg( 'login attempt [%s/%s] succeeded' % (username, password) ) + log.msg( eventid='KIPP0002', + format='login attempt [%(username)s/%(password)s] succeeded', + username=theusername, password=thepassword ) return True else: log.msg( 'login attempt [%s/%s] failed' % (username, password) ) + log.msg( eventid='KIPP0003', + format='login attempt [%(username)s/%(password)s] failed', + username=theusername, password=thepassword ) return False # vim: set sw=4 et: diff --git a/kippo/core/honeypot.py b/kippo/core/honeypot.py index 3fc8a65c..1e5855ab 100644 --- a/kippo/core/honeypot.py +++ b/kippo/core/honeypot.py @@ -125,10 +125,13 @@ class HoneyPotShell(object): cmdclass = self.honeypot.getCommand(cmd, envvars['PATH'].split(':')) if cmdclass: log.msg( 'Command found: %s' % (line,) ) + log.msg( eventid='KIPP0005', input=line, format='Command found: %(input)s' ) self.honeypot.logDispatch('Command found: %s' % (line,)) self.honeypot.call_command(cmdclass, *rargs) else: self.honeypot.logDispatch('Command not found: %s' % (line,)) + log.msg( eventid='KIPP0006', + input=line, format='Command not found: %(input)s' ) log.msg( 'Command not found: %s' % (line,) ) if len(line): self.honeypot.writeln('bash: %s: command not found' % cmd) diff --git a/kippo/core/protocol.py b/kippo/core/protocol.py index 0f552f3e..aad06921 100644 --- a/kippo/core/protocol.py +++ b/kippo/core/protocol.py @@ -74,7 +74,7 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol): # this doesn't seem to be called upon disconnect, so please use # HoneyPotTransport.connectionLost instead def connectionLost(self, reason): - pass + log.msg( eventid='KIPP0011', format='Connection lost') # not sure why i need to do this: # scratch that, these don't seem to be necessary anymore: #del self.fs @@ -243,6 +243,9 @@ class LoggingServerProtocol(insults.ServerProtocol): (config().get('honeypot', 'log_path'), time.strftime('%Y%m%d-%H%M%S'), transport.transportId ) log.msg( 'Opening TTY log: %s' % transport.ttylog_file ) + log.msg( eventid='KIPP0004', logfile=transport.ttylog_file, + format='Opening TTY Log: %(logfile)s') + ttylog.ttylog_open(transport.ttylog_file, time.time()) transport.ttylog_open = True From 156579e38221f5e34b560d9d255731228515fe29 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 3 Feb 2015 11:27:32 +0000 Subject: [PATCH 03/30] small fixes to get it working --- kippo/core/auth.py | 8 ++++---- kippo/core/dblog.py | 2 +- kippo/core/protocol.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/kippo/core/auth.py b/kippo/core/auth.py index f1411f00..34e7783e 100644 --- a/kippo/core/auth.py +++ b/kippo/core/auth.py @@ -153,15 +153,15 @@ class HoneypotPasswordChecker: return defer.succeed(username) return defer.fail(UnauthorizedLogin()) - def checkUserPass(self, username, password): - if UserDB().checklogin(username, password): - log.msg( 'login attempt [%s/%s] succeeded' % (username, password) ) + def checkUserPass(self, theusername, thepassword): + if UserDB().checklogin(theusername, thepassword): + log.msg( 'login attempt [%s/%s] succeeded' % (theusername, thepassword) ) log.msg( eventid='KIPP0002', format='login attempt [%(username)s/%(password)s] succeeded', username=theusername, password=thepassword ) return True else: - log.msg( 'login attempt [%s/%s] failed' % (username, password) ) + log.msg( 'login attempt [%s/%s] failed' % (theusername, thepassword) ) log.msg( eventid='KIPP0003', format='login attempt [%(username)s/%(password)s] failed', username=theusername, password=thepassword ) diff --git a/kippo/core/dblog.py b/kippo/core/dblog.py index 968260ec..f93aade4 100644 --- a/kippo/core/dblog.py +++ b/kippo/core/dblog.py @@ -75,7 +75,7 @@ class DBLogger(object): return # DEBUG: REMOVE ME - # print "emitting: %s" % repr( ev ) + print "emitting: %s" % repr( ev ) # connection event is special. adds to list if ev['eventid'] == 'KIPP0001': diff --git a/kippo/core/protocol.py b/kippo/core/protocol.py index aad06921..c15311b7 100644 --- a/kippo/core/protocol.py +++ b/kippo/core/protocol.py @@ -35,7 +35,7 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol): def logDispatch(self, msg): transport = self.terminal.transport.session.conn.transport - msg = ':dispatch: ' + msg + #msg = ':dispatch: ' + msg transport.factory.logDispatch(transport.transport.sessionno, msg) def connectionMade(self): From e12b76b2066f16102ada567271793042689daace Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 3 Feb 2015 16:18:26 +0000 Subject: [PATCH 04/30] formatted log messages. not completely working yet withdispach --- kippo/commands/base.py | 6 +++--- kippo/commands/wget.py | 8 +++----- kippo/core/auth.py | 4 ++-- kippo/core/dblog.py | 9 ++++----- kippo/core/honeypot.py | 8 ++++---- kippo/core/protocol.py | 8 ++++---- kippo/core/ssh.py | 13 ++++++------- 7 files changed, 26 insertions(+), 30 deletions(-) diff --git a/kippo/commands/base.py b/kippo/commands/base.py index 775ff5c2..ea312780 100644 --- a/kippo/commands/base.py +++ b/kippo/commands/base.py @@ -230,7 +230,7 @@ class command_passwd(HoneyPotCommand): self.exit() def lineReceived(self, line): - log.msg( 'INPUT (passwd):', line ) + #log.msg( 'INPUT (passwd):', line ) log.msg( eventid='KIPP0008', realm='passwd', input=line, format='INPUT (%(realm)s): %(input)s' ) self.password = line.strip() @@ -430,7 +430,7 @@ class command_perl(HoneyPotCommand): self.exit() def lineReceived(self, line): - log.msg( 'INPUT (perl):', line ) + #log.msg( 'INPUT (perl):', line ) log.msg( eventid='KIPP0008', realm='perl', input=line, format='INPUT (%(realm)s): %(input)s' ) @@ -496,7 +496,7 @@ class command_php(HoneyPotCommand): self.exit() def lineReceived(self, line): - log.msg( 'INPUT (php):', line ) + #log.msg( 'INPUT (php):', line ) log.msg( eventid='KIPP0008', realm='php', input=line, format='INPUT (%(realm)s): %(input)s' ) diff --git a/kippo/commands/wget.py b/kippo/commands/wget.py index 9d6b7de4..cc89d99d 100644 --- a/kippo/commands/wget.py +++ b/kippo/commands/wget.py @@ -198,12 +198,10 @@ class HTTPProgressDownloader(client.HTTPDownloader): self.fileName = os.path.devnull self.nomore = True else: - msg = 'Saving URL (%s) to %s' % (self.wget.url, self.fileName) - msg = { 'eventid':'KIPP0007', 'url':self.wget.url, 'outfile':self.fileName, - 'format':'Saving URL (%(url)s) to %(outfile)s' } + log.msg( 'Saving URL (%s) to %s' % (self.wget.url, self.fileName) ) # we're in a completely different class here. need to use logDispatch - self.wget.honeypot.logDispatch(msg) - log.msg( msg ) + self.wget.honeypot.logDispatch( format='Saving URL (%(url)s) to %(outfile)s', + eventid='KIPP0007', url=self.wget.url, outfile=self.fileName ) self.wget.writeln('Saving to: `%s' % self.fakeoutfile) self.wget.honeypot.terminal.nextLine() diff --git a/kippo/core/auth.py b/kippo/core/auth.py index 34e7783e..5ad91867 100644 --- a/kippo/core/auth.py +++ b/kippo/core/auth.py @@ -155,13 +155,13 @@ class HoneypotPasswordChecker: def checkUserPass(self, theusername, thepassword): if UserDB().checklogin(theusername, thepassword): - log.msg( 'login attempt [%s/%s] succeeded' % (theusername, thepassword) ) + #log.msg( 'login attempt [%s/%s] succeeded' % (theusername, thepassword) ) log.msg( eventid='KIPP0002', format='login attempt [%(username)s/%(password)s] succeeded', username=theusername, password=thepassword ) return True else: - log.msg( 'login attempt [%s/%s] failed' % (theusername, thepassword) ) + #log.msg( 'login attempt [%s/%s] failed' % (theusername, thepassword) ) log.msg( eventid='KIPP0003', format='login attempt [%(username)s/%(password)s] failed', username=theusername, password=thepassword ) diff --git a/kippo/core/dblog.py b/kippo/core/dblog.py index f93aade4..e1534f7f 100644 --- a/kippo/core/dblog.py +++ b/kippo/core/dblog.py @@ -18,6 +18,7 @@ import abc # KIPP0011 : Connection Lost class DBLogger(object): + __metaclass__ = abc.ABCMeta def __init__(self, cfg): self.cfg = cfg @@ -45,9 +46,10 @@ class DBLogger(object): # use logDispatch when the HoneypotTransport prefix is not available. # here you can explicitly set the sessionIds to tie the sessions together - def logDispatch(self, sessionid, msg): + def logDispatch(self, *msg, **args): + print( "dblog disp %s" % repr(msg) ) if isinstance( msg, dict ): - msg['sessionid'] = sessionid + msg['sessionid'] = self.sessions[sessionid] return self.emit( msg ) elif isinstance( msg, str ): return self.emit( { 'message':msg, 'sessionid':sessionid } ) @@ -74,9 +76,6 @@ class DBLogger(object): if not 'eventid' in ev: return - # DEBUG: REMOVE ME - print "emitting: %s" % repr( ev ) - # connection event is special. adds to list if ev['eventid'] == 'KIPP0001': sessionid = ev['sessionno'] diff --git a/kippo/core/honeypot.py b/kippo/core/honeypot.py index 1e5855ab..1b206d0b 100644 --- a/kippo/core/honeypot.py +++ b/kippo/core/honeypot.py @@ -124,15 +124,15 @@ class HoneyPotShell(object): rargs.append(arg) cmdclass = self.honeypot.getCommand(cmd, envvars['PATH'].split(':')) if cmdclass: - log.msg( 'Command found: %s' % (line,) ) + #log.msg( 'Command found: %s' % (line,) ) log.msg( eventid='KIPP0005', input=line, format='Command found: %(input)s' ) - self.honeypot.logDispatch('Command found: %s' % (line,)) + #self.honeypot.logDispatch('Command found: %s' % (line,)) self.honeypot.call_command(cmdclass, *rargs) else: - self.honeypot.logDispatch('Command not found: %s' % (line,)) + #log.msg( 'Command not found: %s' % (line,) ) log.msg( eventid='KIPP0006', input=line, format='Command not found: %(input)s' ) - log.msg( 'Command not found: %s' % (line,) ) + #self.honeypot.logDispatch('Command not found: %s' % (line,)) if len(line): self.honeypot.writeln('bash: %s: command not found' % cmd) runOrPrompt() diff --git a/kippo/core/protocol.py b/kippo/core/protocol.py index c15311b7..1cc0d678 100644 --- a/kippo/core/protocol.py +++ b/kippo/core/protocol.py @@ -33,10 +33,10 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol): self.password_input = False self.cmdstack = [] - def logDispatch(self, msg): + def logDispatch(self, *msg, **args): transport = self.terminal.transport.session.conn.transport - #msg = ':dispatch: ' + msg - transport.factory.logDispatch(transport.transport.sessionno, msg) + args['sessionid']=transport.transport.sessionno + transport.factory.logDispatch(msg, args) def connectionMade(self): self.displayMOTD() @@ -242,7 +242,7 @@ class LoggingServerProtocol(insults.ServerProtocol): transport.ttylog_file = '%s/tty/%s-%s.log' % \ (config().get('honeypot', 'log_path'), time.strftime('%Y%m%d-%H%M%S'), transport.transportId ) - log.msg( 'Opening TTY log: %s' % transport.ttylog_file ) + #log.msg( 'Opening TTY log: %s' % transport.ttylog_file ) log.msg( eventid='KIPP0004', logfile=transport.ttylog_file, format='Opening TTY Log: %(logfile)s') diff --git a/kippo/core/ssh.py b/kippo/core/ssh.py index 0aefa408..f8902952 100644 --- a/kippo/core/ssh.py +++ b/kippo/core/ssh.py @@ -172,11 +172,10 @@ class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport): self.logintime = time.time() self.transportId = uuid.uuid4().hex[:8] - log.msg( 'New connection: %s:%s (%s:%s) [session: %d]' % \ - (self.transport.getPeer().host, self.transport.getPeer().port, - self.transport.getHost().host, self.transport.getHost().port, - self.transport.sessionno) ) - + #log.msg( 'New connection: %s:%s (%s:%s) [session: %d]' % \ + # (self.transport.getPeer().host, self.transport.getPeer().port, + # self.transport.getHost().host, self.transport.getHost().port, + # self.transport.sessionno) ) log.msg( eventid='KIPP0001', format='New connection: %(src_ip)s:%(src_port)s (%(dst_ip)s:%(dst_port)s) [session: %(sessionno)s]', src_ip=self.transport.getPeer().host, src_port=self.transport.getPeer().port, @@ -209,7 +208,7 @@ class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport): log.msg('KEXINIT: client supported MAC: %s' % macCS ) log.msg('KEXINIT: client supported compression: %s' % compCS ) log.msg('KEXINIT: client supported lang: %s' % langCS ) - log.msg( 'Remote SSH version: %s' % self.otherVersionString,) + #log.msg( 'Remote SSH version: %s' % self.otherVersionString,) log.msg( eventid='KIPP0009', version=self.otherVersionString, format='Remote SSH version: %(version)s' ) return kippo.core.sshserver.KippoSSHServerTransport.ssh_KEXINIT(self, packet) @@ -293,7 +292,7 @@ class HoneyPotAvatar(avatar.ConchUser): protocol.makeConnection(session.wrapProtocol(serverProtocol)) def getPty(self, terminal, windowSize, attrs): - log.msg( 'Terminal size: %s %s' % windowSize[0:2] ) + #log.msg( 'Terminal size: %s %s' % windowSize[0:2] ) log.msg( eventid='KIPP0010', width=windowSize[0], height=windowSize[1], format='Terminal Size: %(width)s %(height)s' ) From edfea24970bd3cd4d0724e15d3474d2d856a20f4 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 10 Feb 2015 08:25:30 +0000 Subject: [PATCH 05/30] enable json logging by default, small typo fix. --- kippo.cfg.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kippo.cfg.dist b/kippo.cfg.dist index a73fcf6a..20db7c62 100644 --- a/kippo.cfg.dist +++ b/kippo.cfg.dist @@ -216,7 +216,7 @@ interact_port = 5123 #logfile = log/kippo-textlog.log # JSON based logging module -#[database_jsonlog] +[database_jsonlog] logfile = log/kippolog.json #[database_hpfeeds] From 25466f1df064fb03c34fb237f302d25e7f8c53c5 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 10 Feb 2015 09:36:43 +0000 Subject: [PATCH 06/30] consistent sessionno var name use. now works with wget --- kippo/core/dblog.py | 32 +++++++++++++++----------------- kippo/core/protocol.py | 4 ++-- kippo/core/ssh.py | 4 ++-- kippo/dblog/jsonlog.py | 2 -- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/kippo/core/dblog.py b/kippo/core/dblog.py index e1534f7f..79c094f1 100644 --- a/kippo/core/dblog.py +++ b/kippo/core/dblog.py @@ -45,14 +45,10 @@ class DBLogger(object): self.start(cfg) # use logDispatch when the HoneypotTransport prefix is not available. - # here you can explicitly set the sessionIds to tie the sessions together - def logDispatch(self, *msg, **args): - print( "dblog disp %s" % repr(msg) ) - if isinstance( msg, dict ): - msg['sessionid'] = self.sessions[sessionid] - return self.emit( msg ) - elif isinstance( msg, str ): - return self.emit( { 'message':msg, 'sessionid':sessionid } ) + def logDispatch(self, *msg, **kw): + ev = kw + ev['message'] = msg + self.emit(ev) def start(): """Hook that can be used to set up connections in dbloggers""" @@ -78,27 +74,29 @@ class DBLogger(object): # connection event is special. adds to list if ev['eventid'] == 'KIPP0001': - sessionid = ev['sessionno'] - self.sessions[sessionid] = \ + sessionno = ev['sessionno'] + self.sessions[sessionno] = \ self.createSession( ev['src_ip'], ev['src_port'], ev['dst_ip'], ev['dst_port'] ) return - # extract session id from the twisted log prefix - if 'system' in ev: + # use explicit sessionno if coming from dispatch + if 'sessionno' in ev: + sessionno = ev['sessionno'] + del ev['sessionno'] + # else extract session id from the twisted log prefix + elif 'system' in ev: match = self.re_sessionlog.match(ev['system']) if not match: return - sessionid = int(match.groups()[0]) - elif 'sessionid' in ev: - sessionid = ev['sessionid'] + sessionno = int(match.groups()[0]) - if sessionid not in self.sessions.keys(): + if sessionno not in self.sessions.keys(): return if 'eventid' in ev: if ev['eventid'] in self.events: - self.events[ev['eventid']]( self.sessions[sessionid], ev ) + self.events[ev['eventid']]( self.sessions[sessionno], ev ) return print "error, can't dblog %s" % repr(ev) diff --git a/kippo/core/protocol.py b/kippo/core/protocol.py index 1cc0d678..3163fa2d 100644 --- a/kippo/core/protocol.py +++ b/kippo/core/protocol.py @@ -35,8 +35,8 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol): def logDispatch(self, *msg, **args): transport = self.terminal.transport.session.conn.transport - args['sessionid']=transport.transport.sessionno - transport.factory.logDispatch(msg, args) + args['sessionno']=transport.transport.sessionno + transport.factory.logDispatch(*msg,**args) def connectionMade(self): self.displayMOTD() diff --git a/kippo/core/ssh.py b/kippo/core/ssh.py index f8902952..71d4ef08 100644 --- a/kippo/core/ssh.py +++ b/kippo/core/ssh.py @@ -63,9 +63,9 @@ class HoneyPotSSHFactory(factory.SSHFactory): } # Special delivery to the loggers to avoid scope problems - def logDispatch(self, sessionid, msg): + def logDispatch(self, *msg, **args): for dblog in self.dbloggers: - dblog.logDispatch(sessionid, msg) + dblog.logDispatch(*msg, **args) def __init__(self): cfg = config() diff --git a/kippo/dblog/jsonlog.py b/kippo/dblog/jsonlog.py index 2fe0b694..a199cc8f 100644 --- a/kippo/dblog/jsonlog.py +++ b/kippo/dblog/jsonlog.py @@ -8,8 +8,6 @@ import uuid import json from kippo.core import dblog -from twisted.enterprise import adbapi -from twisted.internet import defer from twisted.python import log class DBLogger(dblog.DBLogger): From b2faa13cf8d48183ee760d532667369825e4a1c2 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 10 Feb 2015 09:49:16 +0000 Subject: [PATCH 07/30] more comments --- kippo/core/dblog.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kippo/core/dblog.py b/kippo/core/dblog.py index 79c094f1..8e6fe561 100644 --- a/kippo/core/dblog.py +++ b/kippo/core/dblog.py @@ -5,6 +5,9 @@ import re import time import abc +# dblog now operates based on eventids, no longer on regex parsing of the entry. +# add an eventid using keyword args and it will be picked up by the dblogger + # KIPP0001 : create session # KIPP0002 : succesful login # KIPP0003 : failed login @@ -27,7 +30,7 @@ class DBLogger(object): self.re_sessionlog = re.compile( '.*HoneyPotTransport,([0-9]+),[0-9.]+$') - # KIPP0001 is special since it kicks off new logging event, + # KIPP0001 is special since it kicks off new logging session, # and is not handled here self.events = { 'KIPP0002': self.handleLoginSucceeded, @@ -44,7 +47,7 @@ class DBLogger(object): self.start(cfg) - # use logDispatch when the HoneypotTransport prefix is not available. + # used when the HoneypotTransport prefix is not available. def logDispatch(self, *msg, **kw): ev = kw ev['message'] = msg @@ -99,7 +102,7 @@ class DBLogger(object): self.events[ev['eventid']]( self.sessions[sessionno], ev ) return - print "error, can't dblog %s" % repr(ev) + print "error, unknown eventid %s" % repr(ev) def _connectionLost(self, session, args): self.handleConnectionLost(session, args) From 5252042fb061a6af81aa24f695f4d1969bd8a871 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 10 Feb 2015 10:50:12 +0000 Subject: [PATCH 08/30] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47b49fb8..0aff9277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,3 +33,4 @@ * add 'poweroff' 'halt' and 'reboot' aliases for shutdown * add environment passing to commands * added 'which', 'netstat' and 'gcc' from kippo-extra +* logging framework allows for keyword use From b88380fe9b1bcc387a38117c206ec976a703822d Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Wed, 11 Feb 2015 07:01:32 +0000 Subject: [PATCH 09/30] naming convention warning --- kippo/core/dblog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kippo/core/dblog.py b/kippo/core/dblog.py index 8e6fe561..48ba517f 100644 --- a/kippo/core/dblog.py +++ b/kippo/core/dblog.py @@ -7,6 +7,7 @@ import abc # dblog now operates based on eventids, no longer on regex parsing of the entry. # add an eventid using keyword args and it will be picked up by the dblogger +# the KIPPxxxx naming convention is still subject to change. # KIPP0001 : create session # KIPP0002 : succesful login From ed48f3c950548b6f247b1faf892d27282345d284 Mon Sep 17 00:00:00 2001 From: aabed Date: Tue, 20 Jan 2015 17:30:31 +0000 Subject: [PATCH 10/30] adds implemntation for logging commands,unknown command and urls --- kippo/dblog/hpfeeds.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/kippo/dblog/hpfeeds.py b/kippo/dblog/hpfeeds.py index af496b69..b8b4c530 100644 --- a/kippo/dblog/hpfeeds.py +++ b/kippo/dblog/hpfeeds.py @@ -205,9 +205,9 @@ class DBLogger(dblog.DBLogger): # We have to return an unique ID def createSession(self, peerIP, peerPort, hostIP, hostPort): session = uuid.uuid4().hex - self.meta[session] = {'peerIP': peerIP, 'peerPort': peerPort, - 'hostIP': hostIP, 'hostPort': hostPort, 'loggedin': None, - 'credentials':[], 'version': None, 'ttylog': None } + self.meta[session] = {'session':session,'peerIP': peerIP, 'peerPort': peerPort, + 'hostIP': hostIP, 'hostPort': hostPort, 'loggedin': None, + 'credentials':[], 'commands':[],"unknownCommands":[],'urls':[],'version': None, 'ttylog': None } return session def handleConnectionLost(self, session, args): @@ -226,19 +226,26 @@ class DBLogger(dblog.DBLogger): self.meta[session]['loggedin'] = (u,p) def handleCommand(self, session, args): - pass - + c = args['input'] + self.meta[session]['commands'].append(c) + def handleUnknownCommand(self, session, args): - pass + uc = args['input'] + self.meta[session]['unknownCommands'].append(uc) def handleInput(self, session, args): pass - + def handleTerminalSize(self, session, args): - pass + pass def handleClientVersion(self, session, args): v = args['version'] self.meta[session]['version'] = v + def handleFileDownload(self,session,args): + url = args['url'] + self.meta[session]['urls'].append(url) + + # vim: set sw=4 et: From 4514f9fe73aa86834df6cbcb318980eb1ce5f7e7 Mon Sep 17 00:00:00 2001 From: aabed Date: Sun, 25 Jan 2015 20:41:54 +0000 Subject: [PATCH 11/30] Fixing indentation --- kippo/dblog/hpfeeds.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kippo/dblog/hpfeeds.py b/kippo/dblog/hpfeeds.py index b8b4c530..c178583a 100644 --- a/kippo/dblog/hpfeeds.py +++ b/kippo/dblog/hpfeeds.py @@ -206,8 +206,8 @@ class DBLogger(dblog.DBLogger): def createSession(self, peerIP, peerPort, hostIP, hostPort): session = uuid.uuid4().hex self.meta[session] = {'session':session,'peerIP': peerIP, 'peerPort': peerPort, - 'hostIP': hostIP, 'hostPort': hostPort, 'loggedin': None, - 'credentials':[], 'commands':[],"unknownCommands":[],'urls':[],'version': None, 'ttylog': None } + 'hostIP': hostIP, 'hostPort': hostPort, 'loggedin': None, + 'credentials':[], 'commands':[],"unknownCommands":[],'urls':[],'version': None, 'ttylog': None } return session def handleConnectionLost(self, session, args): From a9cf37b3239c225ffef09fd449ecfd3fdf373d35 Mon Sep 17 00:00:00 2001 From: aabed Date: Mon, 26 Jan 2015 23:15:57 +0000 Subject: [PATCH 12/30] Adds startTime and endTime to the payload --- kippo/dblog/hpfeeds.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kippo/dblog/hpfeeds.py b/kippo/dblog/hpfeeds.py index c178583a..e34d6a80 100644 --- a/kippo/dblog/hpfeeds.py +++ b/kippo/dblog/hpfeeds.py @@ -2,6 +2,7 @@ from kippo.core import dblog from twisted.python import log +from datetime import datetime import os import struct @@ -205,7 +206,8 @@ class DBLogger(dblog.DBLogger): # We have to return an unique ID def createSession(self, peerIP, peerPort, hostIP, hostPort): session = uuid.uuid4().hex - self.meta[session] = {'session':session,'peerIP': peerIP, 'peerPort': peerPort, + startTime=datetime.now().isoformat() + self.meta[session] = {'session':session,'startTime':startTime,'endTime':'','peerIP': peerIP, 'peerPort': peerPort, 'hostIP': hostIP, 'hostPort': hostPort, 'loggedin': None, 'credentials':[], 'commands':[],"unknownCommands":[],'urls':[],'version': None, 'ttylog': None } return session @@ -213,6 +215,7 @@ class DBLogger(dblog.DBLogger): def handleConnectionLost(self, session, args): log.msg('publishing metadata to hpfeeds') meta = self.meta[session] + self.meta[session]['endTime']=datetime.now().isoformat() ttylog = self.ttylog(session) if ttylog: meta['ttylog'] = ttylog.encode('hex') self.client.publish(KIPPOCHAN, **meta) From 7b17bf06ce916f357786a3284935dd856e01474d Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 06:26:43 +0000 Subject: [PATCH 13/30] enable jsonlog by default --- kippo.cfg.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kippo.cfg.dist b/kippo.cfg.dist index a73fcf6a..20db7c62 100644 --- a/kippo.cfg.dist +++ b/kippo.cfg.dist @@ -216,7 +216,7 @@ interact_port = 5123 #logfile = log/kippo-textlog.log # JSON based logging module -#[database_jsonlog] +[database_jsonlog] logfile = log/kippolog.json #[database_hpfeeds] From 9e348e4c8dc5141f14c803060d7120373b69762e Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 06:51:11 +0000 Subject: [PATCH 14/30] rename ssh_addr -> listen_addr and ssh_port -> listen_port old values still accepted for backwards compatibility --- CHANGELOG.md | 3 +++ kippo.cfg.dist | 4 ++-- kippo.tac | 29 ++++++++++++++++++----------- start.sh | 2 -- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47b49fb8..05683b10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ + +* 2015-02-19 Configuration directives have changed! ssh_addr has become listen_addr and ssh_port has become listen_port. The old keywords are still accepted for backwards compatibility + * default behaviour is changed to disable the exit jail * sftp support * exec support diff --git a/kippo.cfg.dist b/kippo.cfg.dist index 20db7c62..24171d5f 100644 --- a/kippo.cfg.dist +++ b/kippo.cfg.dist @@ -16,12 +16,12 @@ # IP addresses to listen for incoming SSH connections. # # (default: 0.0.0.0) = any address -#ssh_addr = 0.0.0.0 +#listen_addr = 0.0.0.0 # Port to listen for incoming SSH connections. # # (default: 2222) -ssh_port = 2222 +#listen_port = 2222 # Hostname for the honeypot. Displayed by the shell prompt of the virtual # environment. diff --git a/kippo.tac b/kippo.tac index 1135836e..b92bbc92 100644 --- a/kippo.tac +++ b/kippo.tac @@ -3,13 +3,11 @@ import sys, os if sys.platform == 'win32': - import os, inspect # this is when just running on win32 sys.path.insert(0, os.path.abspath(os.getcwd())) # and this is when running as a service #os.chdir(os.path.dirname(inspect.getfile(inspect.currentframe()))) -from twisted.internet import reactor, defer from twisted.application import internet, service from twisted.cred import portal from twisted.conch.ssh import factory, keys @@ -23,7 +21,6 @@ if not os.path.exists('kippo.cfg'): sys.exit(1) from kippo.core.config import config -import kippo.core.auth import kippo.core.honeypot import kippo.core.ssh from kippo import core @@ -42,16 +39,27 @@ factory.privateKeys = {'ssh-rsa': keys.Key.fromString(data=rsa_privKeyString), 'ssh-dss': keys.Key.fromString(data=dsa_privKeyString)} cfg = config() -if cfg.has_option('honeypot', 'ssh_addr'): - ssh_addr = cfg.get('honeypot', 'ssh_addr') + +if cfg.has_option('honeypot', 'listen_addr'): + listen_addr = cfg.get('honeypot', 'listen_addr') +elif cfg.has_option('honeypot', 'ssh_addr'): + # ssh_addr for backwards compatibility + listen_addr = cfg.get('honeypot', 'ssh_addr') else: - ssh_addr = '0.0.0.0' + listen_addr = '0.0.0.0' + +if cfg.has_option('honeypot', 'listen_port'): + listen_port = int(cfg.get('honeypot', 'listen_port')) +elif cfg.has_option('honeypot', 'ssh_port'): + # ssh_port for backwards compatibility + listen_port = int(cfg.get('honeypot', 'ssh_port')) +else: + listen_port = 2222 application = service.Application('honeypot') -for i in ssh_addr.split(): - service = internet.TCPServer( - int(cfg.get('honeypot', 'ssh_port')), factory, - interface=i) +for i in listen_addr.split(): + service = internet.TCPServer( listen_port, + factory, interface=i) service.setServiceParent(application) if cfg.has_option('honeypot', 'interact_enabled') and \ @@ -59,7 +67,6 @@ if cfg.has_option('honeypot', 'interact_enabled') and \ ('yes', 'true', 'on'): iport = int(cfg.get('honeypot', 'interact_port')) from kippo.core import interact - from twisted.internet import protocol service = internet.TCPServer(iport, interact.makeInteractFactory(factory)) service.setServiceParent(application) diff --git a/start.sh b/start.sh index f9a4d9df..8a0289e1 100755 --- a/start.sh +++ b/start.sh @@ -24,7 +24,5 @@ then . $VENV/bin/activate fi -twistd --version - echo "Starting kippo in the background..." twistd -y kippo.tac -l log/kippo.log --pidfile kippo.pid From 88f5a3c377cd81500c25914031d58892fa704181 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 06:53:36 +0000 Subject: [PATCH 15/30] update abstract method for start --- kippo/core/dblog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kippo/core/dblog.py b/kippo/core/dblog.py index 57106e74..7908618c 100644 --- a/kippo/core/dblog.py +++ b/kippo/core/dblog.py @@ -55,7 +55,7 @@ class DBLogger(object): func(self.sessions[sessionid], match.groupdict()) break - def start(): + def start(self, cfg): pass def getSensor(self): From f590587d9d67b07bef968ba4ea38976b5c3a2630 Mon Sep 17 00:00:00 2001 From: MrPumo Date: Thu, 19 Feb 2015 10:08:36 +0100 Subject: [PATCH 16/30] change username-pwd delimiter in log from / to ]/[ allowing pwd with / char --- kippo/core/auth.py | 4 ++-- kippo/core/dblog.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kippo/core/auth.py b/kippo/core/auth.py index 3bb2a79a..19e587de 100644 --- a/kippo/core/auth.py +++ b/kippo/core/auth.py @@ -158,10 +158,10 @@ class HoneypotPasswordChecker: def checkUserPass(self, username, password): if UserDB().checklogin(username, password): - log.msg( 'login attempt [%s/%s] succeeded' % (username, password) ) + log.msg( 'login attempt [%s]/[%s] succeeded' % (username, password) ) return True else: - log.msg( 'login attempt [%s/%s] failed' % (username, password) ) + log.msg( 'login attempt [%s]/[%s] failed' % (username, password) ) return False # vim: set sw=4 et: diff --git a/kippo/core/dblog.py b/kippo/core/dblog.py index 57106e74..5caa02dc 100644 --- a/kippo/core/dblog.py +++ b/kippo/core/dblog.py @@ -21,9 +21,9 @@ class DBLogger(object): self.re_map = [(re.compile(x[0]), x[1]) for x in ( ('^connection lost$', self._connectionLost), - ('^login attempt \[(?P.*)/(?P.*)\] failed', + ('^login attempt \[(?P.*)\]/\[(?P.*)\] failed', self.handleLoginFailed), - ('^login attempt \[(?P.*)/(?P.*)\] succeeded', + ('^login attempt \[(?P.*)\]/\[(?P.*)\] succeeded', self.handleLoginSucceeded), ('^Opening TTY log: (?P.*)$', self.handleTTYLogOpened), From 63de6893c8a78750f872019ca120db763b83c8b3 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 11:46:50 +0000 Subject: [PATCH 17/30] space after comma, remove unused imports --- kippo/core/protocol.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/kippo/core/protocol.py b/kippo/core/protocol.py index 0f552f3e..e0b5329a 100644 --- a/kippo/core/protocol.py +++ b/kippo/core/protocol.py @@ -3,19 +3,15 @@ import os import time -import struct import socket import copy from twisted.conch import recvline -from twisted.conch.ssh import transport from twisted.conch.insults import insults -from twisted.internet import protocol from twisted.python import log -from kippo.core import ttylog, fs +from kippo.core import ttylog from kippo.core.config import config -import kippo.core.honeypot from kippo import core class HoneyPotBaseProtocol(insults.TerminalProtocol): @@ -61,7 +57,7 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol): else: # Hack to get ip s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.connect(("8.8.8.8",80)) + s.connect(("8.8.8.8", 80)) self.kippoIP = s.getsockname()[0] s.close() From 687870c45c40769674e565502143297a62c5ca9b Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 12:01:07 +0000 Subject: [PATCH 18/30] use relative style imports instead of absolute paths --- kippo/core/auth.py | 2 +- kippo/core/honeypot.py | 7 +++--- kippo/core/interact.py | 2 +- kippo/core/protocol.py | 12 ++++----- kippo/core/ssh.py | 56 ++++++++++++++++++++++-------------------- kippo/core/utils.py | 2 +- 6 files changed, 43 insertions(+), 38 deletions(-) diff --git a/kippo/core/auth.py b/kippo/core/auth.py index 19e587de..6203271a 100644 --- a/kippo/core/auth.py +++ b/kippo/core/auth.py @@ -16,7 +16,7 @@ from twisted.python import log, failure from twisted.conch import error from twisted.conch.ssh import keys -from kippo.core.config import config +from config import config # by Walter de Jong class UserDB(object): diff --git a/kippo/core/honeypot.py b/kippo/core/honeypot.py index 3fc8a65c..1db44cfc 100644 --- a/kippo/core/honeypot.py +++ b/kippo/core/honeypot.py @@ -9,9 +9,10 @@ import copy import pickle from twisted.python import log -from kippo.core import fs -from kippo.core.config import config -import kippo.core.exceptions + +import fs +import exceptions +from config import config class HoneyPotCommand(object): def __init__(self, protocol, *args): diff --git a/kippo/core/interact.py b/kippo/core/interact.py index 0dcf8601..0079b508 100644 --- a/kippo/core/interact.py +++ b/kippo/core/interact.py @@ -6,7 +6,7 @@ import time from twisted.internet import protocol from twisted.conch import telnet, recvline -from kippo.core import ttylog +import ttylog class Interact(telnet.Telnet): diff --git a/kippo/core/protocol.py b/kippo/core/protocol.py index e0b5329a..a4182576 100644 --- a/kippo/core/protocol.py +++ b/kippo/core/protocol.py @@ -10,9 +10,9 @@ from twisted.conch import recvline from twisted.conch.insults import insults from twisted.python import log -from kippo.core import ttylog -from kippo.core.config import config -from kippo import core +import honeypot +import ttylog +from config import config class HoneyPotBaseProtocol(insults.TerminalProtocol): def __init__(self, avatar, env): @@ -77,7 +77,7 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol): #del self.commands def txtcmd(self, txt): - class command_txtcmd(core.honeypot.HoneyPotCommand): + class command_txtcmd(honeypot.HoneyPotCommand): def call(self): log.msg( 'Reading txtcmd from "%s"' % txt ) f = file(txt, 'r') @@ -147,7 +147,7 @@ class HoneyPotExecProtocol(HoneyPotBaseProtocol): HoneyPotBaseProtocol.connectionMade(self) self.terminal.transport.session.conn.transport.stdinlog_open = True - self.cmdstack = [core.honeypot.HoneyPotShell(self, interactive=False)] + self.cmdstack = [honeypot.HoneyPotShell(self, interactive=False)] self.cmdstack[0].lineReceived(self.execcmd) class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLine): @@ -160,7 +160,7 @@ class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLin HoneyPotBaseProtocol.connectionMade(self) recvline.HistoricRecvLine.connectionMade(self) - self.cmdstack = [core.honeypot.HoneyPotShell(self)] + self.cmdstack = [honeypot.HoneyPotShell(self)] transport = self.terminal.transport.session.conn.transport transport.factory.sessions[transport.transport.sessionno] = self diff --git a/kippo/core/ssh.py b/kippo/core/ssh.py index adc15464..db540831 100644 --- a/kippo/core/ssh.py +++ b/kippo/core/ssh.py @@ -21,13 +21,17 @@ from twisted.conch.ssh.common import NS, getNS import ConfigParser -from kippo.core import ttylog, utils, fs, sshserver -from kippo.core.config import config -import kippo.core.auth -import kippo.core.honeypot -import kippo.core.ssh -import kippo.core.protocol -from kippo import core +import ttylog +import utils +import fs +import sshserver +import auth +import honeypot +import ssh +import protocol +import sshserver +import exceptions +from config import config class HoneyPotSSHUserAuthServer(userauth.SSHUserAuthServer): def serviceStarted(self): @@ -145,7 +149,7 @@ class HoneyPotRealm: def __init__(self): # I don't know if i'm supposed to keep static stuff here - self.env = core.honeypot.HoneyPotEnvironment() + self.env = honeypot.HoneyPotEnvironment() def requestAvatar(self, avatarId, mind, *interfaces): if conchinterfaces.IConchUser in interfaces: @@ -154,7 +158,7 @@ class HoneyPotRealm: else: raise Exception, "No supported interfaces found." -class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport): +class HoneyPotTransport(sshserver.KippoSSHServerTransport): """ @ivar logintime: time of login @@ -181,16 +185,16 @@ class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport): self.transport.getHost().host, self.transport.getHost().port, self.transport.sessionno) ) - kippo.core.sshserver.KippoSSHServerTransport.connectionMade(self) + sshserver.KippoSSHServerTransport.connectionMade(self) def sendKexInit(self): # Don't send key exchange prematurely if not self.gotVersion: return - kippo.core.sshserver.KippoSSHServerTransport.sendKexInit(self) + sshserver.KippoSSHServerTransport.sendKexInit(self) def dataReceived(self, data): - kippo.core.sshserver.KippoSSHServerTransport.dataReceived(self, data) + sshserver.KippoSSHServerTransport.dataReceived(self, data) # later versions seem to call sendKexInit again on their own if twisted.version.major < 11 and \ not self._hadVersion and self.gotVersion: @@ -208,7 +212,7 @@ class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport): log.msg('KEXINIT: client supported compression: %s' % compCS ) log.msg('KEXINIT: client supported lang: %s' % langCS ) log.msg( 'Remote SSH version: %s' % self.otherVersionString,) - return kippo.core.sshserver.KippoSSHServerTransport.ssh_KEXINIT(self, packet) + return sshserver.KippoSSHServerTransport.ssh_KEXINIT(self, packet) def lastlogExit(self): starttime = time.strftime('%a %b %d %H:%M', @@ -230,7 +234,7 @@ class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport): if self.ttylog_open: ttylog.ttylog_close(self.ttylog_file, time.time()) self.ttylog_open = False - kippo.core.sshserver.KippoSSHServerTransport.connectionLost(self, reason) + sshserver.KippoSSHServerTransport.connectionLost(self, reason) class HoneyPotSSHSession(session.SSHSession): @@ -277,38 +281,38 @@ class HoneyPotAvatar(avatar.ConchUser): if ( self.env.cfg.get('honeypot', 'sftp_enabled') == "true" ): self.subsystemLookup['sftp'] = filetransfer.FileTransferServer - self.uid = self.gid = core.auth.UserDB().getUID(self.username) + self.uid = self.gid = auth.UserDB().getUID(self.username) if not self.uid: self.home = '/root' else: self.home = '/home/' + username - def openShell(self, protocol): - serverProtocol = core.protocol.LoggingServerProtocol( - core.protocol.HoneyPotInteractiveProtocol, self, self.env) - serverProtocol.makeConnection(protocol) - protocol.makeConnection(session.wrapProtocol(serverProtocol)) + def openShell(self, proto): + serverProtocol = protocol.LoggingServerProtocol( + protocol.HoneyPotInteractiveProtocol, self, self.env) + serverProtocol.makeConnection(proto) + proto.makeConnection(session.wrapProtocol(serverProtocol)) def getPty(self, terminal, windowSize, attrs): log.msg( 'Terminal size: %s %s' % windowSize[0:2] ) self.windowSize = windowSize return None - def execCommand(self, protocol, cmd): + def execCommand(self, proto, cmd): cfg = config() if not cfg.has_option('honeypot', 'exec_enabled') or \ cfg.get('honeypot', 'exec_enabled').lower() not in \ ('yes', 'true', 'on'): log.msg( 'Exec disabled. Not executing command: "%s"' % cmd ) - raise core.exceptions.NotEnabledException, \ + raise exceptions.NotEnabledException, \ 'exec_enabled not enabled in configuration file!' return log.msg( 'exec command: "%s"' % cmd ) - serverProtocol = kippo.core.protocol.LoggingServerProtocol( - kippo.core.protocol.HoneyPotExecProtocol, self, self.env, cmd) - serverProtocol.makeConnection(protocol) - protocol.makeConnection(session.wrapProtocol(serverProtocol)) + serverProtocol = protocol.LoggingServerProtocol( + protocol.HoneyPotExecProtocol, self, self.env, cmd) + serverProtocol.makeConnection(proto) + proto.makeConnection(session.wrapProtocol(serverProtocol)) def closed(self): pass diff --git a/kippo/core/utils.py b/kippo/core/utils.py index 8c3bc53a..ab4f99af 100644 --- a/kippo/core/utils.py +++ b/kippo/core/utils.py @@ -1,7 +1,7 @@ # Copyright (c) 2010-2014 Upi Tamminen # See the COPYRIGHT file for more information -from kippo.core.config import config +from config import config def addToLastlog(message): f = file('%s/lastlog.txt' % config().get('honeypot', 'data_path'), 'a') From 95b58538aafcc23cf4cd5b6dd009d79c2aed5d52 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 12:10:06 +0000 Subject: [PATCH 19/30] new copyright, imports, etc --- kippo/dblog/jsonlog.py | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/kippo/dblog/jsonlog.py b/kippo/dblog/jsonlog.py index 52c779de..b93f0faa 100644 --- a/kippo/dblog/jsonlog.py +++ b/kippo/dblog/jsonlog.py @@ -1,19 +1,44 @@ +# Copyright (c) 2015 Michel Oosterhof +# All rights reserved. # -# this module uses the dblog feature to create a JSON logfile -# ..so not exactly a dblog. +# 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. import datetime import uuid import json -from kippo.core import dblog -from twisted.enterprise import adbapi -from twisted.internet import defer -from twisted.python import log +from ..core import dblog class DBLogger(dblog.DBLogger): + def __init__(self, cfg): + self.sensor = "" + self.outfile = "" + dblog.DBLogger.__init__(self, cfg) + def start(self, cfg): self.outfile = file(cfg.get('database_jsonlog', 'logfile'), 'a') From b2bc38e94b18eef649918f26d7904e1adfd9328f Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 12:10:19 +0000 Subject: [PATCH 20/30] another relative import --- kippo/core/fs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kippo/core/fs.py b/kippo/core/fs.py index 0dc9c597..ffcf257f 100644 --- a/kippo/core/fs.py +++ b/kippo/core/fs.py @@ -8,7 +8,7 @@ import re import stat import errno -from kippo.core.config import config +from config import config A_NAME, \ A_TYPE, \ From d4a562a55ab25b1be68341b6485623da2c27ff5f Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 12:27:30 +0000 Subject: [PATCH 21/30] they're instance variables, not class variables --- kippo/core/ssh.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/kippo/core/ssh.py b/kippo/core/ssh.py index db540831..aebbfbfd 100644 --- a/kippo/core/ssh.py +++ b/kippo/core/ssh.py @@ -171,14 +171,10 @@ class HoneyPotTransport(sshserver.KippoSSHServerTransport): @ivar _hadVersion: used so we only send key exchange after receive version info """ - _hadVersion = False - ttylog_open = False - interactors = [] - transportId = '' - def connectionMade(self): self.logintime = time.time() self.transportId = uuid.uuid4().hex[:8] + self.interactors = [] log.msg( 'New connection: %s:%s (%s:%s) [session: %d]' % \ (self.transport.getPeer().host, self.transport.getPeer().port, From f6cff3216a6c7d644361842d54344a448332c735 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 14:10:37 +0000 Subject: [PATCH 22/30] wip --- kippo/core/dblog.py | 1 + kippo/core/protocol.py | 20 +++++++++++++------- kippo/core/ssh.py | 21 +++++++-------------- kippo/dblog/jsonlog.py | 3 +++ 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/kippo/core/dblog.py b/kippo/core/dblog.py index 2590dba0..f08b69e8 100644 --- a/kippo/core/dblog.py +++ b/kippo/core/dblog.py @@ -20,6 +20,7 @@ import abc # KIPP0009 : SSH Version # KIPP0010 : Terminal Size # KIPP0011 : Connection Lost +# KIPP0012 : TTY log closed class DBLogger(object): __metaclass__ = abc.ABCMeta diff --git a/kippo/core/protocol.py b/kippo/core/protocol.py index 55b701dd..a3dbae1c 100644 --- a/kippo/core/protocol.py +++ b/kippo/core/protocol.py @@ -43,7 +43,6 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol): self.realClientPort = transport.transport.getPeer().port self.clientVersion = transport.otherVersionString self.logintime = transport.logintime - self.ttylog_file = transport.ttylog_file # source IP of client in user visible reports (can be fake or real) cfg = config() @@ -67,8 +66,7 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol): except: pass - # this doesn't seem to be called upon disconnect, so please use - # HoneyPotTransport.connectionLost instead + # this is only called on explicit logout, not on disconnect def connectionLost(self, reason): log.msg( eventid='KIPP0011', format='Connection lost') # not sure why i need to do this: @@ -230,7 +228,6 @@ class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLin self.lineBuffer = self.lineBuffer[self.lineBufferIndex:] self.lineBufferIndex = 0 - class LoggingServerProtocol(insults.ServerProtocol): def connectionMade(self): transport = self.transport.session.conn.transport @@ -238,7 +235,8 @@ class LoggingServerProtocol(insults.ServerProtocol): transport.ttylog_file = '%s/tty/%s-%s.log' % \ (config().get('honeypot', 'log_path'), time.strftime('%Y%m%d-%H%M%S'), transport.transportId ) - #log.msg( 'Opening TTY log: %s' % transport.ttylog_file ) + + self.ttylog_file = transport.ttylog_file log.msg( eventid='KIPP0004', logfile=transport.ttylog_file, format='Opening TTY Log: %(logfile)s') @@ -259,6 +257,7 @@ class LoggingServerProtocol(insults.ServerProtocol): if transport.ttylog_open and not noLog: ttylog.ttylog_write(transport.ttylog_file, len(bytes), ttylog.TYPE_OUTPUT, time.time(), bytes) + insults.ServerProtocol.write(self, bytes) def dataReceived(self, data, noLog = False): @@ -270,11 +269,18 @@ class LoggingServerProtocol(insults.ServerProtocol): f = file( transport.stdinlog_file, 'ab' ) f.write(data) f.close + insults.ServerProtocol.dataReceived(self, data) - # this doesn't seem to be called upon disconnect, so please use - # HoneyPotTransport.connectionLost instead + # this is only called on explicit logout, not on disconnect def connectionLost(self, reason): + transport = self.transport.session.conn.transport + if transport.ttylog_open: + ttylog.ttylog_close( transport.ttylog_file, time.time() ) + transport.ttylog_open = False + log.msg( eventid='KIPP0012', format='Closing TTY Log: %(ttylog)s', + ttylog=transport.ttylog_file) + insults.ServerProtocol.connectionLost(self, reason) # vim: set sw=4 et: diff --git a/kippo/core/ssh.py b/kippo/core/ssh.py index ba2a12ff..2342fa21 100644 --- a/kippo/core/ssh.py +++ b/kippo/core/ssh.py @@ -160,25 +160,12 @@ class HoneyPotRealm: class HoneyPotTransport(sshserver.KippoSSHServerTransport): """ - @ivar logintime: time of login - - @ivar interactors: interactors - - @ivar ttylog_open: whether log is open - - @ivar transportId: UUID of this transport - - @ivar _hadVersion: used so we only send key exchange after receive version info """ - _hadVersion = False - ttylog_open = False - interactors = [] - transportId = '' - def connectionMade(self): self.logintime = time.time() self.transportId = uuid.uuid4().hex[:8] + self.interactors = [] #log.msg( 'New connection: %s:%s (%s:%s) [session: %d]' % \ # (self.transport.getPeer().host, self.transport.getPeer().port, @@ -231,6 +218,7 @@ class HoneyPotTransport(sshserver.KippoSSHServerTransport): # this seems to be the only reliable place of catching lost connection def connectionLost(self, reason): + log.msg( "Connection Lost in SSH Transport" ) for i in self.interactors: i.sessionClosed() if self.transport.sessionno in self.factory.sessions: @@ -267,6 +255,9 @@ class HoneyPotSSHSession(session.SSHSession): self.conn.sendRequest(self, 'exit-status', "\x00"*4) session.SSHSession.loseConnection(self) + def channelClosed(self): + log.msg( "Called channelClosed in SSHSession") + # FIXME: recent twisted conch avatar.py uses IConchuser here @implementer(conchinterfaces.ISession) class HoneyPotAvatar(avatar.ConchUser): @@ -295,6 +286,7 @@ class HoneyPotAvatar(avatar.ConchUser): def openShell(self, proto): serverProtocol = protocol.LoggingServerProtocol( protocol.HoneyPotInteractiveProtocol, self, self.env) + self.protocol = serverProtocol serverProtocol.makeConnection(proto) proto.makeConnection(session.wrapProtocol(serverProtocol)) @@ -319,6 +311,7 @@ class HoneyPotAvatar(avatar.ConchUser): log.msg( 'exec command: "%s"' % cmd ) serverProtocol = protocol.LoggingServerProtocol( protocol.HoneyPotExecProtocol, self, self.env, cmd) + self.protocol = serverProtocol serverProtocol.makeConnection(proto) proto.makeConnection(session.wrapProtocol(serverProtocol)) diff --git a/kippo/dblog/jsonlog.py b/kippo/dblog/jsonlog.py index ceaa0be5..7cf0ecf6 100644 --- a/kippo/dblog/jsonlog.py +++ b/kippo/dblog/jsonlog.py @@ -63,6 +63,9 @@ class DBLogger(dblog.DBLogger): def handleConnectionLost(self, session, args): logentry = { 'message': 'Connection lost' } self.write( session, logentry ) + ttylog = self.ttylog(session) + if ttylog: + self.write( session, { 'message': repr(ttylog) } ) def handleLoginFailed(self, session, args): logentry = { 'message' : 'Login failed [%s/%s]' % (args['username'], args['password']), 'username' : args['username'], 'password' : args['password'] } From c8824e94d81c4a5fb93ea7fe994aa5c9280674a6 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 19:47:40 +0000 Subject: [PATCH 23/30] ttylog handling is done from logtransport only now. reliably call logtransport on disconnect --- kippo/core/protocol.py | 21 ++++++++++++++------- kippo/core/ssh.py | 19 ++++++++++++------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/kippo/core/protocol.py b/kippo/core/protocol.py index a4182576..91f1b209 100644 --- a/kippo/core/protocol.py +++ b/kippo/core/protocol.py @@ -230,17 +230,18 @@ class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLin self.lineBuffer = self.lineBuffer[self.lineBufferIndex:] self.lineBufferIndex = 0 - class LoggingServerProtocol(insults.ServerProtocol): + """ + Wrapper for ServerProtocol that implements TTY logging + """ def connectionMade(self): transport = self.transport.session.conn.transport - transport.ttylog_file = '%s/tty/%s-%s.log' % \ (config().get('honeypot', 'log_path'), time.strftime('%Y%m%d-%H%M%S'), transport.transportId ) log.msg( 'Opening TTY log: %s' % transport.ttylog_file ) ttylog.ttylog_open(transport.ttylog_file, time.time()) - transport.ttylog_open = True + self.ttylog_open = True transport.stdinlog_file = '%s/%s-%s-stdin.log' % \ (config().get('honeypot', 'download_path'), @@ -253,14 +254,14 @@ class LoggingServerProtocol(insults.ServerProtocol): transport = self.transport.session.conn.transport for i in transport.interactors: i.sessionWrite(bytes) - if transport.ttylog_open and not noLog: + if self.ttylog_open and not noLog: ttylog.ttylog_write(transport.ttylog_file, len(bytes), ttylog.TYPE_OUTPUT, time.time(), bytes) insults.ServerProtocol.write(self, bytes) def dataReceived(self, data, noLog = False): transport = self.transport.session.conn.transport - if transport.ttylog_open and not noLog: + if self.ttylog_open and not noLog: ttylog.ttylog_write(transport.ttylog_file, len(data), ttylog.TYPE_INPUT, time.time(), data) if transport.stdinlog_open and not noLog: @@ -269,9 +270,15 @@ class LoggingServerProtocol(insults.ServerProtocol): f.close insults.ServerProtocol.dataReceived(self, data) - # this doesn't seem to be called upon disconnect, so please use - # HoneyPotTransport.connectionLost instead + # FIXME: this method is called 4 times on logout.... + # it's called once from Avatar.closed() if disconnected def connectionLost(self, reason): + # log.msg( "received call to LSP.connectionLost" ) + transport = self.transport.session.conn.transport + if self.ttylog_open: + log.msg( 'Closing TTY log: %s' % transport.ttylog_file ) + ttylog.ttylog_close(transport.ttylog_file, time.time()) + self.ttylog_open = False insults.ServerProtocol.connectionLost(self, reason) # vim: set sw=4 et: diff --git a/kippo/core/ssh.py b/kippo/core/ssh.py index aebbfbfd..45cd64c1 100644 --- a/kippo/core/ssh.py +++ b/kippo/core/ssh.py @@ -21,7 +21,6 @@ from twisted.conch.ssh.common import NS, getNS import ConfigParser -import ttylog import utils import fs import sshserver @@ -164,8 +163,6 @@ class HoneyPotTransport(sshserver.KippoSSHServerTransport): @ivar interactors: interactors - @ivar ttylog_open: whether log is open - @ivar transportId: UUID of this transport @ivar _hadVersion: used so we only send key exchange after receive version info @@ -227,9 +224,6 @@ class HoneyPotTransport(sshserver.KippoSSHServerTransport): if self.transport.sessionno in self.factory.sessions: del self.factory.sessions[self.transport.sessionno] self.lastlogExit() - if self.ttylog_open: - ttylog.ttylog_close(self.ttylog_file, time.time()) - self.ttylog_open = False sshserver.KippoSSHServerTransport.connectionLost(self, reason) class HoneyPotSSHSession(session.SSHSession): @@ -254,6 +248,10 @@ class HoneyPotSSHSession(session.SSHSession): log.msg('request_x11: %s' % repr(data) ) return 0 + # this is reliably called on session close/disconnect and calls the avatar + def closed(self): + session.SSHSession.closed(self) + def loseConnection(self): self.conn.sendRequest(self, 'exit-status', "\x00"*4) session.SSHSession.loseConnection(self) @@ -268,6 +266,7 @@ class HoneyPotAvatar(avatar.ConchUser): self.env = env self.fs = fs.HoneyPotFilesystem(copy.deepcopy(self.env.fs)) self.hostname = self.env.cfg.get('honeypot', 'hostname') + self.protocol = None self.channelLookup.update({'session': HoneyPotSSHSession}) self.channelLookup['direct-tcpip'] = KippoOpenConnectForwardingClient @@ -288,6 +287,8 @@ class HoneyPotAvatar(avatar.ConchUser): protocol.HoneyPotInteractiveProtocol, self, self.env) serverProtocol.makeConnection(proto) proto.makeConnection(session.wrapProtocol(serverProtocol)) + #self.protocol = serverProtocol + self.protocol = proto def getPty(self, terminal, windowSize, attrs): log.msg( 'Terminal size: %s %s' % windowSize[0:2] ) @@ -309,9 +310,13 @@ class HoneyPotAvatar(avatar.ConchUser): protocol.HoneyPotExecProtocol, self, self.env, cmd) serverProtocol.makeConnection(proto) proto.makeConnection(session.wrapProtocol(serverProtocol)) + self.protocol = serverProtocol + # this is reliably called on both logout and disconnect + # we notify the protocol here we lost the connection def closed(self): - pass + if self.protocol: + self.protocol.connectionLost("disconnected") def eofReceived(self): pass From 62697b967c25dfc96374cb00d8545cf9ca9b7cfb Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 20:01:04 +0000 Subject: [PATCH 24/30] remove the screen clear/reset on logout --- kippo/core/protocol.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kippo/core/protocol.py b/kippo/core/protocol.py index 91f1b209..65d0d847 100644 --- a/kippo/core/protocol.py +++ b/kippo/core/protocol.py @@ -270,6 +270,10 @@ class LoggingServerProtocol(insults.ServerProtocol): f.close insults.ServerProtocol.dataReceived(self, data) + # override super to remove the terminal reset on logout + def loseConnection(self): + self.transport.loseConnection() + # FIXME: this method is called 4 times on logout.... # it's called once from Avatar.closed() if disconnected def connectionLost(self, reason): From e4cd5442aed88867c6c28a030ad40aef85a13826 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 20:03:44 +0000 Subject: [PATCH 25/30] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05683b10..9a6f4458 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ +* 2015-02-20 Removed screen clear/reset on logout * 2015-02-19 Configuration directives have changed! ssh_addr has become listen_addr and ssh_port has become listen_port. The old keywords are still accepted for backwards compatibility * default behaviour is changed to disable the exit jail From a7f189eed1ca55d787dd96e17d2078892424f9a6 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Thu, 19 Feb 2015 20:21:55 +0000 Subject: [PATCH 26/30] stdin log updates now give log message when writing and simplify variable storage --- kippo/core/protocol.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kippo/core/protocol.py b/kippo/core/protocol.py index 65d0d847..e85b2d45 100644 --- a/kippo/core/protocol.py +++ b/kippo/core/protocol.py @@ -145,7 +145,7 @@ class HoneyPotExecProtocol(HoneyPotBaseProtocol): def connectionMade(self): HoneyPotBaseProtocol.connectionMade(self) - self.terminal.transport.session.conn.transport.stdinlog_open = True + self.terminal.stdinlog_open = True self.cmdstack = [honeypot.HoneyPotShell(self, interactive=False)] self.cmdstack[0].lineReceived(self.execcmd) @@ -243,10 +243,10 @@ class LoggingServerProtocol(insults.ServerProtocol): ttylog.ttylog_open(transport.ttylog_file, time.time()) self.ttylog_open = True - transport.stdinlog_file = '%s/%s-%s-stdin.log' % \ + self.stdinlog_file = '%s/%s-%s-stdin.log' % \ (config().get('honeypot', 'download_path'), time.strftime('%Y%m%d-%H%M%S'), transport.transportId ) - transport.stdinlog_open = False + self.stdinlog_open = False insults.ServerProtocol.connectionMade(self) @@ -264,8 +264,9 @@ class LoggingServerProtocol(insults.ServerProtocol): if self.ttylog_open and not noLog: ttylog.ttylog_write(transport.ttylog_file, len(data), ttylog.TYPE_INPUT, time.time(), data) - if transport.stdinlog_open and not noLog: - f = file( transport.stdinlog_file, 'ab' ) + if self.stdinlog_open and not noLog: + log.msg( "Saving stdin log: %s" % self.stdinlog_file ) + f = file( self.stdinlog_file, 'ab' ) f.write(data) f.close insults.ServerProtocol.dataReceived(self, data) From ea7148c521af3ac20fbd7402f477189e3b2b0b23 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 24 Feb 2015 09:37:04 +0000 Subject: [PATCH 27/30] support for 'uname -m' --- kippo/commands/uname.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kippo/commands/uname.py b/kippo/commands/uname.py index 21227d41..5070fa42 100644 --- a/kippo/commands/uname.py +++ b/kippo/commands/uname.py @@ -12,6 +12,8 @@ class command_uname(HoneyPotCommand): self.honeypot.hostname) elif len(self.args) and self.args[0].strip() in ('-r', '--kernel-release'): self.writeln( '2.6.26-2-686' ) + elif len(self.args) and self.args[0].strip() in ('-m', '--machine'): + self.writeln( 'i686' ) else: self.writeln('Linux') From b48e10adb78bd961dfc31ccb1f69044134844506 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 24 Feb 2015 09:38:33 +0000 Subject: [PATCH 28/30] changelog update --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a6f4458..561f878d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ * allow wget download over non-80 port * simple JSON logging to kippo.json * accept log and deny publickey authentication -* add uname -r command +* add uname -r, -m flags * add working sleep command * enabled ssh diffie-hellman-group-exchange-sha1 algorithm * add 'bash -c' support (no effect option) From 8cf00addaf7043f3809c1e5d7848c664cec69c24 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Wed, 25 Feb 2015 17:13:14 +0000 Subject: [PATCH 29/30] don't write ttylog to JSON, is for test only --- kippo/dblog/jsonlog.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kippo/dblog/jsonlog.py b/kippo/dblog/jsonlog.py index 7cf0ecf6..7d70b6bf 100644 --- a/kippo/dblog/jsonlog.py +++ b/kippo/dblog/jsonlog.py @@ -63,9 +63,9 @@ class DBLogger(dblog.DBLogger): def handleConnectionLost(self, session, args): logentry = { 'message': 'Connection lost' } self.write( session, logentry ) - ttylog = self.ttylog(session) - if ttylog: - self.write( session, { 'message': repr(ttylog) } ) + #ttylog = self.ttylog(session) + #if ttylog: + # self.write( session, { 'message': repr(ttylog) } ) def handleLoginFailed(self, session, args): logentry = { 'message' : 'Login failed [%s/%s]' % (args['username'], args['password']), 'username' : args['username'], 'password' : args['password'] } From d36e8077e4a0c1053294ab9aa46a730e4c0c97c5 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Wed, 25 Feb 2015 17:17:03 +0000 Subject: [PATCH 30/30] forgot changelog as usual --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e3726bf..45aa87f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ +* 2015-02-25 Internals for dblog/ modules changed completely. Now accepts structured logging arguments, and uses eventids instead of regex parsing * 2015-02-20 Removed screen clear/reset on logout * 2015-02-19 Configuration directives have changed! ssh_addr has become listen_addr and ssh_port has become listen_port. The old keywords are still accepted for backwards compatibility