Initial support for non-root users. Check your root passwords!

Thanks to Walter de Jong for code
Thanks to Christian Rohling for the push



git-svn-id: https://kippo.googlecode.com/svn/trunk@196 951d7100-d841-11de-b865-b3884708a8e2
This commit is contained in:
desaster 2011-02-05 21:53:54 +00:00
parent 7b607acacb
commit 950ac23907
6 changed files with 166 additions and 31 deletions

1
data/userdb.txt Normal file
View File

@ -0,0 +1 @@
root:0:123456

View File

@ -74,11 +74,9 @@ txtcmds_path = txtcmds
public_key = public.key
private_key = private.key
# Initial root password. Future passwords will be stored in
# {data_path}/pass.db
#
# (default: 123456)
password = 123456
# Initial root password. NO LONGER USED!
# Instead, see {data_path}/userdb.txt
#password = 123456
# IP address to bind to when opening outgoing connections. Used exclusively by
# the wget command.

View File

@ -5,6 +5,7 @@ import os, time, anydbm, datetime
from kippo.core.honeypot import HoneyPotCommand
from twisted.internet import reactor
from kippo.core.config import config
from kippo.core.userdb import UserDB
commands = {}
@ -24,8 +25,9 @@ class command_w(HoneyPotCommand):
self.writeln(' %s up 14 days, 3:53, 1 user, load average: 0.08, 0.02, 0.01' % \
time.strftime('%H:%M:%S'))
self.writeln('USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT')
self.writeln('root pts/0 %s %s 0.00s 0.00s 0.00s w' % \
(self.honeypot.clientIP[:17].ljust(17),
self.writeln('%-8s pts/0 %s %s 0.00s 0.00s 0.00s w' % \
(self.honeypot.user.username,
self.honeypot.clientIP[:17].ljust(17),
time.strftime('%H:%M', time.localtime(self.honeypot.logintime))))
commands['/usr/bin/w'] = command_w
commands['/usr/bin/who'] = command_w
@ -125,7 +127,9 @@ commands['/bin/ps'] = command_ps
class command_id(HoneyPotCommand):
def call(self):
self.writeln('uid=0(root) gid=0(root) groups=0(root)')
u = self.honeypot.user
self.writeln('uid=%d(%s) gid=%d(%s) groups=%d(%s)' % \
(u.uid, u.username, u.gid, u.username, u.gid, u.username))
commands['/usr/bin/id'] = command_id
class command_passwd(HoneyPotCommand):
@ -133,18 +137,23 @@ class command_passwd(HoneyPotCommand):
self.write('Enter new UNIX password: ')
self.honeypot.password_input = True
self.callbacks = [self.ask_again, self.finish]
self.passwd = None
def ask_again(self):
def ask_again(self, line):
self.passwd = line
self.write('Retype new UNIX password: ')
def finish(self):
def finish(self, line):
self.honeypot.password_input = False
data_path = self.honeypot.env.cfg.get('honeypot', 'data_path')
passdb = anydbm.open('%s/pass.db' % (data_path,), 'c')
if len(self.password) and self.password not in passdb:
passdb[self.password] = ''
passdb.close()
if line != self.passwd:
self.writeln('Sorry, passwords do not match')
self.exit()
return
userdb = UserDB()
userdb.adduser(self.honeypot.user.username,
self.honeypot.user.uid, self.passwd)
self.writeln('passwd: password updated successfully')
self.exit()
@ -152,7 +161,7 @@ class command_passwd(HoneyPotCommand):
def lineReceived(self, line):
print 'INPUT (passwd):', line
self.password = line.strip()
self.callbacks.pop(0)()
self.callbacks.pop(0)(line)
commands['/usr/bin/passwd'] = command_passwd
class command_shutdown(HoneyPotCommand):

View File

@ -26,7 +26,7 @@ commands['/bin/cat'] = command_cat
class command_cd(HoneyPotCommand):
def call(self):
if not self.args:
path = '/root'
path = self.honeypot.user.home
else:
path = self.args[0]
try:

View File

@ -14,6 +14,7 @@ from copy import deepcopy, copy
import sys, os, random, pickle, time, stat, shlex, anydbm
from kippo.core import ttylog, fs, utils
from kippo.core.userdb import UserDB
from kippo.core.config import config
import commands
@ -133,10 +134,19 @@ class HoneyPotShell(object):
self.runCommand()
def showPrompt(self):
prompt = '%s:%%(path)s# ' % self.honeypot.hostname
if not self.honeypot.user.uid:
prompt = '%s:%%(path)s# ' % self.honeypot.hostname
else:
prompt = '%s:%%(path)s$ ' % self.honeypot.hostname
path = self.honeypot.cwd
if path == '/root':
homelen = len(self.honeypot.user.home)
if path == self.honeypot.user.home:
path = '~'
elif len(path) > (homelen+1) and \
path[:(homelen+1)] == self.honeypot.user.home + '/':
path = '~' + path[homelen:]
attrs = {'path': path}
self.honeypot.terminal.write(prompt % attrs)
@ -223,7 +233,7 @@ class HoneyPotProtocol(recvline.HistoricRecvLine):
def __init__(self, user, env):
self.user = user
self.env = env
self.cwd = '/root'
self.cwd = user.home
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
@ -406,6 +416,14 @@ class HoneyPotAvatar(avatar.ConchUser):
self.env = env
self.channelLookup.update({'session':session.SSHSession})
userdb = UserDB()
self.uid = self.gid = userdb.getUID(self.username)
if not self.uid:
self.home = '/root'
else:
self.home = '/home/' + username
def openShell(self, protocol):
serverProtocol = LoggingServerProtocol(HoneyPotProtocol, self, self.env)
serverProtocol.makeConnection(protocol)
@ -476,6 +494,24 @@ class HoneyPotSSHFactory(factory.SSHFactory):
def __init__(self):
cfg = config()
# 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
for x in cfg.sections():
if not x.startswith('database_'):
continue
@ -532,20 +568,12 @@ class HoneypotPasswordChecker:
return defer.fail(error.UnauthorizedLogin())
def checkUserPass(self, username, password):
cfg = config()
data_path = cfg.get('honeypot', 'data_path')
passdb = anydbm.open('%s/pass.db' % (data_path,), 'c')
success = False
if username == 'root' and password == cfg.get('honeypot', 'password'):
success = True
elif username == 'root' and password in passdb:
success = True
passdb.close()
if success:
if UserDB().checklogin(username, password):
print 'login attempt [%s/%s] succeeded' % (username, password)
return True
else:
print 'login attempt [%s/%s] failed' % (username, password)
return success
return False
def getRSAKeys():
cfg = config()

99
kippo/core/userdb.py Normal file
View File

@ -0,0 +1,99 @@
#
# userdb.py for kippo
# by Walter de Jong <walter@sara.nl>
#
# adopted and further modified by Upi Tamminen <desaster@gmail.com>
#
from kippo.core.config import config
import os
import string
class UserDB:
def __init__(self):
self.userdb = []
self.load()
def load(self):
'''load the user db'''
userdb_file = '%s/userdb.txt' % \
(config().get('honeypot', 'data_path'),)
f = open(userdb_file, 'r')
while True:
line = f.readline()
if not line:
break
line = string.strip(line)
if not line:
continue
(login, uid_str, passwd) = string.split(line, ':')
uid = 0
try:
uid = int(uid_str)
except ValueError:
uid = 1001
self.userdb.append((login, uid, passwd))
f.close()
def save(self):
'''save the user db'''
userdb_file = '%s/userdb.txt' % \
(config().get('honeypot', 'data_path'),)
# Note: this is subject to races between kippo instances, but hey ...
f = open(userdb_file, 'w')
for (login, uid, passwd) in self.userdb:
f.write('%s:%d:%s\n' % (login, uid, passwd))
f.close()
def checklogin(self, thelogin, thepasswd):
'''check entered username/password against database'''
'''note that it allows multiple passwords for a single username'''
for (login, uid, passwd) in self.userdb:
if login == thelogin and passwd == thepasswd:
return True
return False
def user_exists(self, thelogin):
for (login, uid, passwd) in self.userdb:
if login == thelogin:
return True
return False
def user_password_exists(self, thelogin, thepasswd):
for (login, uid, passwd) in self.userdb:
if login == thelogin and passwd == thepasswd:
return True
return False
def getUID(self, loginname):
for (login, uid, passwd) in self.userdb:
if loginname == login:
return uid
return 1001
def allocUID(self):
'''allocate the next UID'''
min_uid = 0
for (login, uid, passwd) in self.userdb:
if uid > min_uid:
min_uid = uid
return min_uid + 1
def adduser(self, login, uid, passwd):
if self.user_password_exists(login, passwd):
return
self.userdb.append((login, uid, passwd))
self.save()
# vim: set sw=4 et: