mirror of https://github.com/cowrie/cowrie.git
Merge branch 'master' into output-plugin
Conflicts: kippo/core/ssh.py
This commit is contained in:
commit
d4d3bdbe0e
|
@ -1,3 +1,8 @@
|
|||
|
||||
* 2015-02-25 Internals for dblog/ modules changed completely. Now accepts structured logging arguments, and uses eventids instead of regex parsing
|
||||
* 2015-02-20 Removed screen clear/reset on logout
|
||||
* 2015-02-19 Configuration directives have changed! ssh_addr has become listen_addr and ssh_port has become listen_port. The old keywords are still accepted for backwards compatibility
|
||||
|
||||
* default behaviour is changed to disable the exit jail
|
||||
* sftp support
|
||||
* exec support
|
||||
|
@ -6,7 +11,7 @@
|
|||
* allow wget download over non-80 port
|
||||
* simple JSON logging to kippo.json
|
||||
* accept log and deny publickey authentication
|
||||
* add uname -r command
|
||||
* add uname -r, -m flags
|
||||
* add working sleep command
|
||||
* enabled ssh diffie-hellman-group-exchange-sha1 algorithm
|
||||
* add 'bash -c' support (no effect option)
|
||||
|
@ -33,3 +38,4 @@
|
|||
* add 'poweroff' 'halt' and 'reboot' aliases for shutdown
|
||||
* add environment passing to commands
|
||||
* added 'which', 'netstat' and 'gcc' from kippo-extra
|
||||
* logging framework allows for keyword use
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
# IP addresses to listen for incoming SSH connections.
|
||||
#
|
||||
# (default: 0.0.0.0) = any address
|
||||
#ssh_addr = 0.0.0.0
|
||||
#listen_addr = 0.0.0.0
|
||||
|
||||
# Port to listen for incoming SSH connections.
|
||||
#
|
||||
# (default: 2222)
|
||||
ssh_port = 2222
|
||||
#listen_port = 2222
|
||||
|
||||
# Hostname for the honeypot. Displayed by the shell prompt of the virtual
|
||||
# environment.
|
||||
|
@ -216,7 +216,7 @@ interact_port = 5123
|
|||
#logfile = log/kippo-textlog.log
|
||||
|
||||
# JSON based logging module
|
||||
#[database_jsonlog]
|
||||
[database_jsonlog]
|
||||
logfile = log/kippolog.json
|
||||
|
||||
#[database_hpfeeds]
|
||||
|
|
29
kippo.tac
29
kippo.tac
|
@ -3,13 +3,11 @@
|
|||
|
||||
import sys, os
|
||||
if sys.platform == 'win32':
|
||||
import os, inspect
|
||||
# this is when just running on win32
|
||||
sys.path.insert(0, os.path.abspath(os.getcwd()))
|
||||
# and this is when running as a service
|
||||
#os.chdir(os.path.dirname(inspect.getfile(inspect.currentframe())))
|
||||
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.application import internet, service
|
||||
from twisted.cred import portal
|
||||
from twisted.conch.ssh import factory, keys
|
||||
|
@ -23,7 +21,6 @@ if not os.path.exists('kippo.cfg'):
|
|||
sys.exit(1)
|
||||
|
||||
from kippo.core.config import config
|
||||
import kippo.core.auth
|
||||
import kippo.core.honeypot
|
||||
import kippo.core.ssh
|
||||
from kippo import core
|
||||
|
@ -42,16 +39,27 @@ factory.privateKeys = {'ssh-rsa': keys.Key.fromString(data=rsa_privKeyString),
|
|||
'ssh-dss': keys.Key.fromString(data=dsa_privKeyString)}
|
||||
|
||||
cfg = config()
|
||||
if cfg.has_option('honeypot', 'ssh_addr'):
|
||||
ssh_addr = cfg.get('honeypot', 'ssh_addr')
|
||||
|
||||
if cfg.has_option('honeypot', 'listen_addr'):
|
||||
listen_addr = cfg.get('honeypot', 'listen_addr')
|
||||
elif cfg.has_option('honeypot', 'ssh_addr'):
|
||||
# ssh_addr for backwards compatibility
|
||||
listen_addr = cfg.get('honeypot', 'ssh_addr')
|
||||
else:
|
||||
ssh_addr = '0.0.0.0'
|
||||
listen_addr = '0.0.0.0'
|
||||
|
||||
if cfg.has_option('honeypot', 'listen_port'):
|
||||
listen_port = int(cfg.get('honeypot', 'listen_port'))
|
||||
elif cfg.has_option('honeypot', 'ssh_port'):
|
||||
# ssh_port for backwards compatibility
|
||||
listen_port = int(cfg.get('honeypot', 'ssh_port'))
|
||||
else:
|
||||
listen_port = 2222
|
||||
|
||||
application = service.Application('honeypot')
|
||||
for i in ssh_addr.split():
|
||||
service = internet.TCPServer(
|
||||
int(cfg.get('honeypot', 'ssh_port')), factory,
|
||||
interface=i)
|
||||
for i in listen_addr.split():
|
||||
service = internet.TCPServer( listen_port,
|
||||
factory, interface=i)
|
||||
service.setServiceParent(application)
|
||||
|
||||
if cfg.has_option('honeypot', 'interact_enabled') and \
|
||||
|
@ -59,7 +67,6 @@ if cfg.has_option('honeypot', 'interact_enabled') and \
|
|||
('yes', 'true', 'on'):
|
||||
iport = int(cfg.get('honeypot', 'interact_port'))
|
||||
from kippo.core import interact
|
||||
from twisted.internet import protocol
|
||||
service = internet.TCPServer(iport, interact.makeInteractFactory(factory))
|
||||
service.setServiceParent(application)
|
||||
|
||||
|
|
|
@ -230,7 +230,9 @@ class command_passwd(HoneyPotCommand):
|
|||
self.exit()
|
||||
|
||||
def lineReceived(self, line):
|
||||
log.msg( 'INPUT (passwd):', line )
|
||||
#log.msg( 'INPUT (passwd):', line )
|
||||
log.msg( eventid='KIPP0008', realm='passwd', input=line,
|
||||
format='INPUT (%(realm)s): %(input)s' )
|
||||
self.password = line.strip()
|
||||
self.callbacks.pop(0)(line)
|
||||
commands['/usr/bin/passwd'] = command_passwd
|
||||
|
@ -428,7 +430,9 @@ class command_perl(HoneyPotCommand):
|
|||
self.exit()
|
||||
|
||||
def lineReceived(self, line):
|
||||
log.msg( 'INPUT (perl):', line )
|
||||
#log.msg( 'INPUT (perl):', line )
|
||||
log.msg( eventid='KIPP0008', realm='perl', input=line,
|
||||
format='INPUT (%(realm)s): %(input)s' )
|
||||
|
||||
commands['/usr/bin/perl'] = command_perl
|
||||
|
||||
|
@ -492,7 +496,9 @@ class command_php(HoneyPotCommand):
|
|||
self.exit()
|
||||
|
||||
def lineReceived(self, line):
|
||||
log.msg( 'INPUT (php):', line )
|
||||
#log.msg( 'INPUT (php):', line )
|
||||
log.msg( eventid='KIPP0008', realm='php', input=line,
|
||||
format='INPUT (%(realm)s): %(input)s' )
|
||||
|
||||
commands['/usr/bin/php'] = command_php
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ class command_uname(HoneyPotCommand):
|
|||
self.honeypot.hostname)
|
||||
elif len(self.args) and self.args[0].strip() in ('-r', '--kernel-release'):
|
||||
self.writeln( '2.6.26-2-686' )
|
||||
elif len(self.args) and self.args[0].strip() in ('-m', '--machine'):
|
||||
self.writeln( 'i686' )
|
||||
else:
|
||||
self.writeln('Linux')
|
||||
|
||||
|
|
|
@ -143,29 +143,34 @@ class command_wget(HoneyPotCommand):
|
|||
|
||||
def success(self, data, outfile):
|
||||
if not os.path.isfile(self.safeoutfile):
|
||||
print "there's no file " + self.safeoutfile
|
||||
log.msg("there's no file " + self.safeoutfile)
|
||||
self.exit()
|
||||
|
||||
shasum = hashlib.sha256(open(self.safeoutfile, 'rb').read()).hexdigest()
|
||||
hash_path = '%s/%s' % (self.download_path, shasum)
|
||||
|
||||
msg = 'SHA sum %s of URL %s in file %s' % \
|
||||
(shasum, self.url, self.fileName)
|
||||
print msg
|
||||
self.honeypot.logDispatch(msg)
|
||||
|
||||
# if we have content already, delete temp file
|
||||
if not os.path.exists(hash_path):
|
||||
print "moving " + self.safeoutfile + " -> " + hash_path
|
||||
os.rename(self.safeoutfile, hash_path)
|
||||
else:
|
||||
print "deleting " + self.safeoutfile + " SHA sum: " + shasum
|
||||
os.remove(self.safeoutfile)
|
||||
log.msg("Not storing duplicate content " + shasum)
|
||||
|
||||
self.honeypot.logDispatch( format='Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s',
|
||||
eventid='KIPP0007', url=self.url, outfile=hash_path, shasum=shasum )
|
||||
|
||||
log.msg( format='Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s',
|
||||
eventid='KIPP0007', url=self.url, outfile=hash_path, shasum=shasum )
|
||||
|
||||
# link friendly name to hash
|
||||
os.symlink( shasum, self.safeoutfile )
|
||||
|
||||
# FIXME: is this necessary?
|
||||
self.safeoutfile = hash_path
|
||||
|
||||
print "Updating realfile to " + hash_path
|
||||
# update the honeyfs to point to downloaded file
|
||||
f = self.fs.getfile(outfile)
|
||||
f[9] = hash_path
|
||||
f[A_REALFILE] = hash_path
|
||||
self.exit()
|
||||
|
||||
def error(self, error, url):
|
||||
|
@ -224,10 +229,6 @@ class HTTPProgressDownloader(client.HTTPDownloader):
|
|||
(self.wget.url,) )
|
||||
self.fileName = os.path.devnull
|
||||
self.nomore = True
|
||||
else:
|
||||
msg = 'Saving URL (%s) to %s' % (self.wget.url, self.fileName)
|
||||
self.wget.honeypot.logDispatch(msg)
|
||||
log.msg( msg )
|
||||
self.wget.writeln('Saving to: `%s' % self.fakeoutfile)
|
||||
self.wget.honeypot.terminal.nextLine()
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ from twisted.python import log, failure
|
|||
from twisted.conch import error
|
||||
from twisted.conch.ssh import keys
|
||||
|
||||
from kippo.core.config import config
|
||||
from config import config
|
||||
|
||||
# by Walter de Jong <walter@sara.nl>
|
||||
class UserDB(object):
|
||||
|
@ -156,12 +156,18 @@ class HoneypotPasswordChecker:
|
|||
return defer.succeed(username)
|
||||
return defer.fail(UnauthorizedLogin())
|
||||
|
||||
def checkUserPass(self, username, password):
|
||||
if UserDB().checklogin(username, password):
|
||||
log.msg( 'login attempt [%s/%s] succeeded' % (username, password) )
|
||||
def checkUserPass(self, theusername, thepassword):
|
||||
if UserDB().checklogin(theusername, thepassword):
|
||||
#log.msg( 'login attempt [%s/%s] succeeded' % (theusername, thepassword) )
|
||||
log.msg( eventid='KIPP0002',
|
||||
format='login attempt [%(username)s/%(password)s] succeeded',
|
||||
username=theusername, password=thepassword )
|
||||
return True
|
||||
else:
|
||||
log.msg( 'login attempt [%s/%s] failed' % (username, password) )
|
||||
#log.msg( 'login attempt [%s/%s] failed' % (theusername, thepassword) )
|
||||
log.msg( eventid='KIPP0003',
|
||||
format='login attempt [%(username)s/%(password)s] failed',
|
||||
username=theusername, password=thepassword )
|
||||
return False
|
||||
|
||||
# vim: set sw=4 et:
|
||||
|
|
|
@ -3,59 +3,60 @@
|
|||
|
||||
import re
|
||||
import time
|
||||
import abc
|
||||
|
||||
# dblog now operates based on eventids, no longer on regex parsing of the entry.
|
||||
# add an eventid using keyword args and it will be picked up by the dblogger
|
||||
# the KIPPxxxx naming convention is still subject to change.
|
||||
|
||||
# KIPP0001 : create session
|
||||
# KIPP0002 : succesful login
|
||||
# KIPP0003 : failed login
|
||||
# KIPP0004 : TTY log opened
|
||||
# KIPP0005 : handle command
|
||||
# KIPP0006 : handle unknown command
|
||||
# KIPP0007 : file download
|
||||
# KIPP0008 : INPUT
|
||||
# KIPP0009 : SSH Version
|
||||
# KIPP0010 : Terminal Size
|
||||
# KIPP0011 : Connection Lost
|
||||
# KIPP0012 : TTY log closed
|
||||
|
||||
class DBLogger(object):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, cfg):
|
||||
self.cfg = cfg
|
||||
self.sessions = {}
|
||||
self.ttylogs = {}
|
||||
self.re_connected = re.compile(
|
||||
'^New connection: ([0-9.]+):([0-9]+) \(([0-9.]+):([0-9]+)\) ' + \
|
||||
'\[session: ([0-9]+)\]$')
|
||||
self.re_sessionlog = re.compile(
|
||||
'.*HoneyPotTransport,([0-9]+),[0-9.]+$')
|
||||
|
||||
# :dispatch: means the message has been delivered directly via
|
||||
# logDispatch, instead of relying on the twisted logging, which breaks
|
||||
# on scope changes.
|
||||
self.re_map = [(re.compile(x[0]), x[1]) for x in (
|
||||
('^connection lost$',
|
||||
self._connectionLost),
|
||||
('^login attempt \[(?P<username>.*)/(?P<password>.*)\] failed',
|
||||
self.handleLoginFailed),
|
||||
('^login attempt \[(?P<username>.*)/(?P<password>.*)\] succeeded',
|
||||
self.handleLoginSucceeded),
|
||||
('^Opening TTY log: (?P<logfile>.*)$',
|
||||
self.handleTTYLogOpened),
|
||||
('^:dispatch: Command found: (?P<input>.*)$',
|
||||
self.handleCommand),
|
||||
('^:dispatch: Command not found: (?P<input>.*)$',
|
||||
self.handleUnknownCommand),
|
||||
('^:dispatch: Saving URL \((?P<url>.*)\) to (?P<outfile>.*)$',
|
||||
self.handleFileDownload),
|
||||
('^:dispatch: SHA sum (?P<shasum>.*) of URL (?P<url>.*) in file (?P<outfile>.*)$',
|
||||
self.handleShaSum),
|
||||
('^:dispatch: Updated outfile (?P<outfile>.*) to (?P<dl_file>.*) with SHA sum (?P<shasum>.*)$',
|
||||
self.handleUpdatedFile),
|
||||
('^INPUT \((?P<realm>[a-zA-Z0-9]+)\): (?P<input>.*)$',
|
||||
self.handleInput),
|
||||
('^Terminal size: (?P<height>[0-9]+) (?P<width>[0-9]+)$',
|
||||
self.handleTerminalSize),
|
||||
('^Remote SSH version: (?P<version>.*)$',
|
||||
self.handleClientVersion),
|
||||
)]
|
||||
# KIPP0001 is special since it kicks off new logging session,
|
||||
# and is not handled here
|
||||
self.events = {
|
||||
'KIPP0002': self.handleLoginSucceeded,
|
||||
'KIPP0003': self.handleLoginFailed,
|
||||
'KIPP0004': self.handleTTYLogOpened,
|
||||
'KIPP0005': self.handleCommand,
|
||||
'KIPP0006': self.handleUnknownCommand,
|
||||
'KIPP0007': self.handleFileDownload,
|
||||
'KIPP0008': self.handleInput,
|
||||
'KIPP0009': self.handleClientVersion,
|
||||
'KIPP0010': self.handleTerminalSize,
|
||||
'KIPP0011': self._connectionLost,
|
||||
}
|
||||
|
||||
self.start(cfg)
|
||||
|
||||
def logDispatch(self, sessionid, msg):
|
||||
if sessionid not in self.sessions.keys():
|
||||
return
|
||||
for regex, func in self.re_map:
|
||||
match = regex.match(msg)
|
||||
if match:
|
||||
func(self.sessions[sessionid], match.groupdict())
|
||||
break
|
||||
# used when the HoneypotTransport prefix is not available.
|
||||
def logDispatch(self, *msg, **kw):
|
||||
ev = kw
|
||||
ev['message'] = msg
|
||||
self.emit(ev)
|
||||
|
||||
def start():
|
||||
def start(self, cfg):
|
||||
"""Hook that can be used to set up connections in dbloggers"""
|
||||
pass
|
||||
|
||||
def getSensor(self):
|
||||
|
@ -68,28 +69,42 @@ class DBLogger(object):
|
|||
return int(time.mktime(time.gmtime()[:-1] + (-1,)))
|
||||
|
||||
def emit(self, ev):
|
||||
if not len(ev['message']):
|
||||
# ignore stdout and stderr
|
||||
if 'printed' in ev:
|
||||
return
|
||||
match = self.re_connected.match(ev['message'][0])
|
||||
if match:
|
||||
sessionid = int(match.groups()[4])
|
||||
self.sessions[sessionid] = \
|
||||
|
||||
# ignore anything without eventid
|
||||
if not 'eventid' in ev:
|
||||
return
|
||||
|
||||
# connection event is special. adds to list
|
||||
if ev['eventid'] == 'KIPP0001':
|
||||
sessionno = ev['sessionno']
|
||||
self.sessions[sessionno] = \
|
||||
self.createSession(
|
||||
match.groups()[0], int(match.groups()[1]),
|
||||
match.groups()[2], int(match.groups()[3]))
|
||||
ev['src_ip'], ev['src_port'], ev['dst_ip'], ev['dst_port'] )
|
||||
return
|
||||
match = self.re_sessionlog.match(ev['system'])
|
||||
if not match:
|
||||
|
||||
# use explicit sessionno if coming from dispatch
|
||||
if 'sessionno' in ev:
|
||||
sessionno = ev['sessionno']
|
||||
del ev['sessionno']
|
||||
# else extract session id from the twisted log prefix
|
||||
elif 'system' in ev:
|
||||
match = self.re_sessionlog.match(ev['system'])
|
||||
if not match:
|
||||
return
|
||||
sessionno = int(match.groups()[0])
|
||||
|
||||
if sessionno not in self.sessions.keys():
|
||||
return
|
||||
sessionid = int(match.groups()[0])
|
||||
if sessionid not in self.sessions.keys():
|
||||
return
|
||||
message = ev['message'][0]
|
||||
for regex, func in self.re_map:
|
||||
match = regex.match(message)
|
||||
if match:
|
||||
func(self.sessions[sessionid], match.groupdict())
|
||||
break
|
||||
|
||||
if 'eventid' in ev:
|
||||
if ev['eventid'] in self.events:
|
||||
self.events[ev['eventid']]( self.sessions[sessionno], ev )
|
||||
return
|
||||
|
||||
print "error, unknown eventid %s" % repr(ev)
|
||||
|
||||
def _connectionLost(self, session, args):
|
||||
self.handleConnectionLost(session, args)
|
||||
|
@ -106,7 +121,8 @@ class DBLogger(object):
|
|||
f.close()
|
||||
return ttylog
|
||||
|
||||
# We have to return an unique ID
|
||||
# We have to return a unique ID
|
||||
@abc.abstractmethod
|
||||
def createSession(self, peerIP, peerPort, hostIP, hostPort):
|
||||
return 0
|
||||
|
||||
|
@ -150,12 +166,4 @@ class DBLogger(object):
|
|||
def handleFileDownload(self, session, args):
|
||||
pass
|
||||
|
||||
# args has: shasum, url, outfile
|
||||
def handleShaSum(self, session, args):
|
||||
pass
|
||||
|
||||
# args has: outfile, dl_file, shasum
|
||||
def handleUpdatedFile(self, session, args):
|
||||
pass
|
||||
|
||||
# vim: set sw=4 et:
|
||||
|
|
|
@ -8,7 +8,7 @@ import re
|
|||
import stat
|
||||
import errno
|
||||
|
||||
from kippo.core.config import config
|
||||
from config import config
|
||||
|
||||
A_NAME, \
|
||||
A_TYPE, \
|
||||
|
|
|
@ -9,9 +9,10 @@ import copy
|
|||
import pickle
|
||||
|
||||
from twisted.python import log
|
||||
from kippo.core import fs
|
||||
from kippo.core.config import config
|
||||
import kippo.core.exceptions
|
||||
|
||||
import fs
|
||||
import exceptions
|
||||
from config import config
|
||||
|
||||
class HoneyPotCommand(object):
|
||||
def __init__(self, protocol, *args):
|
||||
|
@ -124,12 +125,15 @@ class HoneyPotShell(object):
|
|||
rargs.append(arg)
|
||||
cmdclass = self.honeypot.getCommand(cmd, envvars['PATH'].split(':'))
|
||||
if cmdclass:
|
||||
log.msg( 'Command found: %s' % (line,) )
|
||||
self.honeypot.logDispatch('Command found: %s' % (line,))
|
||||
#log.msg( 'Command found: %s' % (line,) )
|
||||
log.msg( eventid='KIPP0005', input=line, format='Command found: %(input)s' )
|
||||
#self.honeypot.logDispatch('Command found: %s' % (line,))
|
||||
self.honeypot.call_command(cmdclass, *rargs)
|
||||
else:
|
||||
self.honeypot.logDispatch('Command not found: %s' % (line,))
|
||||
log.msg( 'Command not found: %s' % (line,) )
|
||||
#log.msg( 'Command not found: %s' % (line,) )
|
||||
log.msg( eventid='KIPP0006',
|
||||
input=line, format='Command not found: %(input)s' )
|
||||
#self.honeypot.logDispatch('Command not found: %s' % (line,))
|
||||
if len(line):
|
||||
self.honeypot.writeln('bash: %s: command not found' % cmd)
|
||||
runOrPrompt()
|
||||
|
|
|
@ -6,7 +6,7 @@ import time
|
|||
from twisted.internet import protocol
|
||||
from twisted.conch import telnet, recvline
|
||||
|
||||
from kippo.core import ttylog
|
||||
import ttylog
|
||||
|
||||
class Interact(telnet.Telnet):
|
||||
|
||||
|
|
|
@ -3,20 +3,16 @@
|
|||
|
||||
import os
|
||||
import time
|
||||
import struct
|
||||
import socket
|
||||
import copy
|
||||
|
||||
from twisted.conch import recvline
|
||||
from twisted.conch.ssh import transport
|
||||
from twisted.conch.insults import insults
|
||||
from twisted.internet import protocol
|
||||
from twisted.python import log
|
||||
|
||||
from kippo.core import ttylog, fs
|
||||
from kippo.core.config import config
|
||||
import kippo.core.honeypot
|
||||
from kippo import core
|
||||
import honeypot
|
||||
import ttylog
|
||||
from config import config
|
||||
|
||||
class HoneyPotBaseProtocol(insults.TerminalProtocol):
|
||||
def __init__(self, avatar, env):
|
||||
|
@ -33,10 +29,10 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol):
|
|||
self.password_input = False
|
||||
self.cmdstack = []
|
||||
|
||||
def logDispatch(self, msg):
|
||||
def logDispatch(self, *msg, **args):
|
||||
transport = self.terminal.transport.session.conn.transport
|
||||
msg = ':dispatch: ' + msg
|
||||
transport.factory.logDispatch(transport.transport.sessionno, msg)
|
||||
args['sessionno']=transport.transport.sessionno
|
||||
transport.factory.logDispatch(*msg,**args)
|
||||
|
||||
def connectionMade(self):
|
||||
self.displayMOTD()
|
||||
|
@ -47,7 +43,6 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol):
|
|||
self.realClientPort = transport.transport.getPeer().port
|
||||
self.clientVersion = transport.otherVersionString
|
||||
self.logintime = transport.logintime
|
||||
self.ttylog_file = transport.ttylog_file
|
||||
|
||||
# source IP of client in user visible reports (can be fake or real)
|
||||
cfg = config()
|
||||
|
@ -61,7 +56,7 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol):
|
|||
else:
|
||||
# Hack to get ip
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(("8.8.8.8",80))
|
||||
s.connect(("8.8.8.8", 80))
|
||||
self.kippoIP = s.getsockname()[0]
|
||||
s.close()
|
||||
|
||||
|
@ -71,17 +66,16 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol):
|
|||
except:
|
||||
pass
|
||||
|
||||
# this doesn't seem to be called upon disconnect, so please use
|
||||
# HoneyPotTransport.connectionLost instead
|
||||
# this is only called on explicit logout, not on disconnect
|
||||
def connectionLost(self, reason):
|
||||
pass
|
||||
log.msg( eventid='KIPP0011', format='Connection lost')
|
||||
# not sure why i need to do this:
|
||||
# scratch that, these don't seem to be necessary anymore:
|
||||
#del self.fs
|
||||
#del self.commands
|
||||
|
||||
def txtcmd(self, txt):
|
||||
class command_txtcmd(core.honeypot.HoneyPotCommand):
|
||||
class command_txtcmd(honeypot.HoneyPotCommand):
|
||||
def call(self):
|
||||
log.msg( 'Reading txtcmd from "%s"' % txt )
|
||||
f = file(txt, 'r')
|
||||
|
@ -149,9 +143,9 @@ class HoneyPotExecProtocol(HoneyPotBaseProtocol):
|
|||
|
||||
def connectionMade(self):
|
||||
HoneyPotBaseProtocol.connectionMade(self)
|
||||
self.terminal.transport.session.conn.transport.stdinlog_open = True
|
||||
self.terminal.stdinlog_open = True
|
||||
|
||||
self.cmdstack = [core.honeypot.HoneyPotShell(self, interactive=False)]
|
||||
self.cmdstack = [honeypot.HoneyPotShell(self, interactive=False)]
|
||||
self.cmdstack[0].lineReceived(self.execcmd)
|
||||
|
||||
class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLine):
|
||||
|
@ -164,7 +158,7 @@ class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLin
|
|||
HoneyPotBaseProtocol.connectionMade(self)
|
||||
recvline.HistoricRecvLine.connectionMade(self)
|
||||
|
||||
self.cmdstack = [core.honeypot.HoneyPotShell(self)]
|
||||
self.cmdstack = [honeypot.HoneyPotShell(self)]
|
||||
|
||||
transport = self.terminal.transport.session.conn.transport
|
||||
transport.factory.sessions[transport.transport.sessionno] = self
|
||||
|
@ -234,22 +228,27 @@ class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLin
|
|||
self.lineBuffer = self.lineBuffer[self.lineBufferIndex:]
|
||||
self.lineBufferIndex = 0
|
||||
|
||||
|
||||
class LoggingServerProtocol(insults.ServerProtocol):
|
||||
"""
|
||||
Wrapper for ServerProtocol that implements TTY logging
|
||||
"""
|
||||
def connectionMade(self):
|
||||
transport = self.transport.session.conn.transport
|
||||
|
||||
transport.ttylog_file = '%s/tty/%s-%s.log' % \
|
||||
(config().get('honeypot', 'log_path'),
|
||||
time.strftime('%Y%m%d-%H%M%S'), transport.transportId )
|
||||
log.msg( 'Opening TTY log: %s' % transport.ttylog_file )
|
||||
ttylog.ttylog_open(transport.ttylog_file, time.time())
|
||||
transport.ttylog_open = True
|
||||
|
||||
transport.stdinlog_file = '%s/%s-%s-stdin.log' % \
|
||||
self.ttylog_file = transport.ttylog_file
|
||||
log.msg( eventid='KIPP0004', logfile=transport.ttylog_file,
|
||||
format='Opening TTY Log: %(logfile)s')
|
||||
|
||||
ttylog.ttylog_open(transport.ttylog_file, time.time())
|
||||
self.ttylog_open = True
|
||||
|
||||
self.stdinlog_file = '%s/%s-%s-stdin.log' % \
|
||||
(config().get('honeypot', 'download_path'),
|
||||
time.strftime('%Y%m%d-%H%M%S'), transport.transportId )
|
||||
transport.stdinlog_open = False
|
||||
self.stdinlog_open = False
|
||||
|
||||
insults.ServerProtocol.connectionMade(self)
|
||||
|
||||
|
@ -257,25 +256,39 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
|||
transport = self.transport.session.conn.transport
|
||||
for i in transport.interactors:
|
||||
i.sessionWrite(bytes)
|
||||
if transport.ttylog_open and not noLog:
|
||||
if self.ttylog_open and not noLog:
|
||||
ttylog.ttylog_write(transport.ttylog_file, len(bytes),
|
||||
ttylog.TYPE_OUTPUT, time.time(), bytes)
|
||||
|
||||
insults.ServerProtocol.write(self, bytes)
|
||||
|
||||
def dataReceived(self, data, noLog = False):
|
||||
transport = self.transport.session.conn.transport
|
||||
if transport.ttylog_open and not noLog:
|
||||
if self.ttylog_open and not noLog:
|
||||
ttylog.ttylog_write(transport.ttylog_file, len(data),
|
||||
ttylog.TYPE_INPUT, time.time(), data)
|
||||
if transport.stdinlog_open and not noLog:
|
||||
f = file( transport.stdinlog_file, 'ab' )
|
||||
if self.stdinlog_open and not noLog:
|
||||
log.msg( "Saving stdin log: %s" % self.stdinlog_file )
|
||||
f = file( self.stdinlog_file, 'ab' )
|
||||
f.write(data)
|
||||
f.close
|
||||
|
||||
insults.ServerProtocol.dataReceived(self, data)
|
||||
|
||||
# this doesn't seem to be called upon disconnect, so please use
|
||||
# HoneyPotTransport.connectionLost instead
|
||||
# override super to remove the terminal reset on logout
|
||||
def loseConnection(self):
|
||||
self.transport.loseConnection()
|
||||
|
||||
# FIXME: this method is called 4 times on logout....
|
||||
# it's called once from Avatar.closed() if disconnected
|
||||
def connectionLost(self, reason):
|
||||
# log.msg( "received call to LSP.connectionLost" )
|
||||
transport = self.transport.session.conn.transport
|
||||
if self.ttylog_open:
|
||||
log.msg( eventid='KIPP0012', format='Closing TTY Log: %(ttylog)s',
|
||||
ttylog=transport.ttylog_file)
|
||||
ttylog.ttylog_close(transport.ttylog_file, time.time())
|
||||
self.ttylog_open = False
|
||||
insults.ServerProtocol.connectionLost(self, reason)
|
||||
|
||||
# vim: set sw=4 et:
|
||||
|
|
|
@ -21,13 +21,16 @@ from twisted.conch.ssh.common import NS, getNS
|
|||
|
||||
import ConfigParser
|
||||
|
||||
from kippo.core import ttylog, utils, fs, sshserver
|
||||
from kippo.core.config import config
|
||||
import kippo.core.auth
|
||||
import kippo.core.honeypot
|
||||
import kippo.core.ssh
|
||||
import kippo.core.protocol
|
||||
from kippo import core
|
||||
import utils
|
||||
import fs
|
||||
import sshserver
|
||||
import auth
|
||||
import honeypot
|
||||
import ssh
|
||||
import protocol
|
||||
import sshserver
|
||||
import exceptions
|
||||
from config import config
|
||||
|
||||
class HoneyPotSSHUserAuthServer(userauth.SSHUserAuthServer):
|
||||
def serviceStarted(self):
|
||||
|
@ -63,11 +66,11 @@ class HoneyPotSSHFactory(factory.SSHFactory):
|
|||
}
|
||||
|
||||
# Special delivery to the loggers to avoid scope problems
|
||||
def logDispatch(self, sessionid, msg):
|
||||
def logDispatch(self, *msg, **args):
|
||||
for dblog in self.dbloggers:
|
||||
dblog.logDispatch(sessionid, msg)
|
||||
dblog.logDispatch(*msg, **args)
|
||||
for output in self.output_plugins:
|
||||
output.logDispatch(sessionid, msg)
|
||||
output.logDispatch(*msg, **args)
|
||||
|
||||
def __init__(self):
|
||||
cfg = config()
|
||||
|
@ -168,7 +171,7 @@ class HoneyPotRealm:
|
|||
|
||||
def __init__(self):
|
||||
# I don't know if i'm supposed to keep static stuff here
|
||||
self.env = core.honeypot.HoneyPotEnvironment()
|
||||
self.env = honeypot.HoneyPotEnvironment()
|
||||
|
||||
def requestAvatar(self, avatarId, mind, *interfaces):
|
||||
if conchinterfaces.IConchUser in interfaces:
|
||||
|
@ -177,43 +180,35 @@ class HoneyPotRealm:
|
|||
else:
|
||||
raise Exception, "No supported interfaces found."
|
||||
|
||||
class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport):
|
||||
class HoneyPotTransport(sshserver.KippoSSHServerTransport):
|
||||
"""
|
||||
@ivar logintime: time of login
|
||||
|
||||
@ivar interactors: interactors
|
||||
|
||||
@ivar ttylog_open: whether log is open
|
||||
|
||||
@ivar transportId: UUID of this transport
|
||||
|
||||
@ivar _hadVersion: used so we only send key exchange after receive version info
|
||||
"""
|
||||
|
||||
_hadVersion = False
|
||||
ttylog_open = False
|
||||
interactors = []
|
||||
transportId = ''
|
||||
|
||||
def connectionMade(self):
|
||||
self.logintime = time.time()
|
||||
self.transportId = uuid.uuid4().hex[:8]
|
||||
self.interactors = []
|
||||
|
||||
log.msg( 'New connection: %s:%s (%s:%s) [session: %d]' % \
|
||||
(self.transport.getPeer().host, self.transport.getPeer().port,
|
||||
self.transport.getHost().host, self.transport.getHost().port,
|
||||
self.transport.sessionno) )
|
||||
#log.msg( 'New connection: %s:%s (%s:%s) [session: %d]' % \
|
||||
# (self.transport.getPeer().host, self.transport.getPeer().port,
|
||||
# self.transport.getHost().host, self.transport.getHost().port,
|
||||
# self.transport.sessionno) )
|
||||
log.msg( eventid='KIPP0001',
|
||||
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,
|
||||
dst_ip=self.transport.getHost().host, dst_port=self.transport.getHost().port,
|
||||
sessionno=self.transport.sessionno )
|
||||
|
||||
kippo.core.sshserver.KippoSSHServerTransport.connectionMade(self)
|
||||
sshserver.KippoSSHServerTransport.connectionMade(self)
|
||||
|
||||
def sendKexInit(self):
|
||||
# Don't send key exchange prematurely
|
||||
if not self.gotVersion:
|
||||
return
|
||||
kippo.core.sshserver.KippoSSHServerTransport.sendKexInit(self)
|
||||
sshserver.KippoSSHServerTransport.sendKexInit(self)
|
||||
|
||||
def dataReceived(self, data):
|
||||
kippo.core.sshserver.KippoSSHServerTransport.dataReceived(self, data)
|
||||
sshserver.KippoSSHServerTransport.dataReceived(self, data)
|
||||
# later versions seem to call sendKexInit again on their own
|
||||
if twisted.version.major < 11 and \
|
||||
not self._hadVersion and self.gotVersion:
|
||||
|
@ -230,8 +225,8 @@ class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport):
|
|||
log.msg('KEXINIT: client supported MAC: %s' % macCS )
|
||||
log.msg('KEXINIT: client supported compression: %s' % compCS )
|
||||
log.msg('KEXINIT: client supported lang: %s' % langCS )
|
||||
log.msg( 'Remote SSH version: %s' % self.otherVersionString,)
|
||||
return kippo.core.sshserver.KippoSSHServerTransport.ssh_KEXINIT(self, packet)
|
||||
log.msg( eventid='KIPP0009', version=self.otherVersionString, format='Remote SSH version: %(version)s' )
|
||||
return sshserver.KippoSSHServerTransport.ssh_KEXINIT(self, packet)
|
||||
|
||||
def lastlogExit(self):
|
||||
starttime = time.strftime('%a %b %d %H:%M',
|
||||
|
@ -245,15 +240,13 @@ class HoneyPotTransport(kippo.core.sshserver.KippoSSHServerTransport):
|
|||
|
||||
# this seems to be the only reliable place of catching lost connection
|
||||
def connectionLost(self, reason):
|
||||
log.msg( "Connection Lost in SSH Transport" )
|
||||
for i in self.interactors:
|
||||
i.sessionClosed()
|
||||
if self.transport.sessionno in self.factory.sessions:
|
||||
del self.factory.sessions[self.transport.sessionno]
|
||||
self.lastlogExit()
|
||||
if self.ttylog_open:
|
||||
ttylog.ttylog_close(self.ttylog_file, time.time())
|
||||
self.ttylog_open = False
|
||||
kippo.core.sshserver.KippoSSHServerTransport.connectionLost(self, reason)
|
||||
sshserver.KippoSSHServerTransport.connectionLost(self, reason)
|
||||
|
||||
class HoneyPotSSHSession(session.SSHSession):
|
||||
|
||||
|
@ -277,10 +270,17 @@ class HoneyPotSSHSession(session.SSHSession):
|
|||
log.msg('request_x11: %s' % repr(data) )
|
||||
return 0
|
||||
|
||||
# this is reliably called on session close/disconnect and calls the avatar
|
||||
def closed(self):
|
||||
session.SSHSession.closed(self)
|
||||
|
||||
def loseConnection(self):
|
||||
self.conn.sendRequest(self, 'exit-status', "\x00"*4)
|
||||
session.SSHSession.loseConnection(self)
|
||||
|
||||
def channelClosed(self):
|
||||
log.msg( "Called channelClosed in SSHSession")
|
||||
|
||||
# FIXME: recent twisted conch avatar.py uses IConchuser here
|
||||
@implementer(conchinterfaces.ISession)
|
||||
class HoneyPotAvatar(avatar.ConchUser):
|
||||
|
@ -291,6 +291,7 @@ class HoneyPotAvatar(avatar.ConchUser):
|
|||
self.env = env
|
||||
self.fs = fs.HoneyPotFilesystem(copy.deepcopy(self.env.fs))
|
||||
self.hostname = self.env.cfg.get('honeypot', 'hostname')
|
||||
self.protocol = None
|
||||
|
||||
self.channelLookup.update({'session': HoneyPotSSHSession})
|
||||
self.channelLookup['direct-tcpip'] = KippoOpenConnectForwardingClient
|
||||
|
@ -300,41 +301,52 @@ class HoneyPotAvatar(avatar.ConchUser):
|
|||
if ( self.env.cfg.get('honeypot', 'sftp_enabled') == "true" ):
|
||||
self.subsystemLookup['sftp'] = filetransfer.FileTransferServer
|
||||
|
||||
self.uid = self.gid = core.auth.UserDB().getUID(self.username)
|
||||
self.uid = self.gid = auth.UserDB().getUID(self.username)
|
||||
if not self.uid:
|
||||
self.home = '/root'
|
||||
else:
|
||||
self.home = '/home/' + username
|
||||
|
||||
def openShell(self, protocol):
|
||||
serverProtocol = core.protocol.LoggingServerProtocol(
|
||||
core.protocol.HoneyPotInteractiveProtocol, self, self.env)
|
||||
serverProtocol.makeConnection(protocol)
|
||||
protocol.makeConnection(session.wrapProtocol(serverProtocol))
|
||||
def openShell(self, proto):
|
||||
serverProtocol = protocol.LoggingServerProtocol(
|
||||
protocol.HoneyPotInteractiveProtocol, self, self.env)
|
||||
self.protocol = serverProtocol
|
||||
serverProtocol.makeConnection(proto)
|
||||
proto.makeConnection(session.wrapProtocol(serverProtocol))
|
||||
#self.protocol = serverProtocol
|
||||
self.protocol = proto
|
||||
|
||||
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],
|
||||
format='Terminal Size: %(width)s %(height)s' )
|
||||
|
||||
self.windowSize = windowSize
|
||||
return None
|
||||
|
||||
def execCommand(self, protocol, cmd):
|
||||
def execCommand(self, proto, cmd):
|
||||
cfg = config()
|
||||
if not cfg.has_option('honeypot', 'exec_enabled') or \
|
||||
cfg.get('honeypot', 'exec_enabled').lower() not in \
|
||||
('yes', 'true', 'on'):
|
||||
log.msg( 'Exec disabled. Not executing command: "%s"' % cmd )
|
||||
raise core.exceptions.NotEnabledException, \
|
||||
raise exceptions.NotEnabledException, \
|
||||
'exec_enabled not enabled in configuration file!'
|
||||
return
|
||||
|
||||
log.msg( 'exec command: "%s"' % cmd )
|
||||
serverProtocol = kippo.core.protocol.LoggingServerProtocol(
|
||||
kippo.core.protocol.HoneyPotExecProtocol, self, self.env, cmd)
|
||||
serverProtocol.makeConnection(protocol)
|
||||
protocol.makeConnection(session.wrapProtocol(serverProtocol))
|
||||
serverProtocol = protocol.LoggingServerProtocol(
|
||||
protocol.HoneyPotExecProtocol, self, self.env, cmd)
|
||||
self.protocol = serverProtocol
|
||||
serverProtocol.makeConnection(proto)
|
||||
proto.makeConnection(session.wrapProtocol(serverProtocol))
|
||||
self.protocol = serverProtocol
|
||||
|
||||
# this is reliably called on both logout and disconnect
|
||||
# we notify the protocol here we lost the connection
|
||||
def closed(self):
|
||||
pass
|
||||
if self.protocol:
|
||||
self.protocol.connectionLost("disconnected")
|
||||
|
||||
def eofReceived(self):
|
||||
pass
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2010-2014 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
from kippo.core.config import config
|
||||
from config import config
|
||||
|
||||
def addToLastlog(message):
|
||||
f = file('%s/lastlog.txt' % config().get('honeypot', 'data_path'), 'a')
|
||||
|
|
|
@ -1,19 +1,44 @@
|
|||
# Copyright (c) 2015 Michel Oosterhof <michel@oosterhof.net>
|
||||
# All rights reserved.
|
||||
#
|
||||
# this module uses the dblog feature to create a JSON logfile
|
||||
# ..so not exactly a dblog.
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The names of the author(s) may not be used to endorse or promote
|
||||
# products derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from kippo.core import dblog
|
||||
from twisted.enterprise import adbapi
|
||||
from twisted.internet import defer
|
||||
from twisted.python import log
|
||||
from ..core import dblog
|
||||
|
||||
class DBLogger(dblog.DBLogger):
|
||||
|
||||
def __init__(self, cfg):
|
||||
self.sensor = ""
|
||||
self.outfile = ""
|
||||
dblog.DBLogger.__init__(self, cfg)
|
||||
|
||||
def start(self, cfg):
|
||||
self.outfile = file(cfg.get('database_jsonlog', 'logfile'), 'a')
|
||||
|
||||
|
@ -38,6 +63,9 @@ class DBLogger(dblog.DBLogger):
|
|||
def handleConnectionLost(self, session, args):
|
||||
logentry = { 'message': 'Connection lost' }
|
||||
self.write( session, logentry )
|
||||
#ttylog = self.ttylog(session)
|
||||
#if ttylog:
|
||||
# self.write( session, { 'message': repr(ttylog) } )
|
||||
|
||||
def handleLoginFailed(self, session, args):
|
||||
logentry = { 'message' : 'Login failed [%s/%s]' % (args['username'], args['password']), 'username' : args['username'], 'password' : args['password'] }
|
||||
|
@ -68,12 +96,7 @@ class DBLogger(dblog.DBLogger):
|
|||
self.write( session, logentry )
|
||||
|
||||
def handleFileDownload(self, session, args):
|
||||
logentry = { 'message' : 'File download: [%s] -> %s' % (args['url'], args['outfile']), 'url' : args['url'] }
|
||||
self.write( session, logentry )
|
||||
|
||||
def handleShaSum(self, session, args):
|
||||
logentry = { 'message' : 'File SHA sum: %s [%s] -> %s' % \
|
||||
(args['shasum'], args['url'], args['outfile']), 'shasum' : args['shasum'], 'url' : args['url'] }
|
||||
logentry = { 'message' : 'File download: [%s] -> %s' % (args['url'], args['outfile']), 'url' : args['url'], 'shasum' : args['shasum'] }
|
||||
self.write( session, logentry )
|
||||
|
||||
# vim: set sw=4 et:
|
||||
|
|
|
@ -142,13 +142,8 @@ class DBLogger(dblog.DBLogger):
|
|||
|
||||
def handleFileDownload(self, session, args):
|
||||
self.simpleQuery('INSERT INTO `downloads`' + \
|
||||
' (`session`, `timestamp`, `url`, `outfile`)' + \
|
||||
' (`session`, `timestamp`, `url`, `outfile`, `shasum`)' + \
|
||||
' VALUES (%s, FROM_UNIXTIME(%s), %s, %s)',
|
||||
(session, self.nowUnix(), args['url'], args['outfile']))
|
||||
|
||||
def handleShaSum(self, session, args):
|
||||
self.simpleQuery('UPDATE `downloads` SET `shasum` = %s' + \
|
||||
' WHERE `outfile` = %s',
|
||||
(args['shasum'], args['outfile']))
|
||||
(session, self.nowUnix(), args['url'], args['outfile'], args['shasum']))
|
||||
|
||||
# vim: set sw=4 et:
|
||||
|
|
|
@ -50,15 +50,7 @@ class DBLogger(dblog.DBLogger):
|
|||
self.write(session, 'Client version: [%s]' % (args['version'],))
|
||||
|
||||
def handleFileDownload(self, session, args):
|
||||
self.write(session, 'File download: [%s] -> %s' % \
|
||||
(args['url'], args['outfile']))
|
||||
|
||||
def handleShaSum(self, session, args):
|
||||
self.write(session, 'File SHA sum: %s [%s] -> %s' % \
|
||||
(args['shasum'], args['url'], args['outfile']))
|
||||
|
||||
def handleUpdatedFile(self, session, args):
|
||||
self.write(session, 'Updated wget outfile %s to %s' % \
|
||||
(args['outfile'], args['dl_file']))
|
||||
self.write(session, 'File download: [%s] -> %s with SHA-256 %s' % \
|
||||
(args['url'], args['outfile'], args['shasum']))
|
||||
|
||||
# vim: set sw=4 et:
|
||||
|
|
Loading…
Reference in New Issue