From 92a2276c148af3dbcc66793612872a76ba53f77e Mon Sep 17 00:00:00 2001 From: desaster Date: Thu, 19 Nov 2009 14:56:36 +0000 Subject: [PATCH] commands are now loaded and assigned to their fake binaries in a way that is more straightforward when adding new commands. also wget improvements + something else git-svn-id: https://kippo.googlecode.com/svn/trunk@31 951d7100-d841-11de-b865-b3884708a8e2 --- commands/__init__.py | 8 ++++++++ commands/base.py | 34 ++++++++++++++++++++++++++++-- commands/ls.py | 3 +++ commands/ping.py | 3 +++ commands/ssh.py | 3 +++ commands/tar.py | 3 +++ commands/wget.py | 49 +++++++++++++++++++++++++------------------- config.py | 1 + core/cmdl.py | 39 ----------------------------------- core/honeypot.py | 20 ++++++++++++------ 10 files changed, 95 insertions(+), 68 deletions(-) delete mode 100644 core/cmdl.py diff --git a/commands/__init__.py b/commands/__init__.py index e69de29b..794c72ee 100644 --- a/commands/__init__.py +++ b/commands/__init__.py @@ -0,0 +1,8 @@ +__all__ = [ + 'base', + 'ls', + 'ping', + 'ssh', + 'tar', + 'wget', + ] diff --git a/commands/base.py b/commands/base.py index 4c9e4a38..9403a3b2 100644 --- a/commands/base.py +++ b/commands/base.py @@ -1,10 +1,14 @@ import os, time from core.honeypot import HoneyPotCommand from core.fstypes import * +import config + +commands = {} class command_whoami(HoneyPotCommand): def call(self, args): self.writeln(self.honeypot.user.username) +commands['/usr/bin/whoami'] = command_whoami class command_cat(HoneyPotCommand): def call(self, args): @@ -14,12 +18,13 @@ class command_cat(HoneyPotCommand): self.writeln('bash: cat: %s: No such file or directory' % args) return - fakefile = './honeyfs/%s' % path + fakefile = '%s/%s' % (config.contents_path, path) if os.path.exists(fakefile) and \ not os.path.islink(fakefile) and os.path.isfile(fakefile): f = file(fakefile, 'r') self.write(f.read()) f.close() +commands['/bin/cat'] = command_cat class command_cd(HoneyPotCommand): def call(self, args): @@ -36,6 +41,7 @@ class command_cd(HoneyPotCommand): self.writeln('bash: cd: %s: No such file or directory' % args) return self.honeypot.cwd = newpath +commands['cd'] = command_cd class command_rm(HoneyPotCommand): def call(self, args): @@ -57,6 +63,7 @@ class command_rm(HoneyPotCommand): i[A_NAME]) else: dir.remove(i) +commands['/bin/rm'] = command_rm class command_mkdir(HoneyPotCommand): def call(self, args): @@ -74,6 +81,7 @@ class command_mkdir(HoneyPotCommand): 'mkdir: cannot create directory `test\': File exists') continue dir.append([f, T_DIR, 0, 0, 4096, 16877, time.time(), [], None]) +commands['/bin/mkdir'] = command_mkdir class command_rmdir(HoneyPotCommand): def call(self, args): @@ -91,13 +99,14 @@ class command_rmdir(HoneyPotCommand): for i in dir[:]: if i[A_NAME] == f: dir.remove(i) +commands['/bin/rmdir'] = command_rmdir class command_uptime(HoneyPotCommand): def call(self, args): self.writeln( ' %s up 14 days, 3:53, 0 users, load average: 0.08, 0.02, 0.01' % \ time.strftime('%T')) - #self.writeln('USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT') +commands['/usr/bin/uptime'] = command_uptime class command_w(HoneyPotCommand): def call(self, args): @@ -105,28 +114,36 @@ class command_w(HoneyPotCommand): ' %s up 14 days, 3:53, 0 users, load average: 0.08, 0.02, 0.01' % \ time.strftime('%T')) self.writeln('USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT') +commands['/usr/bin/w'] = command_w +commands['/usr/bin/who'] = command_w class command_echo(HoneyPotCommand): def call(self, args): self.writeln(args) +commands['/bin/echo'] = command_echo class command_exit(HoneyPotCommand): def call(self, args): + #self.honeypot.terminal.loseConnection() self.honeypot.terminal.reset() self.writeln('Connection to server closed.') self.honeypot.hostname = 'localhost' +commands['exit'] = command_exit class command_clear(HoneyPotCommand): def call(self, args): self.honeypot.terminal.reset() +commands['/usr/bin/clear'] = command_clear class command_vi(HoneyPotCommand): def call(self, args): self.writeln('E558: Terminal entry not found in terminfo') +commands['/usr/bin/vi'] = command_vi class command_hostname(HoneyPotCommand): def call(self, args): self.writeln(self.honeypot.hostname) +commands['/bin/hostname'] = command_hostname class command_uname(HoneyPotCommand): def call(self, args): @@ -136,6 +153,7 @@ class command_uname(HoneyPotCommand): self.honeypot.hostname) else: self.writeln('Linux') +commands['/bin/uname'] = command_uname class command_ps(HoneyPotCommand): def call(self, args): @@ -183,10 +201,12 @@ class command_ps(HoneyPotCommand): ) for l in output: self.writeln(l) +commands['/bin/ps'] = command_ps class command_id(HoneyPotCommand): def call(self, args): self.writeln('uid=0(root) gid=0(root) groups=0(root)') +commands['/usr/bin/id'] = command_id class command_mount(HoneyPotCommand): def call(self, args): @@ -202,10 +222,12 @@ class command_mount(HoneyPotCommand): 'devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=620)', ]: self.writeln(i) +commands['/usr/mount'] = command_mount class command_pwd(HoneyPotCommand): def call(self, args): self.writeln(self.honeypot.cwd) +commands['/bin/pwd'] = command_pwd class command_passwd(HoneyPotCommand): def start(self): @@ -227,9 +249,17 @@ class command_passwd(HoneyPotCommand): def lineReceived(self, line): print 'INPUT (passwd):', line self.callbacks.pop(0)() +commands['/usr/bin/passwd'] = command_passwd class command_nop(HoneyPotCommand): def call(self, args): pass +commands['/bin/chmod'] = command_nop +commands['set'] = command_nop +commands['unset'] = command_nop +commands['history'] = command_nop +commands['export'] = command_nop +commands['/bin/bash'] = command_nop +commands['/bin/sh'] = command_nop # vim: set sw=4 et: diff --git a/commands/ls.py b/commands/ls.py index 341fe09a..661c3d11 100644 --- a/commands/ls.py +++ b/commands/ls.py @@ -2,6 +2,8 @@ from core.honeypot import HoneyPotCommand from core.fstypes import * import stat, time +commands = {} + class command_ls(HoneyPotCommand): def uid2name(self, uid): @@ -70,5 +72,6 @@ class command_ls(HoneyPotCommand): file[A_NAME]) self.honeypot.writeln(l) +commands['/bin/ls'] = command_ls # vim: set sw=4 et: diff --git a/commands/ping.py b/commands/ping.py index 4bbbb17a..6a0b5144 100644 --- a/commands/ping.py +++ b/commands/ping.py @@ -2,6 +2,8 @@ from core.honeypot import HoneyPotCommand from twisted.internet import reactor import time, re, random, md5 +commands = {} + class command_ping(HoneyPotCommand): def start(self): if not len(self.args.strip()): @@ -44,3 +46,4 @@ class command_ping(HoneyPotCommand): (self.count, self.count)) self.writeln('rtt min/avg/max/mdev = 48.264/50.352/52.441/2.100 ms') self.exit() +commands['/bin/ping'] = command_ping diff --git a/commands/ssh.py b/commands/ssh.py index 02b2959b..18176ff0 100644 --- a/commands/ssh.py +++ b/commands/ssh.py @@ -2,6 +2,8 @@ from core.honeypot import HoneyPotCommand from twisted.internet import reactor import time +commands = {} + class command_ssh(HoneyPotCommand): def start(self): if not len(self.args.strip()): @@ -53,5 +55,6 @@ class command_ssh(HoneyPotCommand): def lineReceived(self, line): if len(self.callbacks): self.callbacks.pop(0)(line) +commands['/usr/bin/ssh'] = command_ssh # vim: set sw=4 et: diff --git a/commands/tar.py b/commands/tar.py index 1df47920..fe1d750a 100644 --- a/commands/tar.py +++ b/commands/tar.py @@ -1,6 +1,8 @@ from core.honeypot import HoneyPotCommand import time, random +commands = {} + class command_tar(HoneyPotCommand): def call(self, args): @@ -30,5 +32,6 @@ class command_tar(HoneyPotCommand): size = 1000000 + int(random.random() * 4000000) self.fs.mkfile('%s/%s' % (self.honeypot.cwd, f), 0, 0, size, 33188) self.writeln('./%s' % f) +commands['/bin/tar'] = command_tar # vim: set sw=4 et: diff --git a/commands/wget.py b/commands/wget.py index 8c9efaa4..0a22ee1f 100644 --- a/commands/wget.py +++ b/commands/wget.py @@ -5,6 +5,8 @@ from twisted.internet import reactor import stat, time, urlparse, random, re import config +commands = {} + def tdiff(seconds): t = seconds days = int(t / (24 * 60 * 60)) @@ -40,9 +42,6 @@ class command_wget(HoneyPotCommand): url = arg.strip() break - if not url.startswith('http://'): - url = 'http://%s' % url - if not url: self.writeln('wget: missing URL') self.writeln('Usage: wget [OPTION]... [URL]...') @@ -51,13 +50,15 @@ class command_wget(HoneyPotCommand): self.exit() return + if url and not url.startswith('http://'): + url = 'http://%s' % url + urldata = urlparse.urlparse(url) outfile = urldata.path.split('/')[-1] if not len(outfile.strip()) or not urldata.path.count('/'): outfile = 'index.html' - # now just dl the file in background... fn = '%s_%s' % \ (time.strftime('%Y%m%d%H%M%S'), re.sub('[^A-Za-z0-9]', '_', url)) @@ -75,29 +76,29 @@ class command_wget(HoneyPotCommand): self.writeln('--%s-- %s' % (time.strftime('%Y-%m-%d %T'), url)) self.writeln('Connecting to %s:%d... connected.' % (host, port)) - self.write('HTTP request sent, awaiting response...') + self.write('HTTP request sent, awaiting response... ') factory = HTTPProgressDownloader( self, fakeoutfile, url, outputfile, *args, **kwargs) - reactor.connectTCP(host, port, factory) + self.connection = reactor.connectTCP(host, port, factory) return factory.deferred - # Dunno how to stop the transfer so let's just make it impossible def ctrl_c(self): self.writeln('^C') + self.connection.transport.loseConnection() def saveurl(self, data, fn): - print 'File download finished (%s)' % fn self.exit() def error(self, error, url): if hasattr(error, 'getErrorMessage'): # exceptions error = error.getErrorMessage() - print 'wget error', error - self.writeln('404 Not Found') - self.writeln('%s ERROR 404: Not Found.' % \ - time.strftime('%Y-%m-%d %T')) + self.writeln(error) + # Real wget also adds this: + #self.writeln('%s ERROR 404: Not Found.' % \ + # time.strftime('%Y-%m-%d %T')) self.exit() +commands['/usr/bin/wget'] = command_wget # from http://code.activestate.com/recipes/525493/ class HTTPProgressDownloader(client.HTTPDownloader): @@ -108,6 +109,7 @@ class HTTPProgressDownloader(client.HTTPDownloader): self.fakeoutfile = fakeoutfile self.lastupdate = 0 self.started = time.time() + self.proglen = 0 def noPage(self, reason): # called for non-200 responses if self.status == '304': @@ -128,10 +130,14 @@ class HTTPProgressDownloader(client.HTTPDownloader): self.contenttype = 'text/whatever' self.currentlength = 0.0 - self.wget.writeln('Length: %d (%s) [%s]' % \ - (self.totallength, - sizeof_fmt(self.totallength), - self.contenttype)) + if self.totallength > 0: + self.wget.writeln('Length: %d (%s) [%s]' % \ + (self.totallength, + sizeof_fmt(self.totallength), + self.contenttype)) + else: + self.wget.writeln('Length: unspecified [%s]' % \ + (self.contenttype)) self.wget.writeln('Saving to: `%s' % self.fakeoutfile) self.wget.honeypot.terminal.nextLine() @@ -150,19 +156,20 @@ class HTTPProgressDownloader(client.HTTPDownloader): percent = 0 self.speed = self.currentlength / (time.time() - self.started) eta = (self.totallength - self.currentlength) / self.speed - # FIXME: output looks bugged (insertmode thing) - self.wget.write( - '\r%s [%s] %s %dK/s eta %s' % \ + s = '\r%s [%s] %s %dK/s eta %s' % \ (spercent.rjust(3), ('%s>' % (int(39.0 / 100.0 * percent) * '=')).ljust(39), splitthousands(str(int(self.currentlength))).ljust(12), self.speed / 1000, - tdiff(eta))) + tdiff(eta)) + self.wget.write(s.ljust(self.proglen)) + self.proglen = len(s) self.lastupdate = time.time() - return client.HTTPDownloader.pagePart(self, data) def pageEnd(self): + if self.totallength != 0 and self.currentlength != self.totallength: + return client.HTTPDownloader.pageEnd(self) self.wget.write('\r100%%[%s] %s %dK/s' % \ ('%s>' % (38 * '='), splitthousands(str(int(self.totallength))).ljust(12), diff --git a/config.py b/config.py index f5120d42..42cec1d6 100644 --- a/config.py +++ b/config.py @@ -4,3 +4,4 @@ ssh_port = 2222 fake_hostname = 'sales' log_path = 'log' download_path = 'dl' +contents_path = 'honeyfs' diff --git a/core/cmdl.py b/core/cmdl.py deleted file mode 100644 index 3f14634f..00000000 --- a/core/cmdl.py +++ /dev/null @@ -1,39 +0,0 @@ -from commands import base, ls, wget, tar, ssh, ping - -cmdl = { - '/bin/echo': base.command_echo, - 'cd': base.command_cd, - '/bin/cat': base.command_cat, - '/usr/bin/whoami': base.command_whoami, - 'exit': base.command_exit, - '/usr/bin/clear': base.command_clear, - '/bin/rm': base.command_rm, - '/bin/chmod': base.command_nop, - '/bin/mount': base.command_mount, - '/bin/pwd': base.command_pwd, - '/bin/uname': base.command_uname, - '/bin/ps': base.command_ps, - '/bin/hostname': base.command_hostname, - '/bin/mkdir': base.command_mkdir, - '/bin/rmdir': base.command_rmdir, - '/usr/bin/uptime': base.command_uptime, - '/usr/bin/w': base.command_w, - '/usr/bin/who': base.command_w, - '/usr/bin/vi': base.command_vi, - '/usr/bin/vim': base.command_vi, - '/usr/bin/id': base.command_id, - '/usr/bin/passwd': base.command_passwd, - '/usr/bin/ssh': ssh.command_ssh, - '/bin/ping': ping.command_ping, - 'set': base.command_nop, - 'unset': base.command_nop, - 'history': base.command_nop, - 'export': base.command_nop, - '/bin/bash': base.command_nop, - '/bin/sh': base.command_nop, - '/bin/ls': ls.command_ls, - '/usr/bin/wget': wget.command_wget, - '/bin/tar': tar.command_tar, - } - -# vim: set sw=4 et: diff --git a/core/honeypot.py b/core/honeypot.py index e9f8d699..7e1f9494 100644 --- a/core/honeypot.py +++ b/core/honeypot.py @@ -61,6 +61,7 @@ class HoneyPotShell(object): if cmdclass: obj = cmdclass(self.honeypot, args) self.honeypot.cmdstack.append(obj) + self.honeypot.setTypeoverMode() obj.start() else: if len(line.strip()): @@ -68,6 +69,7 @@ class HoneyPotShell(object): self.showPrompt() def resume(self): + self.honeypot.setInsertMode() self.showPrompt() def showPrompt(self): @@ -96,6 +98,11 @@ class HoneyPotProtocol(recvline.HistoricRecvLine): recvline.HistoricRecvLine.connectionMade(self) self.cmdstack = [HoneyPotShell(self)] + def connectionLost(self, reason): + recvline.HistoricRecvLine.connectionLost(self, reason) + # not sure why i need to do this: + del self.fs + # Overriding to prevent terminal.reset() def initializeScreen(self): self.setInsertMode() @@ -166,6 +173,7 @@ class LoggingServerProtocol(insults.ServerProtocol): 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) @@ -196,13 +204,13 @@ class HoneyPotAvatar(avatar.ConchUser): class HoneyPotEnvironment(object): def __init__(self): - from core.cmdl import cmdl - self.commands = cmdl - - print 'Loading filesystem...', - sys.stdout.flush() + self.commands = {} + import commands + for c in commands.__all__: + module = __import__('commands.%s' % c, + globals(), locals(), ['commands']) + self.commands.update(module.commands) self.fs = pickle.load(file('fs.pickle')) - print 'done' class HoneyPotFilesystem(object): def __init__(self, fs):