dblog : work in progress : formatted log messages

This commit is contained in:
Michel Oosterhof
2015-02-03 10:50:21 +00:00
parent da1dac8260
commit dfc6174122
2 changed files with 80 additions and 52 deletions

View File

@@ -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<username>.*)/(?P<password>.*)\] failed',
self.handleLoginFailed),
('^login attempt \[(?P<username>.*)/(?P<password>.*)\] succeeded',
self.handleLoginSucceeded),
('^Opening TTY log: (?P<logfile>.*)$',
self.handleTTYLogOpened),
('^:dispatch: Command found: (?P<input>.*)$',
self.handleCommand),
('^:dispatch: Command not found: (?P<input>.*)$',
self.handleUnknownCommand),
('^:dispatch: Saving URL \((?P<url>.*)\) to (?P<outfile>.*)$',
self.handleFileDownload),
('^INPUT \((?P<realm>[a-zA-Z0-9]+)\): (?P<input>.*)$',
self.handleInput),
('^Terminal size: (?P<height>[0-9]+) (?P<width>[0-9]+)$',
self.handleTerminalSize),
('^Remote SSH version: (?P<version>.*)$',
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

View File

@@ -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