cowrie/kippo/core/output.py

158 lines
5.3 KiB
Python

# Copyright (c) 2015 Michel Oosterhof <michel@oosterhof.net>
# 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.
import abc
import datetime
import re
import copy
import socket
import uuid
# 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
# KIPP0012 : TTY log closed
class Output(object):
"""
This is the abstract base class intended to be inherited by kippo output plugins
Plugins require the mandatory methods: stop, start and handleLog
"""
__metaclass__ = abc.ABCMeta
def __init__(self, cfg):
self.cfg = cfg
self.sessions = {}
self.ips = {}
self.re_sessionlog = re.compile(
'.*HoneyPotTransport,([0-9]+),[0-9.]+$')
if self.cfg.has_option('honeypot', 'sensor_name'):
self.sensor = self.cfg.get('honeypot', 'sensor_name')
else:
self.sensor = socket.gethostname()
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 isinstance( msg, dict ):
# msg['sessionid'] = 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)
@abc.abstractmethod
def start(self):
"""Abstract method to initialize output plugin"""
pass
@abc.abstractmethod
def stop(self):
"""Abstract method to shut down output plugin"""
pass
@abc.abstractmethod
def handleLog(self, session, event):
"""Handle a general event within the output plugin"""
pass
# this is the main emit() hook that gets called by the the Twisted logging
def emit(self, event):
# ignore stdout and stderr in output plugins
if 'printed' in event:
return
# ignore anything without eventid
if not 'eventid' in event:
return
ev = copy.copy(event)
if 'isError' in ev:
del ev['isError']
ev['sensor'] = self.sensor
# add ISO timestamp and sensor data
if not 'time' in ev:
ev['timestamp'] = datetime.datetime.today().isoformat() + 'Z'
else:
ev['timestamp'] = datetime.datetime.fromtimestamp(ev['time']).isoformat() + 'Z'
del ev['time']
# on disconnect, add the tty log
#if ev['eventid'] == 'KIPP0012':
# FIXME: file is read for each output plugin
#f = file(ev['ttylog'])
#ev['ttylog'] = f.read(10485760)
#f.close()
#pass
# explicit sessionno (from logDispatch) overrides from 'system'
if 'sessionno' in ev:
sessionno = ev['sessionno']
del ev['sessionno']
# extract session id from the twisted log prefix
elif 'system' in ev:
match = self.re_sessionlog.match(ev['system'])
if not match:
return
sessionno = int(match.groups()[0])
del ev['system']
if sessionno in self.ips:
ev['src_ip'] = self.ips[sessionno]
# connection event is special. adds to session list
if ev['eventid'] == 'KIPP0001':
self.sessions[sessionno] = uuid.uuid4().hex
self.ips[sessionno] = ev['src_ip']
del ev['system']
self.handleLog(self.sessions[sessionno], ev)
# disconnect is special, remove cached data
if ev['eventid'] == 'KIPP0011':
del self.sessions[sessionno]
del self.ips[sessionno]
# vim: set sw=4 et: