2009-11-22 07:07:58 +00:00
|
|
|
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
|
|
|
# See the COPYRIGHT file for more information
|
|
|
|
|
|
|
|
from twisted.cred import portal, checkers, credentials, error
|
|
|
|
from twisted.conch import avatar, recvline, interfaces as conchinterfaces
|
|
|
|
from twisted.conch.ssh import factory, userauth, connection, keys, session, common, transport
|
|
|
|
from twisted.conch.insults import insults
|
|
|
|
from twisted.application import service, internet
|
|
|
|
from twisted.protocols.policies import TrafficLoggingFactory
|
|
|
|
from twisted.internet import reactor, protocol, defer
|
|
|
|
from twisted.python import failure, log
|
|
|
|
from zope.interface import implements
|
|
|
|
from copy import deepcopy, copy
|
2010-04-14 09:26:04 +00:00
|
|
|
import sys, os, random, pickle, time, stat, shlex, anydbm
|
2009-11-22 07:07:58 +00:00
|
|
|
|
2010-06-21 17:53:35 +00:00
|
|
|
from kippo.core import ttylog, fs, utils
|
2011-02-05 21:53:54 +00:00
|
|
|
from kippo.core.userdb import UserDB
|
2009-11-22 07:07:58 +00:00
|
|
|
from kippo.core.config import config
|
|
|
|
import commands
|
|
|
|
|
2011-01-17 12:15:24 +00:00
|
|
|
import ConfigParser
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
class HoneyPotCommand(object):
|
|
|
|
def __init__(self, honeypot, *args):
|
|
|
|
self.honeypot = honeypot
|
|
|
|
self.args = args
|
|
|
|
self.writeln = self.honeypot.writeln
|
|
|
|
self.write = self.honeypot.terminal.write
|
|
|
|
self.nextLine = self.honeypot.terminal.nextLine
|
|
|
|
self.fs = self.honeypot.fs
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
self.call()
|
|
|
|
self.exit()
|
|
|
|
|
|
|
|
def call(self):
|
|
|
|
self.honeypot.writeln('Hello World! [%s]' % repr(self.args))
|
|
|
|
|
|
|
|
def exit(self):
|
|
|
|
self.honeypot.cmdstack.pop()
|
|
|
|
self.honeypot.cmdstack[-1].resume()
|
|
|
|
|
|
|
|
def ctrl_c(self):
|
|
|
|
print 'Received CTRL-C, exiting..'
|
|
|
|
self.writeln('^C')
|
|
|
|
self.exit()
|
|
|
|
|
|
|
|
def lineReceived(self, line):
|
|
|
|
print 'INPUT: %s' % line
|
|
|
|
|
|
|
|
def resume(self):
|
|
|
|
pass
|
|
|
|
|
2011-02-05 18:44:00 +00:00
|
|
|
def handle_TAB(self):
|
|
|
|
pass
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
class HoneyPotShell(object):
|
|
|
|
def __init__(self, honeypot):
|
|
|
|
self.honeypot = honeypot
|
|
|
|
self.showPrompt()
|
2009-11-24 18:52:19 +00:00
|
|
|
self.cmdpending = []
|
2010-04-28 20:02:15 +00:00
|
|
|
self.envvars = {
|
|
|
|
'PATH': '/bin:/usr/bin:/sbin:/usr/sbin',
|
|
|
|
}
|
2009-11-22 07:07:58 +00:00
|
|
|
|
|
|
|
def lineReceived(self, line):
|
|
|
|
print 'CMD: %s' % line
|
2009-11-24 18:52:19 +00:00
|
|
|
for i in [x.strip() for x in line.strip().split(';')]:
|
|
|
|
if not len(i):
|
|
|
|
continue
|
|
|
|
self.cmdpending.append(i)
|
|
|
|
if len(self.cmdpending):
|
|
|
|
self.runCommand()
|
|
|
|
else:
|
|
|
|
self.showPrompt()
|
|
|
|
|
|
|
|
def runCommand(self):
|
2010-10-25 14:11:55 +00:00
|
|
|
def runOrPrompt():
|
|
|
|
if len(self.cmdpending):
|
|
|
|
self.runCommand()
|
|
|
|
else:
|
|
|
|
self.showPrompt()
|
|
|
|
|
2009-11-24 18:52:19 +00:00
|
|
|
if not len(self.cmdpending):
|
2009-11-22 07:07:58 +00:00
|
|
|
self.showPrompt()
|
|
|
|
return
|
2010-06-08 19:13:09 +00:00
|
|
|
line = self.cmdpending.pop(0)
|
2009-11-22 07:07:58 +00:00
|
|
|
try:
|
2010-06-08 19:13:09 +00:00
|
|
|
cmdAndArgs = shlex.split(line)
|
2009-11-22 07:07:58 +00:00
|
|
|
except:
|
|
|
|
self.honeypot.writeln(
|
|
|
|
'-bash: syntax error: unexpected end of file')
|
2009-11-24 18:52:19 +00:00
|
|
|
# could run runCommand here, but i'll just clear the list instead
|
|
|
|
self.cmdpending = []
|
2009-11-22 07:07:58 +00:00
|
|
|
self.showPrompt()
|
|
|
|
return
|
2010-04-28 20:02:15 +00:00
|
|
|
|
|
|
|
# probably no reason to be this comprehensive for just PATH...
|
|
|
|
envvars = copy(self.envvars)
|
2010-10-25 14:11:55 +00:00
|
|
|
cmd = None
|
2010-04-28 20:02:15 +00:00
|
|
|
while len(cmdAndArgs):
|
2010-10-25 14:11:55 +00:00
|
|
|
piece = cmdAndArgs.pop(0)
|
|
|
|
if piece.count('='):
|
|
|
|
key, value = piece.split('=', 1)
|
2010-04-28 20:02:15 +00:00
|
|
|
envvars[key] = value
|
|
|
|
continue
|
2010-10-25 14:11:55 +00:00
|
|
|
cmd = piece
|
2010-04-28 20:02:15 +00:00
|
|
|
break
|
|
|
|
args = cmdAndArgs
|
|
|
|
|
2010-10-25 14:11:55 +00:00
|
|
|
if not cmd:
|
|
|
|
runOrPrompt()
|
|
|
|
return
|
|
|
|
|
2009-11-24 21:35:51 +00:00
|
|
|
rargs = []
|
|
|
|
for arg in args:
|
|
|
|
matches = self.honeypot.fs.resolve_path_wc(arg, self.honeypot.cwd)
|
|
|
|
if matches:
|
|
|
|
rargs.extend(matches)
|
|
|
|
else:
|
|
|
|
rargs.append(arg)
|
2010-04-28 20:02:15 +00:00
|
|
|
cmdclass = self.honeypot.getCommand(cmd, envvars['PATH'].split(':'))
|
2009-11-22 07:07:58 +00:00
|
|
|
if cmdclass:
|
2010-06-08 19:13:09 +00:00
|
|
|
print 'Command found: %s' % (line,)
|
2011-02-06 09:04:44 +00:00
|
|
|
self.honeypot.logDispatch('Command found: %s' % (line,))
|
2010-05-08 20:38:09 +00:00
|
|
|
self.honeypot.call_command(cmdclass, *rargs)
|
2009-11-22 07:07:58 +00:00
|
|
|
else:
|
2011-02-06 09:04:44 +00:00
|
|
|
self.honeypot.logDispatch('Command not found: %s' % (line,))
|
2010-06-08 19:13:09 +00:00
|
|
|
print 'Command not found: %s' % (line,)
|
|
|
|
if len(line):
|
2009-11-22 07:07:58 +00:00
|
|
|
self.honeypot.writeln('bash: %s: command not found' % cmd)
|
2010-10-25 14:11:55 +00:00
|
|
|
runOrPrompt()
|
2009-11-22 07:07:58 +00:00
|
|
|
|
|
|
|
def resume(self):
|
|
|
|
self.honeypot.setInsertMode()
|
2009-11-24 18:52:19 +00:00
|
|
|
self.runCommand()
|
2009-11-22 07:07:58 +00:00
|
|
|
|
|
|
|
def showPrompt(self):
|
2011-02-05 21:53:54 +00:00
|
|
|
if not self.honeypot.user.uid:
|
|
|
|
prompt = '%s:%%(path)s# ' % self.honeypot.hostname
|
|
|
|
else:
|
|
|
|
prompt = '%s:%%(path)s$ ' % self.honeypot.hostname
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
path = self.honeypot.cwd
|
2011-02-05 21:53:54 +00:00
|
|
|
homelen = len(self.honeypot.user.home)
|
|
|
|
if path == self.honeypot.user.home:
|
2009-11-22 07:07:58 +00:00
|
|
|
path = '~'
|
2011-02-05 21:53:54 +00:00
|
|
|
elif len(path) > (homelen+1) and \
|
|
|
|
path[:(homelen+1)] == self.honeypot.user.home + '/':
|
|
|
|
path = '~' + path[homelen:]
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
attrs = {'path': path}
|
|
|
|
self.honeypot.terminal.write(prompt % attrs)
|
|
|
|
|
|
|
|
def ctrl_c(self):
|
2009-11-25 17:10:55 +00:00
|
|
|
self.honeypot.lineBuffer = []
|
|
|
|
self.honeypot.lineBufferIndex = 0
|
2009-11-22 07:07:58 +00:00
|
|
|
self.honeypot.terminal.nextLine()
|
|
|
|
self.showPrompt()
|
|
|
|
|
2011-02-05 18:44:00 +00:00
|
|
|
# Tab completion
|
|
|
|
def handle_TAB(self):
|
|
|
|
if not len(self.honeypot.lineBuffer):
|
|
|
|
return
|
|
|
|
l = ''.join(self.honeypot.lineBuffer)
|
|
|
|
if l[-1] == ' ':
|
|
|
|
clue = ''
|
|
|
|
else:
|
|
|
|
clue = ''.join(self.honeypot.lineBuffer).split()[-1]
|
|
|
|
try:
|
|
|
|
basedir = os.path.dirname(clue)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
if len(basedir) and basedir[-1] != '/':
|
|
|
|
basedir += '/'
|
|
|
|
|
|
|
|
files = []
|
|
|
|
tmppath = basedir
|
|
|
|
if not len(basedir):
|
|
|
|
tmppath = self.honeypot.cwd
|
|
|
|
try:
|
|
|
|
r = self.honeypot.fs.resolve_path(tmppath, self.honeypot.cwd)
|
|
|
|
except:
|
|
|
|
return
|
|
|
|
for x in self.honeypot.fs.get_path(r):
|
|
|
|
if clue == '':
|
|
|
|
files.append(x)
|
|
|
|
continue
|
|
|
|
if not x[fs.A_NAME].startswith(os.path.basename(clue)):
|
|
|
|
continue
|
|
|
|
files.append(x)
|
|
|
|
|
|
|
|
if len(files) == 0:
|
|
|
|
return
|
|
|
|
|
|
|
|
# Clear early so we can call showPrompt if needed
|
|
|
|
for i in range(self.honeypot.lineBufferIndex):
|
|
|
|
self.honeypot.terminal.cursorBackward()
|
|
|
|
self.honeypot.terminal.deleteCharacter()
|
|
|
|
|
|
|
|
newbuf = ''
|
|
|
|
if len(files) == 1:
|
|
|
|
newbuf = ' '.join(l.split()[:-1] + \
|
|
|
|
['%s%s' % (basedir, files[0][fs.A_NAME])])
|
|
|
|
if files[0][fs.A_TYPE] == fs.T_DIR:
|
|
|
|
newbuf += '/'
|
|
|
|
else:
|
|
|
|
newbuf += ' '
|
|
|
|
else:
|
|
|
|
if len(os.path.basename(clue)):
|
|
|
|
prefix = os.path.commonprefix([x[fs.A_NAME] for x in files])
|
|
|
|
else:
|
|
|
|
prefix = ''
|
|
|
|
first = l.split(' ')[:-1]
|
|
|
|
newbuf = ' '.join(first + ['%s%s' % (basedir, prefix)])
|
|
|
|
if newbuf == ''.join(self.honeypot.lineBuffer):
|
|
|
|
self.honeypot.terminal.nextLine()
|
|
|
|
maxlen = max([len(x[fs.A_NAME]) for x in files]) + 1
|
|
|
|
perline = int(self.honeypot.user.windowSize[1] / (maxlen + 1))
|
|
|
|
count = 0
|
|
|
|
for file in files:
|
|
|
|
if count == perline:
|
|
|
|
count = 0
|
|
|
|
self.honeypot.terminal.nextLine()
|
|
|
|
self.honeypot.terminal.write(file[fs.A_NAME].ljust(maxlen))
|
|
|
|
count += 1
|
|
|
|
self.honeypot.terminal.nextLine()
|
|
|
|
self.showPrompt()
|
|
|
|
|
|
|
|
self.honeypot.lineBuffer = list(newbuf)
|
|
|
|
self.honeypot.lineBufferIndex = len(self.honeypot.lineBuffer)
|
|
|
|
self.honeypot.terminal.write(newbuf)
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
class HoneyPotProtocol(recvline.HistoricRecvLine):
|
|
|
|
def __init__(self, user, env):
|
|
|
|
self.user = user
|
|
|
|
self.env = env
|
2011-02-05 21:53:54 +00:00
|
|
|
self.cwd = user.home
|
2009-11-22 07:07:58 +00:00
|
|
|
self.hostname = self.env.cfg.get('honeypot', 'hostname')
|
|
|
|
self.fs = fs.HoneyPotFilesystem(deepcopy(self.env.fs))
|
|
|
|
# commands is also a copy so we can add stuff on the fly
|
|
|
|
self.commands = copy(self.env.commands)
|
|
|
|
self.password_input = False
|
|
|
|
self.cmdstack = []
|
|
|
|
|
2011-02-06 09:04:44 +00:00
|
|
|
def logDispatch(self, msg):
|
|
|
|
transport = self.terminal.transport.session.conn.transport
|
|
|
|
msg = ':dispatch: ' + msg
|
|
|
|
transport.factory.logDispatch(transport.transport.sessionno, msg)
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
def connectionMade(self):
|
|
|
|
recvline.HistoricRecvLine.connectionMade(self)
|
2011-02-03 16:25:45 +00:00
|
|
|
self.displayMOTD()
|
2009-11-22 07:07:58 +00:00
|
|
|
self.cmdstack = [HoneyPotShell(self)]
|
|
|
|
|
2011-02-06 09:04:44 +00:00
|
|
|
transport = self.terminal.transport.session.conn.transport
|
|
|
|
|
2009-11-25 19:02:16 +00:00
|
|
|
# You are in a maze of twisty little passages, all alike
|
2011-02-06 09:04:44 +00:00
|
|
|
p = transport.transport.getPeer()
|
2010-06-08 17:37:16 +00:00
|
|
|
|
2010-10-25 14:57:14 +00:00
|
|
|
# real source IP of client
|
|
|
|
self.realClientIP = p.host
|
|
|
|
|
2011-02-06 09:04:44 +00:00
|
|
|
self.clientVersion = transport.otherVersionString
|
2011-02-03 16:35:54 +00:00
|
|
|
|
2010-10-25 14:57:14 +00:00
|
|
|
# source IP of client in user visible reports (can be fake or real)
|
|
|
|
cfg = config()
|
|
|
|
if cfg.has_option('honeypot', 'fake_addr'):
|
|
|
|
self.clientIP = cfg.get('honeypot', 'fake_addr')
|
|
|
|
else:
|
|
|
|
self.clientIP = self.realClientIP
|
|
|
|
|
2009-11-25 19:02:16 +00:00
|
|
|
self.logintime = time.time()
|
|
|
|
|
2010-05-08 20:38:09 +00:00
|
|
|
self.keyHandlers.update({
|
|
|
|
'\x04': self.handle_CTRL_D,
|
|
|
|
'\x15': self.handle_CTRL_U,
|
|
|
|
'\x03': self.handle_CTRL_C,
|
2011-02-05 18:44:00 +00:00
|
|
|
'\x09': self.handle_TAB,
|
2010-05-08 20:38:09 +00:00
|
|
|
})
|
|
|
|
|
2011-02-03 16:25:45 +00:00
|
|
|
def displayMOTD(self):
|
|
|
|
try:
|
|
|
|
self.writeln(self.fs.file_contents('/etc/motd'))
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
2010-06-21 17:53:35 +00:00
|
|
|
def lastlogExit(self):
|
|
|
|
starttime = time.strftime('%a %b %d %H:%M',
|
|
|
|
time.localtime(self.logintime))
|
|
|
|
endtime = time.strftime('%H:%M',
|
|
|
|
time.localtime(time.time()))
|
|
|
|
duration = utils.durationHuman(time.time() - self.logintime)
|
|
|
|
utils.addToLastlog('root\tpts/0\t%s\t%s - %s (%s)' % \
|
|
|
|
(self.clientIP, starttime, endtime, duration))
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
def connectionLost(self, reason):
|
|
|
|
recvline.HistoricRecvLine.connectionLost(self, reason)
|
2010-06-21 17:53:35 +00:00
|
|
|
self.lastlogExit()
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
# not sure why i need to do this:
|
|
|
|
del self.fs
|
|
|
|
del self.commands
|
|
|
|
|
|
|
|
# Overriding to prevent terminal.reset()
|
|
|
|
def initializeScreen(self):
|
|
|
|
self.setInsertMode()
|
|
|
|
|
2010-04-06 05:35:38 +00:00
|
|
|
def txtcmd(self, txt):
|
|
|
|
class command_txtcmd(HoneyPotCommand):
|
|
|
|
def call(self):
|
|
|
|
print 'Reading txtcmd from "%s"' % txt
|
|
|
|
f = file(txt, 'r')
|
|
|
|
self.write(f.read())
|
|
|
|
f.close()
|
|
|
|
return command_txtcmd
|
|
|
|
|
2010-04-28 20:02:15 +00:00
|
|
|
def getCommand(self, cmd, paths):
|
2009-11-22 07:07:58 +00:00
|
|
|
if not len(cmd.strip()):
|
|
|
|
return None
|
|
|
|
path = None
|
|
|
|
if cmd in self.commands:
|
|
|
|
return self.commands[cmd]
|
|
|
|
if cmd[0] in ('.', '/'):
|
|
|
|
path = self.fs.resolve_path(cmd, self.cwd)
|
|
|
|
if not self.fs.exists(path):
|
|
|
|
return None
|
|
|
|
else:
|
2010-04-28 20:02:15 +00:00
|
|
|
for i in ['%s/%s' % (self.fs.resolve_path(x, self.cwd), cmd) \
|
|
|
|
for x in paths]:
|
2009-11-22 07:07:58 +00:00
|
|
|
if self.fs.exists(i):
|
|
|
|
path = i
|
|
|
|
break
|
2010-04-14 09:26:04 +00:00
|
|
|
txt = os.path.abspath('%s/%s' % \
|
|
|
|
(self.env.cfg.get('honeypot', 'txtcmds_path'), path))
|
2010-04-06 05:35:38 +00:00
|
|
|
if os.path.exists(txt):
|
|
|
|
return self.txtcmd(txt)
|
2009-11-22 07:07:58 +00:00
|
|
|
if path in self.commands:
|
|
|
|
return self.commands[path]
|
|
|
|
return None
|
|
|
|
|
|
|
|
def lineReceived(self, line):
|
|
|
|
if len(self.cmdstack):
|
|
|
|
self.cmdstack[-1].lineReceived(line)
|
|
|
|
|
|
|
|
def keystrokeReceived(self, keyID, modifier):
|
|
|
|
if type(keyID) == type(''):
|
|
|
|
ttylog.ttylog_write(self.terminal.ttylog_file, len(keyID),
|
|
|
|
ttylog.DIR_READ, time.time(), keyID)
|
|
|
|
recvline.HistoricRecvLine.keystrokeReceived(self, keyID, modifier)
|
|
|
|
|
|
|
|
# Easier way to implement password input?
|
|
|
|
def characterReceived(self, ch, moreCharactersComing):
|
|
|
|
if self.mode == 'insert':
|
|
|
|
self.lineBuffer.insert(self.lineBufferIndex, ch)
|
|
|
|
else:
|
|
|
|
self.lineBuffer[self.lineBufferIndex:self.lineBufferIndex+1] = [ch]
|
|
|
|
self.lineBufferIndex += 1
|
|
|
|
if not self.password_input:
|
|
|
|
self.terminal.write(ch)
|
|
|
|
|
|
|
|
def writeln(self, data):
|
|
|
|
self.terminal.write(data)
|
|
|
|
self.terminal.nextLine()
|
|
|
|
|
2010-05-08 20:38:09 +00:00
|
|
|
def call_command(self, cmd, *args):
|
|
|
|
obj = cmd(self, *args)
|
|
|
|
self.cmdstack.append(obj)
|
|
|
|
self.setTypeoverMode()
|
|
|
|
obj.start()
|
|
|
|
|
2009-11-25 17:10:55 +00:00
|
|
|
def handle_RETURN(self):
|
|
|
|
if len(self.cmdstack) == 1:
|
|
|
|
if self.lineBuffer:
|
|
|
|
self.historyLines.append(''.join(self.lineBuffer))
|
|
|
|
self.historyPosition = len(self.historyLines)
|
|
|
|
return recvline.RecvLine.handle_RETURN(self)
|
|
|
|
|
2010-05-08 20:38:09 +00:00
|
|
|
def handle_CTRL_C(self):
|
|
|
|
self.cmdstack[-1].ctrl_c()
|
|
|
|
|
|
|
|
def handle_CTRL_U(self):
|
|
|
|
for i in range(self.lineBufferIndex):
|
|
|
|
self.terminal.cursorBackward()
|
|
|
|
self.terminal.deleteCharacter()
|
|
|
|
self.lineBuffer = self.lineBuffer[self.lineBufferIndex:]
|
|
|
|
self.lineBufferIndex = 0
|
|
|
|
|
|
|
|
def handle_CTRL_D(self):
|
|
|
|
self.call_command(self.commands['exit'])
|
|
|
|
|
2011-02-05 18:44:00 +00:00
|
|
|
def handle_TAB(self):
|
|
|
|
self.cmdstack[-1].handle_TAB()
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
class LoggingServerProtocol(insults.ServerProtocol):
|
|
|
|
def connectionMade(self):
|
|
|
|
self.ttylog_file = '%s/tty/%s-%s.log' % \
|
|
|
|
(config().get('honeypot', 'log_path'),
|
|
|
|
time.strftime('%Y%m%d-%H%M%S'),
|
|
|
|
int(random.random() * 10000))
|
|
|
|
print 'Opening TTY log: %s' % self.ttylog_file
|
|
|
|
ttylog.ttylog_open(self.ttylog_file, time.time())
|
|
|
|
self.ttylog_open = True
|
|
|
|
insults.ServerProtocol.connectionMade(self)
|
|
|
|
|
2010-06-28 13:17:46 +00:00
|
|
|
def write(self, bytes, noLog = False):
|
|
|
|
if self.ttylog_open and not noLog:
|
2009-11-22 07:07:58 +00:00
|
|
|
ttylog.ttylog_write(self.ttylog_file, len(bytes),
|
|
|
|
ttylog.DIR_WRITE, time.time(), bytes)
|
|
|
|
insults.ServerProtocol.write(self, bytes)
|
|
|
|
|
|
|
|
def connectionLost(self, reason):
|
|
|
|
if self.ttylog_open:
|
|
|
|
ttylog.ttylog_close(self.ttylog_file, time.time())
|
|
|
|
self.ttylog_open = False
|
|
|
|
insults.ServerProtocol.connectionLost(self, reason)
|
|
|
|
|
|
|
|
class HoneyPotAvatar(avatar.ConchUser):
|
|
|
|
implements(conchinterfaces.ISession)
|
|
|
|
|
|
|
|
def __init__(self, username, env):
|
|
|
|
avatar.ConchUser.__init__(self)
|
|
|
|
self.username = username
|
|
|
|
self.env = env
|
|
|
|
self.channelLookup.update({'session':session.SSHSession})
|
|
|
|
|
2011-02-05 21:53:54 +00:00
|
|
|
userdb = UserDB()
|
|
|
|
self.uid = self.gid = userdb.getUID(self.username)
|
|
|
|
|
|
|
|
if not self.uid:
|
|
|
|
self.home = '/root'
|
|
|
|
else:
|
|
|
|
self.home = '/home/' + username
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
def openShell(self, protocol):
|
|
|
|
serverProtocol = LoggingServerProtocol(HoneyPotProtocol, self, self.env)
|
|
|
|
serverProtocol.makeConnection(protocol)
|
|
|
|
protocol.makeConnection(session.wrapProtocol(serverProtocol))
|
|
|
|
|
|
|
|
def getPty(self, terminal, windowSize, attrs):
|
2010-06-28 13:17:46 +00:00
|
|
|
print 'Terminal size: %s %s' % windowSize[0:2]
|
2009-11-22 07:07:58 +00:00
|
|
|
self.windowSize = windowSize
|
|
|
|
return None
|
|
|
|
|
|
|
|
def execCommand(self, protocol, cmd):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def closed(self):
|
|
|
|
pass
|
|
|
|
|
2009-12-02 21:13:19 +00:00
|
|
|
def eofReceived(self):
|
|
|
|
pass
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
def windowChanged(self, windowSize):
|
|
|
|
self.windowSize = windowSize
|
|
|
|
|
|
|
|
class HoneyPotEnvironment(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.cfg = config()
|
|
|
|
self.commands = {}
|
|
|
|
import kippo.commands
|
2010-06-30 06:28:00 +00:00
|
|
|
for c in kippo.commands.__all__:
|
2009-11-22 07:07:58 +00:00
|
|
|
module = __import__('kippo.commands.%s' % c,
|
|
|
|
globals(), locals(), ['commands'])
|
|
|
|
self.commands.update(module.commands)
|
|
|
|
self.fs = pickle.load(file(
|
2010-06-17 06:11:23 +00:00
|
|
|
self.cfg.get('honeypot', 'filesystem_file'), 'rb'))
|
2009-11-22 07:07:58 +00:00
|
|
|
|
|
|
|
class HoneyPotRealm:
|
|
|
|
implements(portal.IRealm)
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
# I don't know if i'm supposed to keep static stuff here
|
|
|
|
self.env = HoneyPotEnvironment()
|
|
|
|
|
|
|
|
def requestAvatar(self, avatarId, mind, *interfaces):
|
|
|
|
if conchinterfaces.IConchUser in interfaces:
|
|
|
|
return interfaces[0], \
|
|
|
|
HoneyPotAvatar(avatarId, self.env), lambda: None
|
|
|
|
else:
|
|
|
|
raise Exception, "No supported interfaces found."
|
|
|
|
|
2010-06-16 14:51:26 +00:00
|
|
|
class HoneyPotTransport(transport.SSHServerTransport):
|
|
|
|
|
|
|
|
def connectionMade(self):
|
|
|
|
print '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)
|
|
|
|
transport.SSHServerTransport.connectionMade(self)
|
|
|
|
|
2010-07-05 16:55:03 +00:00
|
|
|
def ssh_KEXINIT(self, packet):
|
|
|
|
print 'Remote SSH version: %s' % (self.otherVersionString,)
|
|
|
|
return transport.SSHServerTransport.ssh_KEXINIT(self, packet)
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
# As implemented by Kojoney
|
|
|
|
class HoneyPotSSHFactory(factory.SSHFactory):
|
|
|
|
services = {
|
|
|
|
'ssh-userauth': userauth.SSHUserAuthServer,
|
|
|
|
'ssh-connection': connection.SSHConnection,
|
|
|
|
}
|
|
|
|
|
2011-02-06 09:04:44 +00:00
|
|
|
# Special delivery to the loggers to avoid scope problems
|
|
|
|
def logDispatch(self, sessionid, msg):
|
|
|
|
for dblog in self.dbloggers:
|
|
|
|
dblog.logDispatch(sessionid, msg)
|
|
|
|
|
2010-06-08 17:37:16 +00:00
|
|
|
def __init__(self):
|
|
|
|
cfg = config()
|
2011-02-05 21:53:54 +00:00
|
|
|
|
|
|
|
# convert old pass.db root passwords
|
|
|
|
passdb_file = '%s/pass.db' % (cfg.get('honeypot', 'data_path'),)
|
|
|
|
if os.path.exists(passdb_file):
|
|
|
|
userdb = UserDB()
|
|
|
|
print 'pass.db deprecated - copying passwords over to userdb.txt'
|
|
|
|
if os.path.exists('%s.bak' % (passdb_file,)):
|
|
|
|
print 'ERROR: %s.bak already exists, skipping conversion!' % \
|
|
|
|
(passdb_file,)
|
|
|
|
else:
|
|
|
|
passdb = anydbm.open(passdb_file, 'c')
|
|
|
|
for p in passdb:
|
|
|
|
userdb.adduser('root', 0, p)
|
|
|
|
passdb.close()
|
|
|
|
os.rename(passdb_file, '%s.bak' % (passdb_file,))
|
|
|
|
print 'pass.db backed up to %s.bak' % (passdb_file,)
|
|
|
|
|
|
|
|
# load db loggers
|
2011-02-06 09:04:44 +00:00
|
|
|
self.dbloggers = []
|
2011-01-17 12:15:24 +00:00
|
|
|
for x in cfg.sections():
|
|
|
|
if not x.startswith('database_'):
|
|
|
|
continue
|
|
|
|
engine = x.split('_')[1]
|
|
|
|
dbengine = 'database_' + engine
|
|
|
|
lcfg = ConfigParser.ConfigParser()
|
|
|
|
lcfg.add_section(dbengine)
|
|
|
|
for i in cfg.options(x):
|
|
|
|
lcfg.set(dbengine, i, cfg.get(x,i))
|
2010-10-23 12:32:49 +00:00
|
|
|
print 'Loading dblog engine: %s' % (engine,)
|
|
|
|
dblogger = __import__(
|
2010-06-08 17:37:16 +00:00
|
|
|
'kippo.dblog.%s' % (engine,),
|
2011-01-17 12:15:24 +00:00
|
|
|
globals(), locals(), ['dblog']).DBLogger(lcfg)
|
2010-10-23 12:32:49 +00:00
|
|
|
log.startLoggingWithObserver(dblogger.emit, setStdout=False)
|
2011-02-06 09:04:44 +00:00
|
|
|
self.dbloggers.append(dblogger)
|
2010-06-08 17:37:16 +00:00
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
def buildProtocol(self, addr):
|
|
|
|
# FIXME: try to mimic something real 100%
|
2010-06-16 14:51:26 +00:00
|
|
|
t = HoneyPotTransport()
|
|
|
|
|
2009-11-22 07:07:58 +00:00
|
|
|
t.ourVersionString = 'SSH-2.0-OpenSSH_5.1p1 Debian-5'
|
|
|
|
t.supportedPublicKeys = self.privateKeys.keys()
|
|
|
|
if not self.primes:
|
|
|
|
ske = t.supportedKeyExchanges[:]
|
|
|
|
ske.remove('diffie-hellman-group-exchange-sha1')
|
|
|
|
t.supportedKeyExchanges = ske
|
|
|
|
t.factory = self
|
|
|
|
return t
|
|
|
|
|
|
|
|
class HoneypotPasswordChecker:
|
|
|
|
implements(checkers.ICredentialsChecker)
|
|
|
|
|
2010-06-28 19:01:02 +00:00
|
|
|
credentialInterfaces = (credentials.IUsernamePassword,
|
|
|
|
credentials.IPluggableAuthenticationModules)
|
2009-11-22 07:07:58 +00:00
|
|
|
|
|
|
|
def requestAvatarId(self, credentials):
|
2010-06-28 19:01:02 +00:00
|
|
|
if hasattr(credentials, 'password'):
|
|
|
|
if self.checkUserPass(credentials.username, credentials.password):
|
|
|
|
return defer.succeed(credentials.username)
|
|
|
|
else:
|
|
|
|
return defer.fail(error.UnauthorizedLogin())
|
|
|
|
elif hasattr(credentials, 'pamConversion'):
|
|
|
|
return self.checkPamUser(credentials.username,
|
|
|
|
credentials.pamConversion)
|
|
|
|
return defer.fail(error.UnhandledCredentials())
|
|
|
|
|
|
|
|
def checkPamUser(self, username, pamConversion):
|
|
|
|
r = pamConversion((('Password:', 1),))
|
|
|
|
return r.addCallback(self.cbCheckPamUser, username)
|
|
|
|
|
|
|
|
def cbCheckPamUser(self, responses, username):
|
|
|
|
for response, zero in responses:
|
|
|
|
if self.checkUserPass(username, response):
|
|
|
|
return defer.succeed(username)
|
|
|
|
return defer.fail(error.UnauthorizedLogin())
|
|
|
|
|
|
|
|
def checkUserPass(self, username, password):
|
2011-02-05 21:53:54 +00:00
|
|
|
if UserDB().checklogin(username, password):
|
2010-06-28 19:01:02 +00:00
|
|
|
print 'login attempt [%s/%s] succeeded' % (username, password)
|
2011-02-05 21:53:54 +00:00
|
|
|
return True
|
2009-11-22 07:07:58 +00:00
|
|
|
else:
|
2010-06-28 19:01:02 +00:00
|
|
|
print 'login attempt [%s/%s] failed' % (username, password)
|
2011-02-05 21:53:54 +00:00
|
|
|
return False
|
2009-11-22 07:07:58 +00:00
|
|
|
|
|
|
|
def getRSAKeys():
|
2009-11-26 21:33:26 +00:00
|
|
|
cfg = config()
|
2009-11-27 22:49:32 +00:00
|
|
|
public_key = cfg.get('honeypot', 'public_key')
|
2009-11-27 22:52:30 +00:00
|
|
|
private_key = cfg.get('honeypot', 'private_key')
|
|
|
|
if not (os.path.exists(public_key) and os.path.exists(private_key)):
|
2009-11-22 07:07:58 +00:00
|
|
|
# generate a RSA keypair
|
|
|
|
print "Generating RSA keypair..."
|
|
|
|
from Crypto.PublicKey import RSA
|
2010-06-01 05:36:14 +00:00
|
|
|
from twisted.python import randbytes
|
2009-11-22 07:07:58 +00:00
|
|
|
KEY_LENGTH = 1024
|
2010-06-01 05:36:14 +00:00
|
|
|
rsaKey = RSA.generate(KEY_LENGTH, randbytes.secureRandom)
|
|
|
|
publicKeyString = keys.Key(rsaKey).public().toString('openssh')
|
|
|
|
privateKeyString = keys.Key(rsaKey).toString('openssh')
|
2009-11-22 07:07:58 +00:00
|
|
|
# save keys for next time
|
2009-11-27 22:49:32 +00:00
|
|
|
file(public_key, 'w+b').write(publicKeyString)
|
|
|
|
file(private_key, 'w+b').write(privateKeyString)
|
2009-11-22 07:07:58 +00:00
|
|
|
print "done."
|
|
|
|
else:
|
2009-12-08 05:22:20 +00:00
|
|
|
publicKeyString = file(public_key).read()
|
2009-11-27 22:52:30 +00:00
|
|
|
privateKeyString = file(private_key).read()
|
2009-11-22 07:07:58 +00:00
|
|
|
return publicKeyString, privateKeyString
|
|
|
|
|
|
|
|
# vim: set sw=4 et:
|