mirror of
https://github.com/cowrie/cowrie.git
synced 2025-07-01 18:07:27 -04:00
fix pylint bad-whitespace warnings
This commit is contained in:
@ -72,7 +72,7 @@ class UserDB(object):
|
|||||||
f.write('%s:%d:%s\n' % (login, uid, passwd))
|
f.write('%s:%d:%s\n' % (login, uid, passwd))
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def checklogin(self, thelogin, thepasswd, src_ip = '0.0.0.0'):
|
def checklogin(self, thelogin, thepasswd, src_ip='0.0.0.0'):
|
||||||
"""
|
"""
|
||||||
check entered username/password against database
|
check entered username/password against database
|
||||||
note that it allows multiple passwords for a single username
|
note that it allows multiple passwords for a single username
|
||||||
|
@ -84,7 +84,7 @@ class DBLogger(object):
|
|||||||
sessionno = ev['sessionno']
|
sessionno = ev['sessionno']
|
||||||
self.sessions[sessionno] = \
|
self.sessions[sessionno] = \
|
||||||
self.createSession(
|
self.createSession(
|
||||||
ev['src_ip'], ev['src_port'], ev['dst_ip'], ev['dst_port'] )
|
ev['src_ip'], ev['src_port'], ev['dst_ip'], ev['dst_port'])
|
||||||
return
|
return
|
||||||
|
|
||||||
# use explicit sessionno if coming from dispatch
|
# use explicit sessionno if coming from dispatch
|
||||||
@ -103,7 +103,7 @@ class DBLogger(object):
|
|||||||
|
|
||||||
if 'eventid' in ev:
|
if 'eventid' in ev:
|
||||||
if ev['eventid'] in self.events:
|
if ev['eventid'] in self.events:
|
||||||
self.events[ev['eventid']]( self.sessions[sessionno], ev )
|
self.events[ev['eventid']](self.sessions[sessionno], ev)
|
||||||
return
|
return
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
@ -116,7 +116,7 @@ class HoneyPotFilesystem(object):
|
|||||||
if not f[A_REALFILE] and os.path.exists(realfile) and \
|
if not f[A_REALFILE] and os.path.exists(realfile) and \
|
||||||
not os.path.islink(realfile) and os.path.isfile(realfile) and \
|
not os.path.islink(realfile) and os.path.isfile(realfile) and \
|
||||||
f[A_SIZE] < 25000000:
|
f[A_SIZE] < 25000000:
|
||||||
#log.msg( 'Updating realfile to %s' % realfile )
|
#log.msg('Updating realfile to %s' % realfile)
|
||||||
f[A_REALFILE] = realfile
|
f[A_REALFILE] = realfile
|
||||||
|
|
||||||
def realfile(self, f, path):
|
def realfile(self, f, path):
|
||||||
@ -140,11 +140,11 @@ class HoneyPotFilesystem(object):
|
|||||||
if x[A_NAME] == piece][0]
|
if x[A_NAME] == piece][0]
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def file_contents(self, target, count = 0):
|
def file_contents(self, target, count=0):
|
||||||
if count > 10:
|
if count > 10:
|
||||||
raise TooManyLevels
|
raise TooManyLevels
|
||||||
path = self.resolve_path(target, os.path.dirname(target))
|
path = self.resolve_path(target, os.path.dirname(target))
|
||||||
#log.msg( '%s resolved into %s' % (target, path) )
|
#log.msg('%s resolved into %s' % (target, path))
|
||||||
if not path or not self.exists(path):
|
if not path or not self.exists(path):
|
||||||
raise FileNotFound
|
raise FileNotFound
|
||||||
f = self.getfile(path)
|
f = self.getfile(path)
|
||||||
@ -156,7 +156,7 @@ class HoneyPotFilesystem(object):
|
|||||||
if realfile:
|
if realfile:
|
||||||
return file(realfile, 'rb').read()
|
return file(realfile, 'rb').read()
|
||||||
|
|
||||||
def mkfile(self, path, uid, gid, size, mode, ctime = None):
|
def mkfile(self, path, uid, gid, size, mode, ctime=None):
|
||||||
if self.newcount > 10000:
|
if self.newcount > 10000:
|
||||||
return False
|
return False
|
||||||
if ctime is None:
|
if ctime is None:
|
||||||
@ -170,7 +170,7 @@ class HoneyPotFilesystem(object):
|
|||||||
self.newcount += 1
|
self.newcount += 1
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def mkdir(self, path, uid, gid, size, mode, ctime = None):
|
def mkdir(self, path, uid, gid, size, mode, ctime=None):
|
||||||
if self.newcount > 10000:
|
if self.newcount > 10000:
|
||||||
return False
|
return False
|
||||||
if ctime is None:
|
if ctime is None:
|
||||||
@ -205,31 +205,31 @@ class HoneyPotFilesystem(object):
|
|||||||
# additions for SFTP support, try to keep functions here similar to os.*
|
# additions for SFTP support, try to keep functions here similar to os.*
|
||||||
|
|
||||||
def open(self, filename, openFlags, mode):
|
def open(self, filename, openFlags, mode):
|
||||||
#log.msg( "fs.open %s" % filename )
|
#log.msg("fs.open %s" % filename)
|
||||||
|
|
||||||
#if (openFlags & os.O_APPEND == os.O_APPEND):
|
#if (openFlags & os.O_APPEND == os.O_APPEND):
|
||||||
# log.msg( "fs.open append" )
|
# log.msg("fs.open append")
|
||||||
|
|
||||||
#if (openFlags & os.O_CREAT == os.O_CREAT):
|
#if (openFlags & os.O_CREAT == os.O_CREAT):
|
||||||
# log.msg( "fs.open creat" )
|
# log.msg("fs.open creat")
|
||||||
|
|
||||||
#if (openFlags & os.O_TRUNC == os.O_TRUNC):
|
#if (openFlags & os.O_TRUNC == os.O_TRUNC):
|
||||||
# log.msg( "fs.open trunc" )
|
# log.msg("fs.open trunc")
|
||||||
|
|
||||||
#if (openFlags & os.O_EXCL == os.O_EXCL):
|
#if (openFlags & os.O_EXCL == os.O_EXCL):
|
||||||
# log.msg( "fs.open excl" )
|
# log.msg("fs.open excl")
|
||||||
|
|
||||||
# treat O_RDWR same as O_WRONLY
|
# treat O_RDWR same as O_WRONLY
|
||||||
if openFlags & os.O_WRONLY == os.O_WRONLY or openFlags & os.O_RDWR == os.O_RDWR:
|
if openFlags & os.O_WRONLY == os.O_WRONLY or openFlags & os.O_RDWR == os.O_RDWR:
|
||||||
# ensure we do not save with executable bit set
|
# ensure we do not save with executable bit set
|
||||||
realmode = mode & ~(stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
realmode = mode & ~(stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
||||||
|
|
||||||
#log.msg( "fs.open wronly" )
|
#log.msg("fs.open wronly")
|
||||||
tempfile = '%s/%s_%s' % \
|
tempfile = '%s/%s_%s' % \
|
||||||
(config().get('honeypot', 'download_path'),
|
(config().get('honeypot', 'download_path'),
|
||||||
time.strftime('%Y%m%d%H%M%S'),
|
time.strftime('%Y%m%d%H%M%S'),
|
||||||
re.sub('[^A-Za-z0-9]', '_', filename))
|
re.sub('[^A-Za-z0-9]', '_', filename))
|
||||||
#log.msg( "fs.open file for writing, saving to %s" % safeoutfile )
|
#log.msg("fs.open file for writing, saving to %s" % safeoutfile)
|
||||||
|
|
||||||
self.mkfile(filename, 0, 0, 0, stat.S_IFREG | mode)
|
self.mkfile(filename, 0, 0, 0, stat.S_IFREG | mode)
|
||||||
fd = os.open(tempfile, openFlags, realmode)
|
fd = os.open(tempfile, openFlags, realmode)
|
||||||
@ -262,7 +262,7 @@ class HoneyPotFilesystem(object):
|
|||||||
else:
|
else:
|
||||||
os.rename(self.tempfiles[fd], shasumfile)
|
os.rename(self.tempfiles[fd], shasumfile)
|
||||||
os.symlink(shasum, self.tempfiles[fd])
|
os.symlink(shasum, self.tempfiles[fd])
|
||||||
self.update_realfile( self.getfile(self.filenames[fd]), shasumfile )
|
self.update_realfile(self.getfile(self.filenames[fd]), shasumfile)
|
||||||
del self.tempfiles[fd]
|
del self.tempfiles[fd]
|
||||||
del self.filenames[fd]
|
del self.filenames[fd]
|
||||||
return os.close(fd)
|
return os.close(fd)
|
||||||
@ -322,7 +322,7 @@ class HoneyPotFilesystem(object):
|
|||||||
raise notImplementedError
|
raise notImplementedError
|
||||||
|
|
||||||
def rename(self, oldpath, newpath):
|
def rename(self, oldpath, newpath):
|
||||||
#log.msg( "rename %s to %s" % (oldpath, newpath) )
|
#log.msg("rename %s to %s" % (oldpath, newpath))
|
||||||
old = self.getfile(oldpath)
|
old = self.getfile(oldpath)
|
||||||
if old == False:
|
if old == False:
|
||||||
raise OSError(errno.ENOENT, os.strerror(errno.ENOENT))
|
raise OSError(errno.ENOENT, os.strerror(errno.ENOENT))
|
||||||
@ -342,7 +342,7 @@ class HoneyPotFilesystem(object):
|
|||||||
def lstat(self, path):
|
def lstat(self, path):
|
||||||
# need to treat / as exception
|
# need to treat / as exception
|
||||||
if (path == "/"):
|
if (path == "/"):
|
||||||
p = { A_TYPE:T_DIR, A_UID:0, A_GID:0, A_SIZE:4096, A_MODE:16877, A_CTIME:time.time() }
|
p = {A_TYPE:T_DIR, A_UID:0, A_GID:0, A_SIZE:4096, A_MODE:16877, A_CTIME:time.time()}
|
||||||
else:
|
else:
|
||||||
p = self.getfile(path)
|
p = self.getfile(path)
|
||||||
|
|
||||||
@ -363,7 +363,7 @@ class HoneyPotFilesystem(object):
|
|||||||
|
|
||||||
def stat(self, path):
|
def stat(self, path):
|
||||||
if (path == "/"):
|
if (path == "/"):
|
||||||
p = { A_TYPE:T_DIR, A_UID:0, A_GID:0, A_SIZE:4096, A_MODE:16877, A_CTIME:time.time() }
|
p = {A_TYPE:T_DIR, A_UID:0, A_GID:0, A_SIZE:4096, A_MODE:16877, A_CTIME:time.time()}
|
||||||
else:
|
else:
|
||||||
p = self.getfile(path)
|
p = self.getfile(path)
|
||||||
|
|
||||||
|
@ -36,12 +36,12 @@ class HoneyPotCommand(object):
|
|||||||
self.honeypot.cmdstack[-1].resume()
|
self.honeypot.cmdstack[-1].resume()
|
||||||
|
|
||||||
def ctrl_c(self):
|
def ctrl_c(self):
|
||||||
log.msg( 'Received CTRL-C, exiting..' )
|
log.msg('Received CTRL-C, exiting..')
|
||||||
self.writeln('^C')
|
self.writeln('^C')
|
||||||
self.exit()
|
self.exit()
|
||||||
|
|
||||||
def lineReceived(self, line):
|
def lineReceived(self, line):
|
||||||
log.msg( 'INPUT: %s' % line )
|
log.msg('INPUT: %s' % line)
|
||||||
|
|
||||||
def resume(self):
|
def resume(self):
|
||||||
pass
|
pass
|
||||||
@ -50,7 +50,7 @@ class HoneyPotCommand(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class HoneyPotShell(object):
|
class HoneyPotShell(object):
|
||||||
def __init__(self, protocol, interactive = True):
|
def __init__(self, protocol, interactive=True):
|
||||||
self.honeypot = protocol
|
self.honeypot = protocol
|
||||||
self.interactive = interactive
|
self.interactive = interactive
|
||||||
self.showPrompt()
|
self.showPrompt()
|
||||||
@ -60,10 +60,10 @@ class HoneyPotShell(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def lineReceived(self, line):
|
def lineReceived(self, line):
|
||||||
log.msg( 'CMD: %s' % line )
|
log.msg('CMD: %s' % line)
|
||||||
line = line[:500]
|
line = line[:500]
|
||||||
comment = re.compile( '^\s*#' )
|
comment = re.compile('^\s*#')
|
||||||
for i in [x.strip() for x in re.split(';|&&|\n',line.strip())[:10]]:
|
for i in [x.strip() for x in re.split(';|&&|\n', line.strip())[:10]]:
|
||||||
if not len(i):
|
if not len(i):
|
||||||
continue
|
continue
|
||||||
if comment.match(i):
|
if comment.match(i):
|
||||||
@ -129,12 +129,12 @@ class HoneyPotShell(object):
|
|||||||
rargs.append(arg)
|
rargs.append(arg)
|
||||||
cmdclass = self.honeypot.getCommand(cmd, envvars['PATH'].split(':'))
|
cmdclass = self.honeypot.getCommand(cmd, envvars['PATH'].split(':'))
|
||||||
if cmdclass:
|
if cmdclass:
|
||||||
log.msg( eventid='KIPP0005', input=line, format='Command found: %(input)s' )
|
log.msg(eventid='KIPP0005', input=line, format='Command found: %(input)s')
|
||||||
#self.honeypot.logDispatch('Command found: %s' % (line,))
|
#self.honeypot.logDispatch('Command found: %s' % (line,))
|
||||||
self.honeypot.call_command(cmdclass, *rargs)
|
self.honeypot.call_command(cmdclass, *rargs)
|
||||||
else:
|
else:
|
||||||
log.msg( eventid='KIPP0006',
|
log.msg(eventid='KIPP0006',
|
||||||
input=line, format='Command not found: %(input)s' )
|
input=line, format='Command not found: %(input)s')
|
||||||
#self.honeypot.logDispatch('Command not found: %s' % (line,))
|
#self.honeypot.logDispatch('Command not found: %s' % (line,))
|
||||||
if len(line):
|
if len(line):
|
||||||
self.honeypot.writeln('bash: %s: command not found' % cmd)
|
self.honeypot.writeln('bash: %s: command not found' % cmd)
|
||||||
|
@ -120,7 +120,7 @@ class Interact(telnet.Telnet):
|
|||||||
session.realClientIP.ljust(15),
|
session.realClientIP.ljust(15),
|
||||||
session.clientVersion))
|
session.clientVersion))
|
||||||
|
|
||||||
def cmd_help(self, args = ''):
|
def cmd_help(self, args=''):
|
||||||
self.transport.write('List of commands:\r\n')
|
self.transport.write('List of commands:\r\n')
|
||||||
self.transport.write(' list - list all active sessions\r\n')
|
self.transport.write(' list - list all active sessions\r\n')
|
||||||
self.transport.write(
|
self.transport.write(
|
||||||
@ -146,7 +146,7 @@ class Interact(telnet.Telnet):
|
|||||||
return
|
return
|
||||||
self.transport.write('** No such session found.\r\n')
|
self.transport.write('** No such session found.\r\n')
|
||||||
|
|
||||||
def cmd_exit(self, args = ''):
|
def cmd_exit(self, args=''):
|
||||||
self.transport.loseConnection()
|
self.transport.loseConnection()
|
||||||
|
|
||||||
def makeInteractFactory(honeypotFactory):
|
def makeInteractFactory(honeypotFactory):
|
||||||
|
@ -32,8 +32,8 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol):
|
|||||||
|
|
||||||
def logDispatch(self, *msg, **args):
|
def logDispatch(self, *msg, **args):
|
||||||
transport = self.terminal.transport.session.conn.transport
|
transport = self.terminal.transport.session.conn.transport
|
||||||
args['sessionno']=transport.transport.sessionno
|
args['sessionno'] = transport.transport.sessionno
|
||||||
transport.factory.logDispatch(*msg,**args)
|
transport.factory.logDispatch(*msg, **args)
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
transport = self.terminal.transport.session.conn.transport
|
transport = self.terminal.transport.session.conn.transport
|
||||||
@ -71,7 +71,7 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol):
|
|||||||
def txtcmd(self, txt):
|
def txtcmd(self, txt):
|
||||||
class command_txtcmd(honeypot.HoneyPotCommand):
|
class command_txtcmd(honeypot.HoneyPotCommand):
|
||||||
def call(self):
|
def call(self):
|
||||||
log.msg( 'Reading txtcmd from "%s"' % txt )
|
log.msg('Reading txtcmd from "%s"' % txt)
|
||||||
f = file(txt, 'r')
|
f = file(txt, 'r')
|
||||||
self.write(f.read())
|
self.write(f.read())
|
||||||
f.close()
|
f.close()
|
||||||
@ -122,7 +122,7 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol):
|
|||||||
transport = self.terminal.transport.session.conn.transport
|
transport = self.terminal.transport.session.conn.transport
|
||||||
transport.interactors.remove(interactor)
|
transport.interactors.remove(interactor)
|
||||||
|
|
||||||
def uptime(self, reset = None):
|
def uptime(self, reset=None):
|
||||||
transport = self.terminal.transport.session.conn.transport
|
transport = self.terminal.transport.session.conn.transport
|
||||||
r = time.time() - transport.factory.starttime
|
r = time.time() - transport.factory.starttime
|
||||||
if reset:
|
if reset:
|
||||||
@ -247,10 +247,10 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
|||||||
transport = self.transport.session.conn.transport
|
transport = self.transport.session.conn.transport
|
||||||
transport.ttylog_file = '%s/tty/%s-%s.log' % \
|
transport.ttylog_file = '%s/tty/%s-%s.log' % \
|
||||||
(config().get('honeypot', 'log_path'),
|
(config().get('honeypot', 'log_path'),
|
||||||
time.strftime('%Y%m%d-%H%M%S'), transport.transportId )
|
time.strftime('%Y%m%d-%H%M%S'), transport.transportId)
|
||||||
|
|
||||||
self.ttylog_file = transport.ttylog_file
|
self.ttylog_file = transport.ttylog_file
|
||||||
log.msg( eventid='KIPP0004', ttylog=transport.ttylog_file,
|
log.msg(eventid='KIPP0004', ttylog=transport.ttylog_file,
|
||||||
format='Opening TTY Log: %(ttylog)s')
|
format='Opening TTY Log: %(ttylog)s')
|
||||||
|
|
||||||
ttylog.ttylog_open(transport.ttylog_file, time.time())
|
ttylog.ttylog_open(transport.ttylog_file, time.time())
|
||||||
@ -258,12 +258,12 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
|||||||
|
|
||||||
self.stdinlog_file = '%s/%s-%s-stdin.log' % \
|
self.stdinlog_file = '%s/%s-%s-stdin.log' % \
|
||||||
(config().get('honeypot', 'download_path'),
|
(config().get('honeypot', 'download_path'),
|
||||||
time.strftime('%Y%m%d-%H%M%S'), transport.transportId )
|
time.strftime('%Y%m%d-%H%M%S'), transport.transportId)
|
||||||
self.stdinlog_open = False
|
self.stdinlog_open = False
|
||||||
|
|
||||||
insults.ServerProtocol.connectionMade(self)
|
insults.ServerProtocol.connectionMade(self)
|
||||||
|
|
||||||
def write(self, bytes, noLog = False):
|
def write(self, bytes, noLog=False):
|
||||||
transport = self.transport.session.conn.transport
|
transport = self.transport.session.conn.transport
|
||||||
for i in transport.interactors:
|
for i in transport.interactors:
|
||||||
i.sessionWrite(bytes)
|
i.sessionWrite(bytes)
|
||||||
@ -273,14 +273,14 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
|||||||
|
|
||||||
insults.ServerProtocol.write(self, bytes)
|
insults.ServerProtocol.write(self, bytes)
|
||||||
|
|
||||||
def dataReceived(self, data, noLog = False):
|
def dataReceived(self, data, noLog=False):
|
||||||
transport = self.transport.session.conn.transport
|
transport = self.transport.session.conn.transport
|
||||||
if self.ttylog_open and not noLog:
|
if self.ttylog_open and not noLog:
|
||||||
ttylog.ttylog_write(transport.ttylog_file, len(data),
|
ttylog.ttylog_write(transport.ttylog_file, len(data),
|
||||||
ttylog.TYPE_INPUT, time.time(), data)
|
ttylog.TYPE_INPUT, time.time(), data)
|
||||||
if self.stdinlog_open and not noLog:
|
if self.stdinlog_open and not noLog:
|
||||||
log.msg( "Saving stdin log: %s" % self.stdinlog_file )
|
log.msg("Saving stdin log: %s" % self.stdinlog_file)
|
||||||
f = file( self.stdinlog_file, 'ab' )
|
f = file(self.stdinlog_file, 'ab')
|
||||||
f.write(data)
|
f.write(data)
|
||||||
f.close
|
f.close
|
||||||
|
|
||||||
@ -293,10 +293,10 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
|||||||
# FIXME: this method is called 4 times on logout....
|
# FIXME: this method is called 4 times on logout....
|
||||||
# it's called once from Avatar.closed() if disconnected
|
# it's called once from Avatar.closed() if disconnected
|
||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
# log.msg( "received call to LSP.connectionLost" )
|
# log.msg("received call to LSP.connectionLost")
|
||||||
transport = self.transport.session.conn.transport
|
transport = self.transport.session.conn.transport
|
||||||
if self.ttylog_open:
|
if self.ttylog_open:
|
||||||
log.msg( eventid='KIPP0012', format='Closing TTY Log: %(ttylog)s',
|
log.msg(eventid='KIPP0012', format='Closing TTY Log: %(ttylog)s',
|
||||||
ttylog=transport.ttylog_file)
|
ttylog=transport.ttylog_file)
|
||||||
ttylog.ttylog_close(transport.ttylog_file, time.time())
|
ttylog.ttylog_close(transport.ttylog_file, time.time())
|
||||||
self.ttylog_open = False
|
self.ttylog_open = False
|
||||||
|
@ -44,7 +44,7 @@ class HoneyPotSSHUserAuthServer(userauth.SSHUserAuthServer):
|
|||||||
try:
|
try:
|
||||||
honeyfs = cfg.get('honeypot', 'contents_path')
|
honeyfs = cfg.get('honeypot', 'contents_path')
|
||||||
issuefile = honeyfs + "/etc/issue.net"
|
issuefile = honeyfs + "/etc/issue.net"
|
||||||
data = file( issuefile ).read()
|
data = file(issuefile).read()
|
||||||
except IOError:
|
except IOError:
|
||||||
return
|
return
|
||||||
if not data or not len(data.strip()):
|
if not data or not len(data.strip()):
|
||||||
@ -113,7 +113,7 @@ class HoneyPotSSHFactory(factory.SSHFactory):
|
|||||||
lcfg.add_section('honeypot')
|
lcfg.add_section('honeypot')
|
||||||
for i in cfg.options('honeypot'):
|
for i in cfg.options('honeypot'):
|
||||||
lcfg.set('honeypot', i, cfg.get('honeypot', i))
|
lcfg.set('honeypot', i, cfg.get('honeypot', i))
|
||||||
log.msg( 'Loading dblog engine: %s' % (engine,) )
|
log.msg('Loading dblog engine: %s' % (engine,))
|
||||||
dblogger = __import__(
|
dblogger = __import__(
|
||||||
'kippo.dblog.%s' % (engine,),
|
'kippo.dblog.%s' % (engine,),
|
||||||
globals(), locals(), ['dblog']).DBLogger(lcfg)
|
globals(), locals(), ['dblog']).DBLogger(lcfg)
|
||||||
@ -134,7 +134,7 @@ class HoneyPotSSHFactory(factory.SSHFactory):
|
|||||||
lcfg.add_section('honeypot')
|
lcfg.add_section('honeypot')
|
||||||
for i in cfg.options('honeypot'):
|
for i in cfg.options('honeypot'):
|
||||||
lcfg.set('honeypot', i, cfg.get('honeypot', i))
|
lcfg.set('honeypot', i, cfg.get('honeypot', i))
|
||||||
log.msg( 'Loading output engine: %s' % (engine,) )
|
log.msg('Loading output engine: %s' % (engine,))
|
||||||
output = __import__(
|
output = __import__(
|
||||||
'kippo.output.%s' % (engine,),
|
'kippo.output.%s' % (engine,),
|
||||||
globals(), locals(), ['output']).Output(lcfg)
|
globals(), locals(), ['output']).Output(lcfg)
|
||||||
@ -159,27 +159,27 @@ class HoneyPotSSHFactory(factory.SSHFactory):
|
|||||||
t = HoneyPotTransport()
|
t = HoneyPotTransport()
|
||||||
|
|
||||||
if cfg.has_option('honeypot', 'ssh_version_string'):
|
if cfg.has_option('honeypot', 'ssh_version_string'):
|
||||||
t.ourVersionString = cfg.get('honeypot','ssh_version_string')
|
t.ourVersionString = cfg.get('honeypot', 'ssh_version_string')
|
||||||
else:
|
else:
|
||||||
t.ourVersionString = "SSH-2.0-OpenSSH_5.1p1 Debian-5"
|
t.ourVersionString = "SSH-2.0-OpenSSH_5.1p1 Debian-5"
|
||||||
|
|
||||||
t.supportedPublicKeys = self.privateKeys.keys()
|
t.supportedPublicKeys = self.privateKeys.keys()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.primes = primes.parseModuliFile( _moduli )
|
self.primes = primes.parseModuliFile(moduli)
|
||||||
except IOError as err:
|
except IOError as err:
|
||||||
log.err( err )
|
log.err(err)
|
||||||
|
|
||||||
if not self.primes:
|
if not self.primes:
|
||||||
log.msg( "Disabling diffie-hellman-group-exchange-sha1" )
|
log.msg("Disabling diffie-hellman-group-exchange-sha1")
|
||||||
ske = t.supportedKeyExchanges[:]
|
ske = t.supportedKeyExchanges[:]
|
||||||
ske.remove('diffie-hellman-group-exchange-sha1')
|
ske.remove('diffie-hellman-group-exchange-sha1')
|
||||||
t.supportedKeyExchanges = ske
|
t.supportedKeyExchanges = ske
|
||||||
|
|
||||||
# reorder supported ciphers to resemble current openssh more
|
# reorder supported ciphers to resemble current openssh more
|
||||||
t.supportedCiphers = ['aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', '3des-cbc', 'blowfish-cbc', 'cast128-cbc', 'aes192-cbc', 'aes256-cbc' ]
|
t.supportedCiphers = ['aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', '3des-cbc', 'blowfish-cbc', 'cast128-cbc', 'aes192-cbc', 'aes256-cbc']
|
||||||
t.supportedPublicKeys = ['ssh-rsa', 'ssh-dss']
|
t.supportedPublicKeys = ['ssh-rsa', 'ssh-dss']
|
||||||
t.supportedMACs = [ 'hmac-md5', 'hmac-sha1']
|
t.supportedMACs = ['hmac-md5', 'hmac-sha1']
|
||||||
|
|
||||||
t.factory = self
|
t.factory = self
|
||||||
return t
|
return t
|
||||||
@ -206,11 +206,11 @@ class HoneyPotTransport(sshserver.KippoSSHServerTransport):
|
|||||||
self.transportId = uuid.uuid4().hex[:8]
|
self.transportId = uuid.uuid4().hex[:8]
|
||||||
self.interactors = []
|
self.interactors = []
|
||||||
|
|
||||||
log.msg( eventid='KIPP0001',
|
log.msg(eventid='KIPP0001',
|
||||||
format='New connection: %(src_ip)s:%(src_port)s (%(dst_ip)s:%(dst_port)s) [session: %(sessionno)s]',
|
format='New connection: %(src_ip)s:%(src_port)s (%(dst_ip)s:%(dst_port)s) [session: %(sessionno)s]',
|
||||||
src_ip=self.transport.getPeer().host, src_port=self.transport.getPeer().port,
|
src_ip=self.transport.getPeer().host, src_port=self.transport.getPeer().port,
|
||||||
dst_ip=self.transport.getHost().host, dst_port=self.transport.getHost().port,
|
dst_ip=self.transport.getHost().host, dst_port=self.transport.getHost().port,
|
||||||
sessionno=self.transport.sessionno )
|
sessionno=self.transport.sessionno)
|
||||||
|
|
||||||
sshserver.KippoSSHServerTransport.connectionMade(self)
|
sshserver.KippoSSHServerTransport.connectionMade(self)
|
||||||
|
|
||||||
@ -232,16 +232,16 @@ class HoneyPotTransport(sshserver.KippoSSHServerTransport):
|
|||||||
k = getNS(packet[16:], 10)
|
k = getNS(packet[16:], 10)
|
||||||
strings, rest = k[:-1], k[-1]
|
strings, rest = k[:-1], k[-1]
|
||||||
(kexAlgs, keyAlgs, encCS, encSC, macCS, macSC, compCS, compSC, langCS, langSC) = [s.split(',') for s in strings]
|
(kexAlgs, keyAlgs, encCS, encSC, macCS, macSC, compCS, compSC, langCS, langSC) = [s.split(',') for s in strings]
|
||||||
log.msg('KEXINIT: client supported key exchange: %s' % kexAlgs )
|
log.msg('KEXINIT: client supported key exchange: %s' % kexAlgs)
|
||||||
log.msg('KEXINIT: client supported public keys: %s' % keyAlgs )
|
log.msg('KEXINIT: client supported public keys: %s' % keyAlgs)
|
||||||
log.msg('KEXINIT: client supported encryption: %s' % encCS )
|
log.msg('KEXINIT: client supported encryption: %s' % encCS)
|
||||||
log.msg('KEXINIT: client supported MAC: %s' % macCS )
|
log.msg('KEXINIT: client supported MAC: %s' % macCS)
|
||||||
log.msg('KEXINIT: client supported compression: %s' % compCS )
|
log.msg('KEXINIT: client supported compression: %s' % compCS)
|
||||||
log.msg('KEXINIT: client supported lang: %s' % langCS )
|
log.msg('KEXINIT: client supported lang: %s' % langCS)
|
||||||
|
|
||||||
log.msg( eventid='KIPP0009', version=self.otherVersionString,
|
log.msg(eventid='KIPP0009', version=self.otherVersionString,
|
||||||
kexAlgs=kexAlgs, keyAlgs=keyAlgs, encCS=encCS, macCS=macCS,
|
kexAlgs=kexAlgs, keyAlgs=keyAlgs, encCS=encCS, macCS=macCS,
|
||||||
compCS=compCS, format='Remote SSH version: %(version)s' )
|
compCS=compCS, format='Remote SSH version: %(version)s')
|
||||||
|
|
||||||
return sshserver.KippoSSHServerTransport.ssh_KEXINIT(self, packet)
|
return sshserver.KippoSSHServerTransport.ssh_KEXINIT(self, packet)
|
||||||
|
|
||||||
@ -252,7 +252,7 @@ class HoneyPotTransport(sshserver.KippoSSHServerTransport):
|
|||||||
if self.transport.sessionno in self.factory.sessions:
|
if self.transport.sessionno in self.factory.sessions:
|
||||||
del self.factory.sessions[self.transport.sessionno]
|
del self.factory.sessions[self.transport.sessionno]
|
||||||
sshserver.KippoSSHServerTransport.connectionLost(self, reason)
|
sshserver.KippoSSHServerTransport.connectionLost(self, reason)
|
||||||
log.msg( eventid='KIPP0011', format='Connection lost')
|
log.msg(eventid='KIPP0011', format='Connection lost')
|
||||||
|
|
||||||
class HoneyPotSSHSession(session.SSHSession):
|
class HoneyPotSSHSession(session.SSHSession):
|
||||||
|
|
||||||
@ -265,15 +265,15 @@ class HoneyPotSSHSession(session.SSHSession):
|
|||||||
value, rest = getNS(rest)
|
value, rest = getNS(rest)
|
||||||
if rest:
|
if rest:
|
||||||
raise ValueError("Bad data given in env request")
|
raise ValueError("Bad data given in env request")
|
||||||
log.msg( eventid='KIPP0013', format='request_env: %(name)s=%(value)s', name=name, value=value )
|
log.msg(eventid='KIPP0013', format='request_env: %(name)s=%(value)s', name=name, value=value)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def request_agent(self, data):
|
def request_agent(self, data):
|
||||||
log.msg('request_agent: %s' % repr(data) )
|
log.msg('request_agent: %s' % repr(data))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def request_x11_req(self, data):
|
def request_x11_req(self, data):
|
||||||
log.msg('request_x11: %s' % repr(data) )
|
log.msg('request_x11: %s' % repr(data))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# this is reliably called on session close/disconnect and calls the avatar
|
# this is reliably called on session close/disconnect and calls the avatar
|
||||||
@ -297,7 +297,7 @@ class HoneyPotSSHSession(session.SSHSession):
|
|||||||
session.SSHSession.loseConnection(self)
|
session.SSHSession.loseConnection(self)
|
||||||
|
|
||||||
def channelClosed(self):
|
def channelClosed(self):
|
||||||
log.msg( "Called channelClosed in SSHSession")
|
log.msg("Called channelClosed in SSHSession")
|
||||||
|
|
||||||
# FIXME: recent twisted conch avatar.py uses IConchuser here
|
# FIXME: recent twisted conch avatar.py uses IConchuser here
|
||||||
@implementer(conchinterfaces.ISession)
|
@implementer(conchinterfaces.ISession)
|
||||||
@ -316,7 +316,7 @@ class HoneyPotAvatar(avatar.ConchUser):
|
|||||||
|
|
||||||
# sftp support enabled only when option is explicitly set
|
# sftp support enabled only when option is explicitly set
|
||||||
if self.env.cfg.has_option('honeypot', 'sftp_enabled'):
|
if self.env.cfg.has_option('honeypot', 'sftp_enabled'):
|
||||||
if ( self.env.cfg.get('honeypot', 'sftp_enabled') == "true" ):
|
if (self.env.cfg.get('honeypot', 'sftp_enabled') == "true"):
|
||||||
self.subsystemLookup['sftp'] = filetransfer.FileTransferServer
|
self.subsystemLookup['sftp'] = filetransfer.FileTransferServer
|
||||||
|
|
||||||
self.uid = self.gid = auth.UserDB().getUID(self.username)
|
self.uid = self.gid = auth.UserDB().getUID(self.username)
|
||||||
@ -335,9 +335,9 @@ class HoneyPotAvatar(avatar.ConchUser):
|
|||||||
self.protocol = proto
|
self.protocol = proto
|
||||||
|
|
||||||
def getPty(self, terminal, windowSize, attrs):
|
def getPty(self, terminal, windowSize, attrs):
|
||||||
#log.msg( 'Terminal size: %s %s' % windowSize[0:2] )
|
#log.msg('Terminal size: %s %s' % windowSize[0:2])
|
||||||
log.msg( eventid='KIPP0010', width=windowSize[0], height=windowSize[1],
|
log.msg(eventid='KIPP0010', width=windowSize[0], height=windowSize[1],
|
||||||
format='Terminal Size: %(width)s %(height)s' )
|
format='Terminal Size: %(width)s %(height)s')
|
||||||
|
|
||||||
self.windowSize = windowSize
|
self.windowSize = windowSize
|
||||||
return None
|
return None
|
||||||
@ -347,7 +347,7 @@ class HoneyPotAvatar(avatar.ConchUser):
|
|||||||
if not cfg.has_option('honeypot', 'exec_enabled') or \
|
if not cfg.has_option('honeypot', 'exec_enabled') or \
|
||||||
cfg.get('honeypot', 'exec_enabled').lower() not in \
|
cfg.get('honeypot', 'exec_enabled').lower() not in \
|
||||||
('yes', 'true', 'on'):
|
('yes', 'true', 'on'):
|
||||||
log.msg( 'Exec disabled. Not executing command: "%s"' % cmd )
|
log.msg('Exec disabled. Not executing command: "%s"' % cmd)
|
||||||
raise exceptions.NotEnabledException(
|
raise exceptions.NotEnabledException(
|
||||||
'exec_enabled not enabled in configuration file!')
|
'exec_enabled not enabled in configuration file!')
|
||||||
return
|
return
|
||||||
@ -376,7 +376,7 @@ def getRSAKeys():
|
|||||||
public_key = cfg.get('honeypot', 'rsa_public_key')
|
public_key = cfg.get('honeypot', 'rsa_public_key')
|
||||||
private_key = cfg.get('honeypot', 'rsa_private_key')
|
private_key = cfg.get('honeypot', 'rsa_private_key')
|
||||||
if not (os.path.exists(public_key) and os.path.exists(private_key)):
|
if not (os.path.exists(public_key) and os.path.exists(private_key)):
|
||||||
log.msg( "Generating new RSA keypair..." )
|
log.msg("Generating new RSA keypair...")
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
from twisted.python import randbytes
|
from twisted.python import randbytes
|
||||||
KEY_LENGTH = 2048
|
KEY_LENGTH = 2048
|
||||||
@ -399,7 +399,7 @@ def getDSAKeys():
|
|||||||
public_key = cfg.get('honeypot', 'dsa_public_key')
|
public_key = cfg.get('honeypot', 'dsa_public_key')
|
||||||
private_key = cfg.get('honeypot', 'dsa_private_key')
|
private_key = cfg.get('honeypot', 'dsa_private_key')
|
||||||
if not (os.path.exists(public_key) and os.path.exists(private_key)):
|
if not (os.path.exists(public_key) and os.path.exists(private_key)):
|
||||||
log.msg( "Generating new DSA keypair..." )
|
log.msg("Generating new DSA keypair...")
|
||||||
from Crypto.PublicKey import DSA
|
from Crypto.PublicKey import DSA
|
||||||
from twisted.python import randbytes
|
from twisted.python import randbytes
|
||||||
KEY_LENGTH = 1024
|
KEY_LENGTH = 1024
|
||||||
@ -455,7 +455,7 @@ class KippoSFTPFile:
|
|||||||
self.contents = self.server.fs.file_contents(self.filename)
|
self.contents = self.server.fs.file_contents(self.filename)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if ( self.bytes_written > 0 ):
|
if (self.bytes_written > 0):
|
||||||
self.server.fs.update_size(self.filename, self.bytes_written)
|
self.server.fs.update_size(self.filename, self.bytes_written)
|
||||||
return self.server.fs.close(self.fd)
|
return self.server.fs.close(self.fd)
|
||||||
|
|
||||||
@ -531,34 +531,34 @@ class KippoSFTPServer:
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
def openFile(self, filename, flags, attrs):
|
def openFile(self, filename, flags, attrs):
|
||||||
log.msg( "SFTP openFile: %s" % filename )
|
log.msg("SFTP openFile: %s" % filename)
|
||||||
return KippoSFTPFile(self, self._absPath(filename), flags, attrs)
|
return KippoSFTPFile(self, self._absPath(filename), flags, attrs)
|
||||||
|
|
||||||
def removeFile(self, filename):
|
def removeFile(self, filename):
|
||||||
log.msg( "SFTP removeFile: %s" % filename )
|
log.msg("SFTP removeFile: %s" % filename)
|
||||||
return self.fs.remove(self._absPath(filename))
|
return self.fs.remove(self._absPath(filename))
|
||||||
|
|
||||||
def renameFile(self, oldpath, newpath):
|
def renameFile(self, oldpath, newpath):
|
||||||
log.msg( "SFTP renameFile: %s %s" % (oldpath, newpath) )
|
log.msg("SFTP renameFile: %s %s" % (oldpath, newpath))
|
||||||
return self.fs.rename(self._absPath(oldpath), self._absPath(newpath))
|
return self.fs.rename(self._absPath(oldpath), self._absPath(newpath))
|
||||||
|
|
||||||
def makeDirectory(self, path, attrs):
|
def makeDirectory(self, path, attrs):
|
||||||
log.msg( "SFTP makeDirectory: %s" % path )
|
log.msg("SFTP makeDirectory: %s" % path)
|
||||||
path = self._absPath(path)
|
path = self._absPath(path)
|
||||||
self.fs.mkdir2(path)
|
self.fs.mkdir2(path)
|
||||||
self._setAttrs(path, attrs)
|
self._setAttrs(path, attrs)
|
||||||
return
|
return
|
||||||
|
|
||||||
def removeDirectory(self, path):
|
def removeDirectory(self, path):
|
||||||
log.msg( "SFTP removeDirectory: %s" % path )
|
log.msg("SFTP removeDirectory: %s" % path)
|
||||||
return self.fs.rmdir(self._absPath(path))
|
return self.fs.rmdir(self._absPath(path))
|
||||||
|
|
||||||
def openDirectory(self, path):
|
def openDirectory(self, path):
|
||||||
log.msg( "SFTP OpenDirectory: %s" % path )
|
log.msg("SFTP OpenDirectory: %s" % path)
|
||||||
return KippoSFTPDirectory(self, self._absPath(path))
|
return KippoSFTPDirectory(self, self._absPath(path))
|
||||||
|
|
||||||
def getAttrs(self, path, followLinks):
|
def getAttrs(self, path, followLinks):
|
||||||
log.msg( "SFTP getAttrs: %s" % path )
|
log.msg("SFTP getAttrs: %s" % path)
|
||||||
path = self._absPath(path)
|
path = self._absPath(path)
|
||||||
if followLinks:
|
if followLinks:
|
||||||
s = self.fs.stat(path)
|
s = self.fs.stat(path)
|
||||||
@ -567,33 +567,33 @@ class KippoSFTPServer:
|
|||||||
return self._getAttrs(s)
|
return self._getAttrs(s)
|
||||||
|
|
||||||
def setAttrs(self, path, attrs):
|
def setAttrs(self, path, attrs):
|
||||||
log.msg( "SFTP setAttrs: %s" % path )
|
log.msg("SFTP setAttrs: %s" % path)
|
||||||
path = self._absPath(path)
|
path = self._absPath(path)
|
||||||
return self._setAttrs(path, attrs)
|
return self._setAttrs(path, attrs)
|
||||||
|
|
||||||
def readLink(self, path):
|
def readLink(self, path):
|
||||||
log.msg( "SFTP readLink: %s" % path )
|
log.msg("SFTP readLink: %s" % path)
|
||||||
path = self._absPath(path)
|
path = self._absPath(path)
|
||||||
return self.fs.readlink(path)
|
return self.fs.readlink(path)
|
||||||
|
|
||||||
def makeLink(self, linkPath, targetPath):
|
def makeLink(self, linkPath, targetPath):
|
||||||
log.msg( "SFTP makeLink: %s" % path )
|
log.msg("SFTP makeLink: %s" % path)
|
||||||
linkPath = self._absPath(linkPath)
|
linkPath = self._absPath(linkPath)
|
||||||
targetPath = self._absPath(targetPath)
|
targetPath = self._absPath(targetPath)
|
||||||
return self.fs.symlink(targetPath, linkPath)
|
return self.fs.symlink(targetPath, linkPath)
|
||||||
|
|
||||||
def realPath(self, path):
|
def realPath(self, path):
|
||||||
log.msg( "SFTP realPath: %s" % path )
|
log.msg("SFTP realPath: %s" % path)
|
||||||
return self.fs.realpath(self._absPath(path))
|
return self.fs.realpath(self._absPath(path))
|
||||||
|
|
||||||
def extendedRequest(self, extName, extData):
|
def extendedRequest(self, extName, extData):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
components.registerAdapter( KippoSFTPServer, HoneyPotAvatar, conchinterfaces.ISFTPServer)
|
components.registerAdapter(KippoSFTPServer, HoneyPotAvatar, conchinterfaces.ISFTPServer)
|
||||||
|
|
||||||
def KippoOpenConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avatar):
|
def KippoOpenConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avatar):
|
||||||
remoteHP, origHP = twisted.conch.ssh.forwarding.unpackOpen_direct_tcpip(data)
|
remoteHP, origHP = twisted.conch.ssh.forwarding.unpackOpen_direct_tcpip(data)
|
||||||
log.msg( "direct-tcp connection attempt to %s:%i" % remoteHP )
|
log.msg("direct-tcp connection attempt to %s:%i" % remoteHP)
|
||||||
return KippoConnectForwardingChannel(remoteHP,
|
return KippoConnectForwardingChannel(remoteHP,
|
||||||
remoteWindow=remoteWindow,
|
remoteWindow=remoteWindow,
|
||||||
remoteMaxPacket=remoteMaxPacket,
|
remoteMaxPacket=remoteMaxPacket,
|
||||||
@ -602,10 +602,10 @@ def KippoOpenConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avatar
|
|||||||
class KippoConnectForwardingChannel(forwarding.SSHConnectForwardingChannel):
|
class KippoConnectForwardingChannel(forwarding.SSHConnectForwardingChannel):
|
||||||
|
|
||||||
def channelOpen(self, specificData):
|
def channelOpen(self, specificData):
|
||||||
log.msg( "Faking channel open %s:%i" % self.hostport )
|
log.msg("Faking channel open %s:%i" % self.hostport)
|
||||||
|
|
||||||
def dataReceived(self, data):
|
def dataReceived(self, data):
|
||||||
log.msg( "received data %s" % repr( data ))
|
log.msg("received data %s" % repr(data))
|
||||||
|
|
||||||
|
|
||||||
# vim: set et sw=4 et:
|
# vim: set et sw=4 et:
|
||||||
|
@ -8,7 +8,7 @@ import struct
|
|||||||
OP_OPEN, OP_CLOSE, OP_WRITE, OP_EXEC = 1, 2, 3, 4
|
OP_OPEN, OP_CLOSE, OP_WRITE, OP_EXEC = 1, 2, 3, 4
|
||||||
TYPE_INPUT, TYPE_OUTPUT, TYPE_INTERACT = 1, 2, 3
|
TYPE_INPUT, TYPE_OUTPUT, TYPE_INTERACT = 1, 2, 3
|
||||||
|
|
||||||
def ttylog_write(logfile, len, direction, stamp, data = None):
|
def ttylog_write(logfile, len, direction, stamp, data=None):
|
||||||
f = file(logfile, 'ab')
|
f = file(logfile, 'ab')
|
||||||
sec, usec = int(stamp), int(1000000 * (stamp - int(stamp)))
|
sec, usec = int(stamp), int(1000000 * (stamp - int(stamp)))
|
||||||
f.write(struct.pack('<iLiiLL', 3, 0, len, direction, sec, usec))
|
f.write(struct.pack('<iLiiLL', 3, 0, len, direction, sec, usec))
|
||||||
|
@ -59,13 +59,13 @@ def uptime(total_seconds):
|
|||||||
total_seconds = float(total_seconds)
|
total_seconds = float(total_seconds)
|
||||||
|
|
||||||
# Helper vars:
|
# Helper vars:
|
||||||
MINUTE = 60
|
MINUTE = 60
|
||||||
HOUR = MINUTE * 60
|
HOUR = MINUTE * 60
|
||||||
DAY = HOUR * 24
|
DAY = HOUR * 24
|
||||||
|
|
||||||
# Get the days, hours, etc:
|
# Get the days, hours, etc:
|
||||||
days = int(total_seconds / DAY)
|
days = int(total_seconds / DAY)
|
||||||
hours = int((total_seconds % DAY) / HOUR)
|
hours = int((total_seconds % DAY) / HOUR)
|
||||||
minutes = int((total_seconds % HOUR) / MINUTE)
|
minutes = int((total_seconds % HOUR) / MINUTE)
|
||||||
|
|
||||||
# 14 days, 3:53
|
# 14 days, 3:53
|
||||||
@ -73,7 +73,7 @@ def uptime(total_seconds):
|
|||||||
|
|
||||||
s = ''
|
s = ''
|
||||||
if days > 0:
|
if days > 0:
|
||||||
s += str(days) + " " + (days == 1 and "day" or "days" ) + ", "
|
s += str(days) + " " + (days == 1 and "day" or "days") + ", "
|
||||||
if len(s) > 0 or hours > 0:
|
if len(s) > 0 or hours > 0:
|
||||||
s += '%s:%s' % (str(hours).rjust(2), str(minutes).rjust(2, '0'))
|
s += '%s:%s' % (str(hours).rjust(2), str(minutes).rjust(2, '0'))
|
||||||
else:
|
else:
|
||||||
|
Reference in New Issue
Block a user