mirror of https://github.com/cowrie/cowrie.git
move core/ and commands/ to kippo/
git-svn-id: https://kippo.googlecode.com/svn/trunk@50 951d7100-d841-11de-b865-b3884708a8e2
This commit is contained in:
parent
876e116b62
commit
88d377cfc6
|
@ -13,8 +13,8 @@ from twisted.internet import reactor, defer
|
|||
from twisted.application import internet, service
|
||||
from twisted.cred import portal
|
||||
from twisted.conch.ssh import factory, keys
|
||||
from core import honeypot
|
||||
from core.config import config
|
||||
from kippo.core import honeypot
|
||||
from kippo.core.config import config
|
||||
|
||||
factory = honeypot.HoneyPotSSHFactory()
|
||||
factory.portal = portal.Portal(honeypot.HoneyPotRealm())
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
__all__ = [
|
||||
'base',
|
||||
'ls',
|
||||
'ping',
|
||||
'ssh',
|
||||
'tar',
|
||||
'wget',
|
||||
'dice',
|
||||
]
|
|
@ -0,0 +1,293 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
import os, time
|
||||
from kippo.core.honeypot import HoneyPotCommand
|
||||
from kippo.core.fs import *
|
||||
from twisted.internet import reactor
|
||||
import config
|
||||
|
||||
commands = {}
|
||||
|
||||
class command_whoami(HoneyPotCommand):
|
||||
def call(self):
|
||||
self.writeln(self.honeypot.user.username)
|
||||
commands['/usr/bin/whoami'] = command_whoami
|
||||
|
||||
class command_cat(HoneyPotCommand):
|
||||
def call(self):
|
||||
for arg in self.args:
|
||||
path = self.fs.resolve_path(arg, self.honeypot.cwd)
|
||||
if not path or not self.fs.exists(path):
|
||||
self.writeln('bash: cat: %s: No such file or directory' % arg)
|
||||
return
|
||||
f = self.fs.getfile(path)
|
||||
|
||||
realfile = self.fs.realfile(f,
|
||||
'%s/%s' % (config.contents_path, path))
|
||||
if realfile:
|
||||
f = file(realfile, 'rb')
|
||||
self.write(f.read())
|
||||
f.close()
|
||||
commands['/bin/cat'] = command_cat
|
||||
|
||||
class command_cd(HoneyPotCommand):
|
||||
def call(self):
|
||||
if not self.args:
|
||||
path = '/root'
|
||||
else:
|
||||
path = self.args[0]
|
||||
try:
|
||||
newpath = self.fs.resolve_path(path, self.honeypot.cwd)
|
||||
newdir = self.fs.get_path(newpath)
|
||||
except IndexError:
|
||||
newdir = None
|
||||
if newdir is None:
|
||||
self.writeln('bash: cd: %s: No such file or directory' % path)
|
||||
return
|
||||
if not self.fs.is_dir(newpath):
|
||||
self.writeln('-bash: cd: %s: Not a directory' % path)
|
||||
return
|
||||
self.honeypot.cwd = newpath
|
||||
commands['cd'] = command_cd
|
||||
|
||||
class command_rm(HoneyPotCommand):
|
||||
def call(self):
|
||||
for f in self.args:
|
||||
path = self.fs.resolve_path(f, self.honeypot.cwd)
|
||||
try:
|
||||
dir = self.fs.get_path('/'.join(path.split('/')[:-1]))
|
||||
except IndexError:
|
||||
self.writeln(
|
||||
'rm: cannot remove `%s\': No such file or directory' % f)
|
||||
continue
|
||||
basename = path.split('/')[-1]
|
||||
contents = [x for x in dir]
|
||||
for i in dir[:]:
|
||||
if i[A_NAME] == basename:
|
||||
if i[A_TYPE] == T_DIR:
|
||||
self.writeln(
|
||||
'rm: cannot remove `%s\': Is a directory' % \
|
||||
i[A_NAME])
|
||||
else:
|
||||
dir.remove(i)
|
||||
commands['/bin/rm'] = command_rm
|
||||
|
||||
class command_mkdir(HoneyPotCommand):
|
||||
def call(self):
|
||||
for f in self.args:
|
||||
path = self.fs.resolve_path(f, self.honeypot.cwd)
|
||||
if self.fs.exists(path):
|
||||
self.writeln(
|
||||
'mkdir: cannot create directory `%s\': File exists' % f)
|
||||
return
|
||||
ok = self.fs.mkdir(path, 0, 0, 4096, 16877)
|
||||
if not ok:
|
||||
self.writeln(
|
||||
'mkdir: cannot create directory `%s\': ' % f + \
|
||||
'No such file or directory')
|
||||
return
|
||||
commands['/bin/mkdir'] = command_mkdir
|
||||
|
||||
class command_rmdir(HoneyPotCommand):
|
||||
def call(self):
|
||||
for f in self.args:
|
||||
path = self.fs.resolve_path(f, self.honeypot.cwd)
|
||||
if len(self.fs.get_path(path)):
|
||||
self.writeln(
|
||||
'rmdir: failed to remove `%s\': Directory not empty' % f)
|
||||
continue
|
||||
try:
|
||||
dir = self.fs.get_path('/'.join(path.split('/')[:-1]))
|
||||
except IndexError:
|
||||
dir = None
|
||||
if not dir or f not in [x[A_NAME] for x in dir]:
|
||||
self.writeln(
|
||||
'rmdir: failed to remove `%s\': ' % f + \
|
||||
'No such file or directory')
|
||||
continue
|
||||
for i in dir[:]:
|
||||
if i[A_NAME] == f:
|
||||
dir.remove(i)
|
||||
commands['/bin/rmdir'] = command_rmdir
|
||||
|
||||
class command_uptime(HoneyPotCommand):
|
||||
def call(self):
|
||||
self.writeln(
|
||||
' %s up 14 days, 3:53, 0 users, load average: 0.08, 0.02, 0.01' % \
|
||||
time.strftime('%H:%M:%S'))
|
||||
commands['/usr/bin/uptime'] = command_uptime
|
||||
|
||||
class command_w(HoneyPotCommand):
|
||||
def call(self):
|
||||
self.writeln(
|
||||
' %s up 14 days, 3:53, 0 users, load average: 0.08, 0.02, 0.01' % \
|
||||
time.strftime('%H:%M:%S'))
|
||||
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):
|
||||
self.writeln(' '.join(self.args))
|
||||
commands['/bin/echo'] = command_echo
|
||||
|
||||
class command_exit(HoneyPotCommand):
|
||||
def call(self):
|
||||
#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):
|
||||
self.honeypot.terminal.reset()
|
||||
commands['/usr/bin/clear'] = command_clear
|
||||
|
||||
class command_vi(HoneyPotCommand):
|
||||
def call(self):
|
||||
self.writeln('E558: Terminal entry not found in terminfo')
|
||||
commands['/usr/bin/vi'] = command_vi
|
||||
|
||||
class command_hostname(HoneyPotCommand):
|
||||
def call(self):
|
||||
self.writeln(self.honeypot.hostname)
|
||||
commands['/bin/hostname'] = command_hostname
|
||||
|
||||
class command_uname(HoneyPotCommand):
|
||||
def call(self):
|
||||
if len(self.args) and self.args[0].strip() == '-a':
|
||||
self.writeln(
|
||||
'Linux %s 2.6.26-2-686 #1 SMP Wed Nov 4 20:45:37 UTC 2009 i686 GNU/Linux' % \
|
||||
self.honeypot.hostname)
|
||||
else:
|
||||
self.writeln('Linux')
|
||||
commands['/bin/uname'] = command_uname
|
||||
|
||||
class command_ps(HoneyPotCommand):
|
||||
def call(self):
|
||||
if len(self.args) and self.args[0].strip().count('a'):
|
||||
output = (
|
||||
'USER PID %%CPU %%MEM VSZ RSS TTY STAT START TIME COMMAND',
|
||||
'root 1 0.0 0.1 2100 688 ? Ss Nov06 0:07 init [2] ',
|
||||
'root 2 0.0 0.0 0 0 ? S< Nov06 0:00 [kthreadd]',
|
||||
'root 3 0.0 0.0 0 0 ? S< Nov06 0:00 [migration/0]',
|
||||
'root 4 0.0 0.0 0 0 ? S< Nov06 0:00 [ksoftirqd/0]',
|
||||
'root 5 0.0 0.0 0 0 ? S< Nov06 0:00 [watchdog/0]',
|
||||
'root 6 0.0 0.0 0 0 ? S< Nov06 0:17 [events/0]',
|
||||
'root 7 0.0 0.0 0 0 ? S< Nov06 0:00 [khelper]',
|
||||
'root 39 0.0 0.0 0 0 ? S< Nov06 0:00 [kblockd/0]',
|
||||
'root 41 0.0 0.0 0 0 ? S< Nov06 0:00 [kacpid]',
|
||||
'root 42 0.0 0.0 0 0 ? S< Nov06 0:00 [kacpi_notify]',
|
||||
'root 170 0.0 0.0 0 0 ? S< Nov06 0:00 [kseriod]',
|
||||
'root 207 0.0 0.0 0 0 ? S Nov06 0:01 [pdflush]',
|
||||
'root 208 0.0 0.0 0 0 ? S Nov06 0:00 [pdflush]',
|
||||
'root 209 0.0 0.0 0 0 ? S< Nov06 0:00 [kswapd0]',
|
||||
'root 210 0.0 0.0 0 0 ? S< Nov06 0:00 [aio/0]',
|
||||
'root 748 0.0 0.0 0 0 ? S< Nov06 0:00 [ata/0]',
|
||||
'root 749 0.0 0.0 0 0 ? S< Nov06 0:00 [ata_aux]',
|
||||
'root 929 0.0 0.0 0 0 ? S< Nov06 0:00 [scsi_eh_0]',
|
||||
'root 1014 0.0 0.0 0 0 ? D< Nov06 0:03 [kjournald]',
|
||||
'root 1087 0.0 0.1 2288 772 ? S<s Nov06 0:00 udevd --daemon',
|
||||
'root 1553 0.0 0.0 0 0 ? S< Nov06 0:00 [kpsmoused]',
|
||||
'root 2054 0.0 0.2 28428 1508 ? Sl Nov06 0:01 /usr/sbin/rsyslogd -c3',
|
||||
'root 2103 0.0 0.2 2628 1196 tty1 Ss Nov06 0:00 /bin/login -- ',
|
||||
'root 2105 0.0 0.0 1764 504 tty2 Ss+ Nov06 0:00 /sbin/getty 38400 tty2',
|
||||
'root 2107 0.0 0.0 1764 504 tty3 Ss+ Nov06 0:00 /sbin/getty 38400 tty3',
|
||||
'root 2109 0.0 0.0 1764 504 tty4 Ss+ Nov06 0:00 /sbin/getty 38400 tty4',
|
||||
'root 2110 0.0 0.0 1764 504 tty5 Ss+ Nov06 0:00 /sbin/getty 38400 tty5',
|
||||
'root 2112 0.0 0.0 1764 508 tty6 Ss+ Nov06 0:00 /sbin/getty 38400 tty6',
|
||||
'root 2133 0.0 0.1 2180 620 ? S<s Nov06 0:00 dhclient3 -pf /var/run/dhclient.eth0.pid -lf /var/lib/dhcp3/dhclien',
|
||||
'root 4969 0.0 0.1 5416 1024 ? Ss Nov08 0:00 /usr/sbin/sshd',
|
||||
'root 5673 0.0 0.2 2924 1540 pts/0 Ss 04:30 0:00 -bash',
|
||||
'root 5679 0.0 0.1 2432 928 pts/0 R+ 04:32 0:00 ps %s' % ' '.join(self.args),
|
||||
)
|
||||
else:
|
||||
output = (
|
||||
' PID TTY TIME CMD',
|
||||
' 5673 pts/0 00:00:00 bash',
|
||||
' 5677 pts/0 00:00:00 ps %s' % ' '.join(self.args),
|
||||
)
|
||||
for l in output:
|
||||
self.writeln(l)
|
||||
commands['/bin/ps'] = command_ps
|
||||
|
||||
class command_id(HoneyPotCommand):
|
||||
def call(self):
|
||||
self.writeln('uid=0(root) gid=0(root) groups=0(root)')
|
||||
commands['/usr/bin/id'] = command_id
|
||||
|
||||
class command_mount(HoneyPotCommand):
|
||||
def call(self):
|
||||
if self.args and len(self.args[0].strip()):
|
||||
return
|
||||
for i in [
|
||||
'/dev/sda1 on / type ext3 (rw,errors=remount-ro)',
|
||||
'tmpfs on /lib/init/rw type tmpfs (rw,nosuid,mode=0755)',
|
||||
'proc on /proc type proc (rw,noexec,nosuid,nodev)',
|
||||
'sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)',
|
||||
'udev on /dev type tmpfs (rw,mode=0755)',
|
||||
'tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)',
|
||||
'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):
|
||||
self.writeln(self.honeypot.cwd)
|
||||
commands['/bin/pwd'] = command_pwd
|
||||
|
||||
class command_passwd(HoneyPotCommand):
|
||||
def start(self):
|
||||
self.write('Enter new UNIX password: ')
|
||||
self.honeypot.password_input = True
|
||||
self.callbacks = [self.ask_again, self.finish]
|
||||
|
||||
def ask_again(self):
|
||||
self.write('Retype new UNIX password: ')
|
||||
|
||||
def finish(self):
|
||||
self.honeypot.password_input = False
|
||||
self.writeln('Sorry, passwords do not match')
|
||||
self.writeln(
|
||||
'passwd: Authentication information cannot be recovered')
|
||||
self.writeln('passwd: password unchanged')
|
||||
self.exit()
|
||||
|
||||
def lineReceived(self, line):
|
||||
print 'INPUT (passwd):', line
|
||||
self.callbacks.pop(0)()
|
||||
commands['/usr/bin/passwd'] = command_passwd
|
||||
|
||||
class command_reboot(HoneyPotCommand):
|
||||
def start(self):
|
||||
self.nextLine()
|
||||
self.writeln(
|
||||
'Broadcast message from root@%s (pts/0) (%s):' % \
|
||||
(self.honeypot.hostname, time.ctime()))
|
||||
self.nextLine()
|
||||
self.writeln('The system is going down for reboot NOW!')
|
||||
reactor.callLater(3, self.finish)
|
||||
|
||||
def finish(self):
|
||||
self.writeln('Connection to server closed.')
|
||||
self.honeypot.hostname = 'localhost'
|
||||
self.exit()
|
||||
|
||||
commands['/sbin/reboot'] = command_reboot
|
||||
|
||||
class command_nop(HoneyPotCommand):
|
||||
def call(self):
|
||||
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
|
||||
commands['/bin/kill'] = command_nop
|
||||
|
||||
# vim: set sw=4 et:
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
# Random commands when running new executables
|
||||
|
||||
from kippo.core.honeypot import HoneyPotCommand
|
||||
|
||||
commands = {}
|
||||
clist = []
|
||||
|
||||
class command_orly(HoneyPotCommand):
|
||||
def start(self):
|
||||
self.orly()
|
||||
|
||||
def orly(self):
|
||||
self.writeln(' ___ ')
|
||||
self.writeln(' {o,o}')
|
||||
self.writeln(' |)__)')
|
||||
self.writeln(' -"-"-')
|
||||
self.write('O RLY? ')
|
||||
|
||||
def lineReceived(self, data):
|
||||
if data.strip().lower() in ('ya', 'yarly', 'ya rly', 'yes', 'y'):
|
||||
self.writeln(' ___')
|
||||
self.writeln(' {o,o}')
|
||||
self.writeln(' (__(|')
|
||||
self.writeln(' -"-"-')
|
||||
self.writeln('NO WAI!')
|
||||
self.exit()
|
||||
return
|
||||
self.orly()
|
||||
clist.append(command_orly)
|
||||
|
||||
class command_wargames(HoneyPotCommand):
|
||||
def start(self):
|
||||
self.write('Shall we play a game? ')
|
||||
|
||||
def lineReceived(self, data):
|
||||
self.writeln('A strange game. ' + \
|
||||
'The only winning move is not to play. ' + \
|
||||
'How about a nice game of chess?')
|
||||
self.exit()
|
||||
clist.append(command_wargames)
|
||||
|
||||
# vim: set sw=4 et:
|
|
@ -0,0 +1,121 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
from kippo.core.honeypot import HoneyPotCommand
|
||||
from kippo.core.fs import *
|
||||
import stat, time
|
||||
|
||||
commands = {}
|
||||
|
||||
class command_ls(HoneyPotCommand):
|
||||
|
||||
def uid2name(self, uid):
|
||||
if uid == 0:
|
||||
return 'root'
|
||||
return uid
|
||||
|
||||
def gid2name(self, gid):
|
||||
if gid == 0:
|
||||
return 'root'
|
||||
return gid
|
||||
|
||||
def call(self):
|
||||
path = self.honeypot.cwd
|
||||
paths = []
|
||||
if len(self.args):
|
||||
for arg in self.args:
|
||||
if not arg.startswith('-'):
|
||||
paths.append(self.honeypot.fs.resolve_path(arg,
|
||||
self.honeypot.cwd))
|
||||
|
||||
self.show_hidden = False
|
||||
func = self.do_ls_normal
|
||||
for x in self.args:
|
||||
if x.startswith('-') and x.count('l'):
|
||||
func = self.do_ls_l
|
||||
if x.startswith('-') and x.count('a'):
|
||||
self.show_hidden = True
|
||||
|
||||
if not paths:
|
||||
func(path)
|
||||
else:
|
||||
for path in paths:
|
||||
func(path)
|
||||
|
||||
def do_ls_normal(self, path):
|
||||
try:
|
||||
files = self.honeypot.fs.list_files(path)
|
||||
except:
|
||||
self.honeypot.writeln(
|
||||
'ls: cannot access %s: No such file or directory' % path)
|
||||
return
|
||||
if not len(files):
|
||||
return
|
||||
l = [x[A_NAME] for x in files \
|
||||
if self.show_hidden or not x[A_NAME].startswith('.')]
|
||||
if not l:
|
||||
return
|
||||
count = 0
|
||||
maxlen = max([len(x) for x in l])
|
||||
perline = int(self.honeypot.user.windowSize[1] / (maxlen + 1))
|
||||
if self.show_hidden:
|
||||
l.insert(0, '..')
|
||||
l.insert(0, '.')
|
||||
for f in l:
|
||||
if count == perline:
|
||||
count = 0
|
||||
self.nextLine()
|
||||
self.write(f.ljust(maxlen + 1))
|
||||
count += 1
|
||||
self.nextLine()
|
||||
|
||||
def do_ls_l(self, path):
|
||||
try:
|
||||
files = self.honeypot.fs.list_files(path)[:]
|
||||
except:
|
||||
self.honeypot.writeln(
|
||||
'ls: cannot access %s: No such file or directory' % path)
|
||||
return
|
||||
|
||||
largest = 0
|
||||
if len(files):
|
||||
largest = max([x[A_SIZE] for x in files])
|
||||
|
||||
# FIXME: should grab these off the parents instead
|
||||
files.insert(0,
|
||||
['..', T_DIR, 0, 0, 4096, 16877, time.time(), [], None])
|
||||
files.insert(0,
|
||||
['.', T_DIR, 0, 0, 4096, 16877, time.time(), [], None])
|
||||
for file in files:
|
||||
perms = ['-'] * 10
|
||||
|
||||
if file[A_MODE] & stat.S_IRUSR: perms[1] = 'r'
|
||||
if file[A_MODE] & stat.S_IWUSR: perms[2] = 'w'
|
||||
if file[A_MODE] & stat.S_IXUSR: perms[3] = 'x'
|
||||
|
||||
if file[A_MODE] & stat.S_IRGRP: perms[4] = 'r'
|
||||
if file[A_MODE] & stat.S_IWGRP: perms[5] = 'w'
|
||||
if file[A_MODE] & stat.S_IXGRP: perms[6] = 'x'
|
||||
|
||||
if file[A_MODE] & stat.S_IROTH: perms[7] = 'r'
|
||||
if file[A_MODE] & stat.S_IWOTH: perms[8] = 'w'
|
||||
if file[A_MODE] & stat.S_IXOTH: perms[9] = 'x'
|
||||
|
||||
if file[A_TYPE] == T_DIR:
|
||||
perms[0] = 'd'
|
||||
|
||||
perms = ''.join(perms)
|
||||
ctime = time.localtime(file[A_CTIME])
|
||||
|
||||
l = '%s 1 %s %s %s %s %s' % \
|
||||
(perms,
|
||||
self.uid2name(file[A_UID]),
|
||||
self.gid2name(file[A_GID]),
|
||||
str(file[A_SIZE]).rjust(len(str(largest))),
|
||||
time.strftime('%Y-%m-%d %H:%M', ctime),
|
||||
file[A_NAME])
|
||||
|
||||
self.honeypot.writeln(l)
|
||||
commands['/bin/ls'] = command_ls
|
||||
|
||||
# vim: set sw=4 et:
|
|
@ -0,0 +1,52 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
from kippo.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):
|
||||
for l in (
|
||||
'Usage: ping [-LRUbdfnqrvVaA] [-c count] [-i interval] [-w deadline]',
|
||||
' [-p pattern] [-s packetsize] [-t ttl] [-I interface or address]',
|
||||
' [-M mtu discovery hint] [-S sndbuf]',
|
||||
' [ -T timestamp option ] [ -Q tos ] [hop1 ...] destination',
|
||||
):
|
||||
self.writeln(l)
|
||||
self.exit()
|
||||
return
|
||||
|
||||
self.host = self.args[0]
|
||||
if re.match('^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$',
|
||||
self.host):
|
||||
self.ip = self.host
|
||||
else:
|
||||
s = md5.md5(self.host).hexdigest()
|
||||
self.ip = '.'.join([str(int(x, 16)) for x in
|
||||
(s[0:2], s[2:4], s[4:6], s[6:8])])
|
||||
|
||||
self.writeln('PING %s (%s) 56(84) bytes of data.' % \
|
||||
(self.host, self.ip))
|
||||
self.scheduled = reactor.callLater(0.2, self.showreply)
|
||||
self.count = 0
|
||||
|
||||
def showreply(self):
|
||||
ms = 40 + random.random() * 10
|
||||
self.writeln(
|
||||
'64 bytes from %s (%s): icmp_seq=%d ttl=50 time=%.1f ms' % \
|
||||
(self.host, self.ip, self.count + 1, ms))
|
||||
self.count += 1
|
||||
self.scheduled = reactor.callLater(1, self.showreply)
|
||||
|
||||
def ctrl_c(self):
|
||||
self.scheduled.cancel()
|
||||
self.writeln('--- %s ping statistics ---' % self.host)
|
||||
self.writeln('%d packets transmitted, %d received, 0%% packet loss, time 907ms' % \
|
||||
(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
|
|
@ -0,0 +1,63 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
from kippo.core.honeypot import HoneyPotCommand
|
||||
from twisted.internet import reactor
|
||||
import time
|
||||
|
||||
commands = {}
|
||||
|
||||
class command_ssh(HoneyPotCommand):
|
||||
def start(self):
|
||||
if not self.args:
|
||||
for l in (
|
||||
'usage: ssh [-1246AaCfgKkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]',
|
||||
' [-D [bind_address:]port] [-e escape_char] [-F configfile]',
|
||||
' [-i identity_file] [-L [bind_address:]port:host:hostport]',
|
||||
' [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]',
|
||||
' [-R [bind_address:]port:host:hostport] [-S ctl_path]',
|
||||
' [-w local_tun[:remote_tun]] [user@]hostname [command]',
|
||||
):
|
||||
self.writeln(l)
|
||||
self.exit()
|
||||
return
|
||||
self.host = self.args[0].strip()
|
||||
self.writeln('The authenticity of host \'187.42.2.9 (187.42.2.9)\' can\'t be established.')
|
||||
self.writeln('RSA key fingerprint is 9d:30:97:8a:9e:48:0d:de:04:8d:76:3a:7b:4b:30:f8.')
|
||||
self.write('Are you sure you want to continue connecting (yes/no)? ')
|
||||
self.callbacks = [self.yesno, self.wait]
|
||||
|
||||
def yesno(self, line):
|
||||
host = line.strip()
|
||||
self.writeln(
|
||||
'Warning: Permanently added \'%s\' (RSA) to the list of known hosts.' % \
|
||||
host)
|
||||
self.write('%s\'s password: ' % self.host)
|
||||
self.honeypot.password_input = True
|
||||
|
||||
def wait(self, line):
|
||||
reactor.callLater(2, self.finish, line)
|
||||
|
||||
def finish(self, line):
|
||||
self.pause = False
|
||||
user, rest, host = 'root', self.host, 'localhost'
|
||||
if self.host.count('@'):
|
||||
user, rest = self.host.split('@', 1)
|
||||
rest = rest.strip().split('.')
|
||||
if len(rest) and rest[0].isalpha():
|
||||
host = rest[0]
|
||||
self.honeypot.hostname = host
|
||||
self.honeypot.password_input = False
|
||||
self.writeln(
|
||||
'Linux %s 2.6.26-2-686 #1 SMP Wed Nov 4 20:45:37 UTC 2009 i686' % \
|
||||
self.honeypot.hostname)
|
||||
self.writeln('Last login: %s from 192.168.9.4' % \
|
||||
time.ctime(time.time() - 123123))
|
||||
self.exit()
|
||||
|
||||
def lineReceived(self, line):
|
||||
if len(self.callbacks):
|
||||
self.callbacks.pop(0)(line)
|
||||
commands['/usr/bin/ssh'] = command_ssh
|
||||
|
||||
# vim: set sw=4 et:
|
|
@ -0,0 +1,66 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
from kippo.core.honeypot import HoneyPotCommand
|
||||
from kippo.core.fs import *
|
||||
from kippo.commands import dice
|
||||
import time, random, tarfile, os
|
||||
|
||||
commands = {}
|
||||
|
||||
class command_tar(HoneyPotCommand):
|
||||
def call(self):
|
||||
if len(self.args) < 2:
|
||||
self.writeln('tar: You must specify one of the `-Acdtrux\' options')
|
||||
self.writeln('Try `tar --help\' or `tar --usage\' for more information.')
|
||||
return
|
||||
|
||||
filename = self.args[1]
|
||||
|
||||
extract = False
|
||||
if 'x' in self.args[0]:
|
||||
extract = True
|
||||
verbose = False
|
||||
if 'v' in self.args[0]:
|
||||
verbose = True
|
||||
|
||||
path = self.fs.resolve_path(filename, self.honeypot.cwd)
|
||||
if not path or not self.honeypot.fs.exists(path):
|
||||
self.writeln('tar: %s: Cannot open: No such file or directory' % \
|
||||
filename)
|
||||
self.writeln('tar: Error is not recoverable: exiting now')
|
||||
self.writeln('tar: Child returned status 2')
|
||||
self.writeln('tar: Error exit delayed from previous errors')
|
||||
return
|
||||
|
||||
f = self.fs.getfile(path)
|
||||
if not f[A_REALFILE]:
|
||||
self.writeln('tar: this does not look like a tar archive')
|
||||
self.writeln('tar: skipping to next header')
|
||||
self.writeln('tar: error exit delayed from previous errors')
|
||||
return
|
||||
|
||||
try:
|
||||
t = tarfile.open(f[A_REALFILE])
|
||||
except:
|
||||
self.writeln('tar: this does not look like a tar archive')
|
||||
self.writeln('tar: skipping to next header')
|
||||
self.writeln('tar: error exit delayed from previous errors')
|
||||
return
|
||||
|
||||
for f in t:
|
||||
dest = self.fs.resolve_path(f.name.strip('/'), self.honeypot.cwd)
|
||||
if verbose:
|
||||
self.writeln(f.name)
|
||||
if not extract or not len(dest):
|
||||
continue
|
||||
if f.isdir():
|
||||
self.fs.mkdir(dest, 0, 0, 4096, f.mode, f.mtime)
|
||||
elif f.isfile():
|
||||
self.fs.mkfile(dest, 0, 0, f.size, f.mode, f.mtime)
|
||||
self.honeypot.commands[dest] = random.choice(dice.clist)
|
||||
else:
|
||||
print 'tar: skipping [%s]' % f.name
|
||||
commands['/bin/tar'] = command_tar
|
||||
|
||||
# vim: set sw=4 et:
|
|
@ -0,0 +1,194 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
from kippo.core.honeypot import HoneyPotCommand
|
||||
from kippo.core.fs import *
|
||||
from twisted.web import client
|
||||
from twisted.internet import reactor
|
||||
import stat, time, urlparse, random, re
|
||||
|
||||
commands = {}
|
||||
|
||||
def tdiff(seconds):
|
||||
t = seconds
|
||||
days = int(t / (24 * 60 * 60))
|
||||
t -= (days * 24 * 60 * 60)
|
||||
hours = int(t / (60 * 60))
|
||||
t -= (hours * 60 * 60)
|
||||
minutes = int(t / 60)
|
||||
t -= (minutes * 60)
|
||||
|
||||
s = '%ds' % int(t)
|
||||
if minutes >= 1: s = '%dm %s' % (minutes, s)
|
||||
if hours >= 1: s = '%dh %s' % (hours, s)
|
||||
if days >= 1: s = '%dd %s' % (days, s)
|
||||
return s
|
||||
|
||||
def sizeof_fmt(num):
|
||||
for x in ['bytes','K','M','G','T']:
|
||||
if num < 1024.0:
|
||||
return "%d%s" % (num, x)
|
||||
num /= 1024.0
|
||||
|
||||
# Luciano Ramalho @ http://code.activestate.com/recipes/498181/
|
||||
def splitthousands( s, sep=','):
|
||||
if len(s) <= 3: return s
|
||||
return splitthousands(s[:-3], sep) + sep + s[-3:]
|
||||
|
||||
class command_wget(HoneyPotCommand):
|
||||
def start(self):
|
||||
url = None
|
||||
for arg in self.args:
|
||||
if arg.startswith('-'):
|
||||
continue
|
||||
url = arg.strip()
|
||||
break
|
||||
|
||||
if not url:
|
||||
self.writeln('wget: missing URL')
|
||||
self.writeln('Usage: wget [OPTION]... [URL]...')
|
||||
self.nextLine()
|
||||
self.writeln('Try `wget --help\' for more options.')
|
||||
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'
|
||||
|
||||
self.safeoutfile = '%s/%s_%s' % \
|
||||
(self.honeypot.env.cfg.get('honeypot', 'download_path'),
|
||||
time.strftime('%Y%m%d%H%M%S'),
|
||||
re.sub('[^A-Za-z0-9]', '_', url))
|
||||
self.deferred = self.download(url, outfile,
|
||||
file(self.safeoutfile, 'wb'))
|
||||
if self.deferred:
|
||||
self.deferred.addCallback(self.success)
|
||||
self.deferred.addErrback(self.error, url)
|
||||
|
||||
def download(self, url, fakeoutfile, outputfile, *args, **kwargs):
|
||||
scheme, host, port, path = client._parse(url)
|
||||
if scheme == 'https':
|
||||
self.writeln('Sorry, SSL not supported in this release')
|
||||
return None
|
||||
|
||||
self.writeln('--%s-- %s' % (time.strftime('%Y-%m-%d %H:%M:%S'), url))
|
||||
self.writeln('Connecting to %s:%d... connected.' % (host, port))
|
||||
self.write('HTTP request sent, awaiting response... ')
|
||||
|
||||
factory = HTTPProgressDownloader(
|
||||
self, fakeoutfile, url, outputfile, *args, **kwargs)
|
||||
self.connection = reactor.connectTCP(host, port, factory)
|
||||
return factory.deferred
|
||||
|
||||
def ctrl_c(self):
|
||||
self.writeln('^C')
|
||||
self.connection.transport.loseConnection()
|
||||
|
||||
def success(self, data):
|
||||
self.exit()
|
||||
|
||||
def error(self, error, url):
|
||||
if hasattr(error, 'getErrorMessage'): # exceptions
|
||||
error = error.getErrorMessage()
|
||||
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):
|
||||
def __init__(self, wget, fakeoutfile, url, outfile, headers=None):
|
||||
client.HTTPDownloader.__init__(self, url, outfile, headers=headers)
|
||||
self.status = None
|
||||
self.wget = wget
|
||||
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':
|
||||
client.HTTPDownloader.page(self, '')
|
||||
else:
|
||||
client.HTTPDownloader.noPage(self, reason)
|
||||
|
||||
def gotHeaders(self, headers):
|
||||
if self.status == '200':
|
||||
self.wget.writeln('200 OK')
|
||||
if headers.has_key('content-length'):
|
||||
self.totallength = int(headers['content-length'][0])
|
||||
else:
|
||||
self.totallength = 0
|
||||
if headers.has_key('content-type'):
|
||||
self.contenttype = headers['content-type'][0]
|
||||
else:
|
||||
self.contenttype = 'text/whatever'
|
||||
self.currentlength = 0.0
|
||||
|
||||
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()
|
||||
|
||||
return client.HTTPDownloader.gotHeaders(self, headers)
|
||||
|
||||
def pagePart(self, data):
|
||||
if self.status == '200':
|
||||
self.currentlength += len(data)
|
||||
if (time.time() - self.lastupdate) < 0.5:
|
||||
return client.HTTPDownloader.pagePart(self, data)
|
||||
if self.totallength:
|
||||
percent = (self.currentlength/self.totallength)*100
|
||||
spercent = "%i%%" % percent
|
||||
else:
|
||||
spercent = '%dK' % (self.currentlength/1000)
|
||||
percent = 0
|
||||
self.speed = self.currentlength / (time.time() - self.started)
|
||||
eta = (self.totallength - self.currentlength) / self.speed
|
||||
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))
|
||||
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),
|
||||
self.speed / 1000))
|
||||
self.wget.honeypot.terminal.nextLine()
|
||||
self.wget.honeypot.terminal.nextLine()
|
||||
self.wget.writeln(
|
||||
'%s (%d KB/s) - `%s\' saved [%d/%d]' % \
|
||||
(time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
self.speed / 1000,
|
||||
self.fakeoutfile, self.currentlength, self.totallength))
|
||||
outfile = '%s/%s' % (self.wget.honeypot.cwd, self.fakeoutfile)
|
||||
self.wget.fs.mkfile(outfile, 0, 0, self.totallength, 33188)
|
||||
self.wget.fs.update_realfile(
|
||||
self.wget.fs.getfile(outfile),
|
||||
self.wget.safeoutfile)
|
||||
return client.HTTPDownloader.pageEnd(self)
|
||||
|
||||
# vim: set sw=4 et:
|
|
@ -0,0 +1,14 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
import ConfigParser, os
|
||||
|
||||
def config():
|
||||
cfg = ConfigParser.ConfigParser()
|
||||
for f in ('kippo.cfg', '/etc/kippo/kippo.cfg', '/etc/kippo.cfg'):
|
||||
if os.path.exists(f):
|
||||
cfg.read('kippo.cfg')
|
||||
return cfg
|
||||
return None
|
||||
|
||||
# vim: set sw=4 et:
|
|
@ -0,0 +1,126 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
import os, time
|
||||
|
||||
A_NAME, \
|
||||
A_TYPE, \
|
||||
A_UID, \
|
||||
A_GID, \
|
||||
A_SIZE, \
|
||||
A_MODE, \
|
||||
A_CTIME, \
|
||||
A_CONTENTS, \
|
||||
A_TARGET, \
|
||||
A_REALFILE = range(0, 10)
|
||||
T_LINK, \
|
||||
T_DIR, \
|
||||
T_FILE, \
|
||||
T_BLK, \
|
||||
T_CHR, \
|
||||
T_SOCK, \
|
||||
T_FIFO = range(0, 7)
|
||||
|
||||
class HoneyPotFilesystem(object):
|
||||
def __init__(self, fs):
|
||||
self.fs = fs
|
||||
|
||||
def resolve_path(self, path, cwd):
|
||||
pieces = path.rstrip('/').split('/')
|
||||
|
||||
if path[0] == '/':
|
||||
cwd = []
|
||||
else:
|
||||
cwd = [x for x in cwd.split('/') if len(x) and x is not None]
|
||||
|
||||
while 1:
|
||||
if not len(pieces):
|
||||
break
|
||||
piece = pieces.pop(0)
|
||||
if piece == '..':
|
||||
if len(cwd): cwd.pop()
|
||||
continue
|
||||
if piece in ('.', ''):
|
||||
continue
|
||||
cwd.append(piece)
|
||||
|
||||
return '/%s' % '/'.join(cwd)
|
||||
|
||||
def get_path(self, path):
|
||||
p = self.fs
|
||||
for i in path.split('/'):
|
||||
if not i:
|
||||
continue
|
||||
p = [x for x in p[A_CONTENTS] if x[A_NAME] == i][0]
|
||||
return p[A_CONTENTS]
|
||||
|
||||
def list_files(self, path):
|
||||
return self.get_path(path)
|
||||
|
||||
def exists(self, path):
|
||||
f = self.getfile(path)
|
||||
if f is not False:
|
||||
return True
|
||||
|
||||
def update_realfile(self, f, realfile):
|
||||
if not f[A_REALFILE] and os.path.exists(realfile) and \
|
||||
not os.path.islink(realfile) and os.path.isfile(realfile) and \
|
||||
f[A_SIZE] < 25000000:
|
||||
print 'Updating realfile to %s' % realfile
|
||||
f[A_REALFILE] = realfile
|
||||
|
||||
def realfile(self, f, path):
|
||||
self.update_realfile(f, path)
|
||||
if f[A_REALFILE]:
|
||||
return f[A_REALFILE]
|
||||
return None
|
||||
|
||||
def getfile(self, path):
|
||||
pieces = path.strip('/').split('/')
|
||||
p = self.fs
|
||||
while 1:
|
||||
if not len(pieces):
|
||||
break
|
||||
piece = pieces.pop(0)
|
||||
if piece not in [x[A_NAME] for x in p[A_CONTENTS]]:
|
||||
return False
|
||||
p = [x for x in p[A_CONTENTS] \
|
||||
if x[A_NAME] == piece][0]
|
||||
return p
|
||||
|
||||
def mkfile(self, path, uid, gid, size, mode, ctime = None):
|
||||
if ctime is None:
|
||||
ctime = time.time()
|
||||
dir = self.get_path(os.path.dirname(path))
|
||||
outfile = os.path.basename(path)
|
||||
if outfile in [x[A_NAME] for x in dir]:
|
||||
dir.remove([x for x in dir if x[A_NAME] == outfile][0])
|
||||
dir.append([outfile, T_FILE, uid, gid, size, mode, ctime, [],
|
||||
None, None])
|
||||
return True
|
||||
|
||||
def mkdir(self, path, uid, gid, size, mode, ctime = None):
|
||||
if ctime is None:
|
||||
ctime = time.time()
|
||||
if not len(path.strip('/')):
|
||||
return False
|
||||
try:
|
||||
dir = self.get_path(os.path.dirname(path.strip('/')))
|
||||
except IndexError:
|
||||
return False
|
||||
dir.append([os.path.basename(path), T_DIR, uid, gid, size, mode,
|
||||
ctime, [], None, None])
|
||||
return True
|
||||
|
||||
def is_dir(self, path):
|
||||
if path == '/':
|
||||
return True
|
||||
dir = self.get_path(os.path.dirname(path))
|
||||
l = [x for x in dir
|
||||
if x[A_NAME] == os.path.basename(path) and
|
||||
x[A_TYPE] == T_DIR]
|
||||
if l:
|
||||
return True
|
||||
return False
|
||||
|
||||
# vim: set sw=4 et:
|
|
@ -0,0 +1,303 @@
|
|||
# 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
|
||||
import sys, os, random, pickle, time, stat, shlex
|
||||
|
||||
from kippo.core import ttylog, fs
|
||||
from kippo.core.config import config
|
||||
import commands
|
||||
|
||||
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
|
||||
|
||||
class HoneyPotShell(object):
|
||||
def __init__(self, honeypot):
|
||||
self.honeypot = honeypot
|
||||
self.showPrompt()
|
||||
|
||||
def lineReceived(self, line):
|
||||
print 'CMD: %s' % line
|
||||
if not len(line.strip()):
|
||||
self.showPrompt()
|
||||
return
|
||||
try:
|
||||
cmdAndArgs = shlex.split(line.strip())
|
||||
except:
|
||||
self.honeypot.writeln(
|
||||
'-bash: syntax error: unexpected end of file')
|
||||
self.showPrompt()
|
||||
return
|
||||
cmd, args = cmdAndArgs[0], []
|
||||
if len(cmdAndArgs) > 1:
|
||||
args = cmdAndArgs[1:]
|
||||
cmdclass = self.honeypot.getCommand(cmd)
|
||||
if cmdclass:
|
||||
obj = cmdclass(self.honeypot, *args)
|
||||
self.honeypot.cmdstack.append(obj)
|
||||
self.honeypot.setTypeoverMode()
|
||||
obj.start()
|
||||
else:
|
||||
if len(line.strip()):
|
||||
self.honeypot.writeln('bash: %s: command not found' % cmd)
|
||||
self.showPrompt()
|
||||
|
||||
def resume(self):
|
||||
self.honeypot.setInsertMode()
|
||||
self.showPrompt()
|
||||
|
||||
def showPrompt(self):
|
||||
prompt = '%s:%%(path)s# ' % self.honeypot.hostname
|
||||
path = self.honeypot.cwd
|
||||
if path == '/root':
|
||||
path = '~'
|
||||
attrs = {'path': path}
|
||||
self.honeypot.terminal.write(prompt % attrs)
|
||||
|
||||
def ctrl_c(self):
|
||||
self.honeypot.terminal.nextLine()
|
||||
self.showPrompt()
|
||||
|
||||
class HoneyPotProtocol(recvline.HistoricRecvLine):
|
||||
def __init__(self, user, env):
|
||||
self.user = user
|
||||
self.env = env
|
||||
self.cwd = '/root'
|
||||
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 = []
|
||||
|
||||
def connectionMade(self):
|
||||
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
|
||||
del self.commands
|
||||
|
||||
# Overriding to prevent terminal.reset()
|
||||
def initializeScreen(self):
|
||||
self.setInsertMode()
|
||||
|
||||
def getCommand(self, cmd):
|
||||
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:
|
||||
for i in ['%s/%s' % (x, cmd) for x in \
|
||||
'/bin', '/usr/bin', '/sbin', '/usr/sbin']:
|
||||
if self.fs.exists(i):
|
||||
path = i
|
||||
break
|
||||
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)
|
||||
if keyID == '\x03':
|
||||
self.cmdstack[-1].ctrl_c()
|
||||
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()
|
||||
|
||||
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)
|
||||
|
||||
def write(self, bytes):
|
||||
if self.ttylog_open:
|
||||
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})
|
||||
|
||||
def openShell(self, protocol):
|
||||
serverProtocol = LoggingServerProtocol(HoneyPotProtocol, self, self.env)
|
||||
serverProtocol.makeConnection(protocol)
|
||||
protocol.makeConnection(session.wrapProtocol(serverProtocol))
|
||||
|
||||
def getPty(self, terminal, windowSize, attrs):
|
||||
self.windowSize = windowSize
|
||||
return None
|
||||
|
||||
def execCommand(self, protocol, cmd):
|
||||
raise NotImplementedError
|
||||
|
||||
def closed(self):
|
||||
pass
|
||||
|
||||
def windowChanged(self, windowSize):
|
||||
self.windowSize = windowSize
|
||||
|
||||
class HoneyPotEnvironment(object):
|
||||
def __init__(self):
|
||||
self.cfg = config()
|
||||
self.commands = {}
|
||||
import kippo.commands
|
||||
for c in kippo.commands.__all__:
|
||||
module = __import__('kippo.commands.%s' % c,
|
||||
globals(), locals(), ['commands'])
|
||||
self.commands.update(module.commands)
|
||||
self.fs = pickle.load(file(
|
||||
self.cfg.get('honeypot', 'filesystem_file')))
|
||||
|
||||
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."
|
||||
|
||||
# As implemented by Kojoney
|
||||
class HoneyPotSSHFactory(factory.SSHFactory):
|
||||
#publicKeys = {'ssh-rsa': keys.getPublicKeyString(data=publicKey)}
|
||||
#privateKeys = {'ssh-rsa': keys.getPrivateKeyObject(data=privateKey)}
|
||||
services = {
|
||||
'ssh-userauth': userauth.SSHUserAuthServer,
|
||||
'ssh-connection': connection.SSHConnection,
|
||||
}
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
# FIXME: try to mimic something real 100%
|
||||
t = transport.SSHServerTransport()
|
||||
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)
|
||||
|
||||
credentialInterfaces = (credentials.IUsernamePassword,)
|
||||
|
||||
def __init__(self, users):
|
||||
self.users = users
|
||||
|
||||
def requestAvatarId(self, credentials):
|
||||
if (credentials.username, credentials.password) in self.users:
|
||||
print 'login attempt [%s/%s] succeeded' % \
|
||||
(credentials.username, credentials.password)
|
||||
return defer.succeed(credentials.username)
|
||||
else:
|
||||
print 'login attempt [%s/%s] failed' % \
|
||||
(credentials.username, credentials.password)
|
||||
return defer.fail(error.UnauthorizedLogin())
|
||||
|
||||
def getRSAKeys():
|
||||
if not (os.path.exists('public.key') and os.path.exists('private.key')):
|
||||
# generate a RSA keypair
|
||||
print "Generating RSA keypair..."
|
||||
from Crypto.PublicKey import RSA
|
||||
KEY_LENGTH = 1024
|
||||
rsaKey = RSA.generate(KEY_LENGTH, common.entropy.get_bytes)
|
||||
publicKeyString = keys.makePublicKeyString(rsaKey)
|
||||
privateKeyString = keys.makePrivateKeyString(rsaKey)
|
||||
# save keys for next time
|
||||
file('public.key', 'w+b').write(publicKeyString)
|
||||
file('private.key', 'w+b').write(privateKeyString)
|
||||
print "done."
|
||||
else:
|
||||
publicKeyString = file('public.key').read()
|
||||
privateKeyString = file('private.key').read()
|
||||
return publicKeyString, privateKeyString
|
||||
|
||||
# vim: set sw=4 et:
|
|
@ -0,0 +1,30 @@
|
|||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
# Should be compatible with user mode linux
|
||||
|
||||
import struct, sys
|
||||
|
||||
OP_OPEN, OP_CLOSE, OP_WRITE, OP_EXEC = 1, 2, 3, 4
|
||||
DIR_READ, DIR_WRITE = 1, 2
|
||||
|
||||
def ttylog_write(logfile, len, direction, stamp, data = None):
|
||||
f = file(logfile, 'a')
|
||||
sec, usec = int(stamp), int(1000000 * (stamp - int(stamp)))
|
||||
f.write(struct.pack('iLiiLL', 3, 0, len, direction, sec, usec))
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
def ttylog_open(logfile, stamp):
|
||||
f = file(logfile, 'a')
|
||||
sec, usec = int(stamp), int(1000000 * (stamp - int(stamp)))
|
||||
f.write(struct.pack('iLiiLL', 1, 0, 0, 0, sec, usec))
|
||||
f.close()
|
||||
|
||||
def ttylog_close(logfile, stamp):
|
||||
f = file(logfile, 'a')
|
||||
sec, usec = int(stamp), int(1000000 * (stamp - int(stamp)))
|
||||
f.write(struct.pack('iLiiLL', 2, 0, 0, 0, sec, usec))
|
||||
f.close()
|
||||
|
||||
# vim: set sw=4 et:
|
Loading…
Reference in New Issue