Added a telnet based session management interface for interacting with active

sessions


git-svn-id: https://kippo.googlecode.com/svn/trunk@209 951d7100-d841-11de-b865-b3884708a8e2
This commit is contained in:
desaster
2011-10-21 09:29:06 +00:00
parent 796dc1babb
commit f8429dfd0f
4 changed files with 189 additions and 0 deletions

View File

@@ -108,6 +108,16 @@ private_key = private.key
# (default: not specified)
#banner_file =
# Session management interface.
#
# This is a telnet based service that can be used to interact with active
# sessions. Disabled by default.
#
# (default: false)
interact_enable = false
# (default: 5123)
interact_port = 5123
# MySQL logging module
#
# Database structure for this module is supplied in doc/sql/mysql.sql

View File

@@ -46,4 +46,13 @@ for i in ssh_addr.split():
interface=i)
service.setServiceParent(application)
if cfg.has_option('honeypot', 'interact_enabled') and \
cfg.get('honeypot', 'interact_enabled').lower() in \
('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)
# vim: set ft=python sw=4 et:

View File

@@ -254,6 +254,7 @@ class HoneyPotProtocol(recvline.HistoricRecvLine):
self.cmdstack = [HoneyPotShell(self)]
transport = self.terminal.transport.session.conn.transport
transport.factory.sessions.append(self) # this is for the interactors
# You are in a maze of twisty little passages, all alike
p = transport.transport.getPeer()
@@ -296,6 +297,8 @@ class HoneyPotProtocol(recvline.HistoricRecvLine):
def connectionLost(self, reason):
recvline.HistoricRecvLine.connectionLost(self, reason)
transport = self.terminal.transport.session.conn.transport
transport.factory.sessions.remove(self)
self.lastlogExit()
# not sure why i need to do this:
@@ -392,6 +395,12 @@ class HoneyPotProtocol(recvline.HistoricRecvLine):
def handle_TAB(self):
self.cmdstack[-1].handle_TAB()
def addInteractor(self, interactor):
self.terminal.interactors.append(interactor)
def delInteractor(self, interactor):
self.terminal.interactors.remove(interactor)
class LoggingServerProtocol(insults.ServerProtocol):
def connectionMade(self):
self.ttylog_file = '%s/tty/%s-%s.log' % \
@@ -401,15 +410,20 @@ class LoggingServerProtocol(insults.ServerProtocol):
print 'Opening TTY log: %s' % self.ttylog_file
ttylog.ttylog_open(self.ttylog_file, time.time())
self.ttylog_open = True
self.interactors = []
insults.ServerProtocol.connectionMade(self)
def write(self, bytes, noLog = False):
for i in self.interactors:
i.sessionWrite(bytes)
if self.ttylog_open and not noLog:
ttylog.ttylog_write(self.ttylog_file, len(bytes),
ttylog.DIR_WRITE, time.time(), bytes)
insults.ServerProtocol.write(self, bytes)
def connectionLost(self, reason):
for i in self.interactors:
i.sessionClosed()
if self.ttylog_open:
ttylog.ttylog_close(self.ttylog_file, time.time())
self.ttylog_open = False
@@ -532,6 +546,9 @@ class HoneyPotSSHFactory(factory.SSHFactory):
def __init__(self):
cfg = config()
# protocol instances are kept here for use by the interact feature
self.sessions = []
# convert old pass.db root passwords
passdb_file = '%s/pass.db' % (cfg.get('honeypot', 'data_path'),)
if os.path.exists(passdb_file):

153
kippo/core/interact.py Normal file
View File

@@ -0,0 +1,153 @@
from twisted.internet import protocol
from twisted.conch import telnet
class Interact(telnet.Telnet):
def connectionMade(self):
print 'Connected'
self.interacting = None
self.cmdbuf = ''
self.honeypotFactory = self.factory.honeypotFactory
# someone tell me if i'm doing this wrong?
d = self.do(telnet.LINEMODE)
self.requestNegotiation(telnet.LINEMODE, telnet.LINEMODE_EDIT + '\0')
self.will(telnet.ECHO)
self.transport.write('*** kippo session management console ***\r\n')
self.cmd_help()
def connectionLost(self, reason):
print 'Connection lost'
if self.interacting != None:
self.interacting.delInteractor(self)
def enableRemote(self, option):
print 'enableRemote', repr(option)
return option == telnet.LINEMODE
def disableRemote(self, option):
print 'disableRemote', repr(option)
def applicationDataReceived(self, bytes):
# in command mode, we want to echo characters and buffer the input
if not self.interacting:
self.transport.write(bytes)
if bytes == '\r':
self.transport.write('\n')
pieces = self.cmdbuf.split(' ', 1)
self.cmdbuf = ''
cmd, args = pieces[0], ''
if len(pieces) > 1:
args = pieces[1]
try:
func = getattr(self, 'cmd_' + cmd)
except AttributeError:
print 'Unknown command: %s' % (cmd,)
self.transport.write('** Unknown command.\r\n')
return
func(args)
else:
self.cmdbuf += bytes
# in non-command mode we are passing input to the session we are
# watching
else:
for c in bytes:
if ord(c) == 27: # escape
self.interacting.delInteractor(self)
self.interacting = None
self.transport.write(
'\r\n** Interactive session closed.\r\n')
return
if not self.readonly:
self.interacting.keystrokeReceived(bytes, None)
def sessionWrite(self, data):
buf, prev = '', ''
for c in data:
if c == '\n' and prev != '\r':
buf += '\r\n'
else:
buf += c
prev = c
self.transport.write(buf)
def sessionClosed(self):
self.interacting.delInteractor(self)
self.interacting = None
self.transport.write('\r\n** Interactive session disconnected.\r\n')
def cmd_hijack(self, args):
self.cmd_view(args)
self.readonly = False
def cmd_view(self, args):
self.readonly = True
try:
sessionno = int(args)
except ValueError:
self.transport.write('** Invalid session ID.\r\n')
return
for s in self.honeypotFactory.sessions:
transport = s.terminal.transport.session.conn.transport
if sessionno == transport.transport.sessionno:
self.view(s)
return
self.transport.write('** No such session found.\r\n')
def view(self, session):
transport = session.terminal.transport.session.conn.transport
sessionno = transport.transport.sessionno
self.transport.write(
'** Attaching to #%d, hit ESC to return\r\n' % sessionno)
session.addInteractor(self)
self.interacting = session
def cmd_list(self, args):
self.transport.write('ID clientIP clientVersion\r\n')
for s in self.honeypotFactory.sessions:
transport = s.terminal.transport.session.conn.transport
sessionno = transport.transport.sessionno
self.transport.write('%s %s %s\r\n' % \
(str(sessionno).ljust(4),
s.realClientIP.ljust(15),
s.clientVersion))
def cmd_help(self, args = ''):
self.transport.write('List of commands:\r\n')
self.transport.write(' list - list all active sessions\r\n')
self.transport.write(
' view - attach to a session in read-only mode\r\n')
self.transport.write(
' hijack - attach to a session in interactive mode\r\n')
self.transport.write(
' disconnect - disconnect a session\r\n')
self.transport.write(' help - this help\r\n')
self.transport.write(' exit - disconnect the console\r\n')
def cmd_disconnect(self, args):
try:
sessionno = int(args)
except ValueError:
self.transport.write('** Invalid session ID.\r\n')
return
for s in self.honeypotFactory.sessions:
transport = s.terminal.transport.session.conn.transport
if sessionno == transport.transport.sessionno:
self.transport.write(
'** Disconnecting session #%d\r\n' % sessionno)
transport.loseConnection()
return
self.transport.write('** No such session found.\r\n')
def cmd_exit(self, args = ''):
self.transport.loseConnection()
def makeInteractFactory(honeypotFactory):
ifactory = protocol.Factory()
ifactory.protocol = Interact
ifactory.honeypotFactory = honeypotFactory
return ifactory
# vim: set sw=4 et: