This commit is contained in:
Michel Oosterhof
2016-11-05 14:12:47 +00:00
16 changed files with 107934 additions and 107569 deletions

View File

@@ -59,15 +59,18 @@ cowrie_stop () {
fi
}
cowrie_usage() {
echo "usage: $0 <start|stop>"
}
################################################################################
## Main script
################################################################################
if [ "$#" = 0 ]
then
echo "Usage: $0 <command>"
echo 'Command can be "start" or "stop"'
exit 0
cowrie_usage
exit 1
fi
find_cowrie_directory $0
@@ -89,5 +92,9 @@ do
status)
cowrie_status
;;
*)
cowrie_usage
exit 1
;;
esac
done

View File

@@ -51,7 +51,7 @@ data_path = data
contents_path = honeyfs
# File in the python pickle format containing the virtual filesystem.
# File in the Python pickle format containing the virtual filesystem.
#
# This includes the filenames, paths, permissions for the Cowrie filesystem,
# but not the file contents. This is created by the bin/createfs utility from
@@ -358,9 +358,11 @@ logfile = log/cowrie.json
# MySQL logging module
# Identical functionality as [database_mysql] but with different internals
# Database structure for this module is supplied in doc/sql/mysql.sql
#
# MySQL logging requires extra software: sudo apt-get install libmysqlclient-dev
# MySQL logging requires an extra Python module: pip install mysql-python
#
#[output_mysql]
#host = localhost
#database = cowrie

View File

@@ -30,5 +30,6 @@ __all__ = [
'which',
'perl',
'uptime',
'python'
'python',
'tftp'
]

View File

@@ -139,7 +139,8 @@ class command_echo(HoneyPotCommand):
# FIXME: Wrap in exception, Python escape cannot handle single digit \x codes (e.g. \x1)
try:
self.write(escape_fn(re.sub('(?<=\\\\)x([0-9a-fA-F]{1})(?=\\\\|\"|\s|$)', 'x0\g<1>', ' '.join(args))))
self.write(escape_fn(re.sub('(?<=\\\\)x([0-9a-fA-F]{1})(?=\\\\|\"|\'|\s|$)', 'x0\g<1>',
' '.join(args))).strip('\"\''))
except ValueError as e:
log.msg("echo command received Python incorrect hex escape")

128
cowrie/commands/tftp.py Normal file
View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python
import time
import re
import tftpy
import os
from cowrie.core.customparser import CustomParser
from cowrie.core.customparser import OptionNotFound
from cowrie.core.customparser import ExitException
from cowrie.core.honeypot import HoneyPotCommand
from cowrie.core.fs import *
commands = {}
class Progress(object):
def __init__(self, protocol):
self.progress = 0
self.out = protocol
def progresshook(self, pkt):
if isinstance(pkt, tftpy.TftpPacketDAT):
self.progress += len(pkt.data)
self.out.write("Transferred %d bytes" % self.progress + "\n")
elif isinstance(pkt, tftpy.TftpPacketOACK):
self.out.write("Received OACK, options are: %s" % pkt.options + "\n")
class command_tftp(HoneyPotCommand):
port = 69
hostname = None
file_to_get = None
def makeTftpRetrieval(self):
progresshook = Progress(self).progresshook
tclient = tftpy.TftpClient(self.hostname,
int(self.port))
cfg = self.protocol.cfg
if cfg.has_option('honeypot', 'download_limit_size'):
self.limit_size = int(cfg.get('honeypot', 'download_limit_size'))
self.download_path = cfg.get('honeypot', 'download_path')
self.safeoutfile = '%s/%s_%s' % \
(self.download_path,
time.strftime('%Y%m%d%H%M%S'),
re.sub('[^A-Za-z0-9]', '_', self.file_to_get))
self.protocol.logDispatch(eventid='cowrie.session.file_download',
format='Downloaded tftpFile to %(outfile)s',
outfile=self.safeoutfile
)
log.msg(eventid='cowrie.session.file_download',
format='Downloaded tftpFile to %(outfile)s',
outfile=self.safeoutfile
)
try:
tclient.download(self.file_to_get, self.safeoutfile, progresshook)
self.file_to_get = self.fs.resolve_path(self.file_to_get, self.protocol.cwd)
self.fs.mkfile(self.file_to_get, 0, 0, tclient.context.metrics.bytes, 33188)
self.fs.update_realfile(self.fs.getfile(self.file_to_get), self.safeoutfile)
f = self.fs.getfile(self.file_to_get)
f[A_REALFILE] = self.safeoutfile
except tftpy.TftpException, err:
return
except KeyboardInterrupt:
pass
def start(self):
parser = CustomParser(self)
parser.prog = "tftp"
parser.add_argument("hostname", nargs='?', default=None)
parser.add_argument("-c", nargs=2)
parser.add_argument("-l")
parser.add_argument("-g")
parser.add_argument("-p")
parser.add_argument("-r")
try:
args = parser.parse_args(self.args)
if args.c:
if len(args.c) > 1:
command = args.c[0]
self.file_to_get = args.c[1]
if args.hostname is None:
raise OptionNotFound("Hostname is invalid")
self.hostname = args.hostname
elif args.r:
self.file_to_get = args.r
self.hostname = args.g
else:
parser.print_usage()
raise OptionNotFound("Missing!!")
if self.hostname is None:
raise OptionNotFound("Hostname is invalid")
self.makeTftpRetrieval()
except OptionNotFound:
self.exit()
return
except ExitException:
self.exit()
return
except Exception:
self.exit()
return
self.exit()
commands['/usr/bin/tftp'] = command_tftp

View File

@@ -256,8 +256,11 @@ class HTTPProgressDownloader(client.HTTPDownloader):
if self.status == '304':
client.HTTPDownloader.page(self, '')
else:
reason.webStatus = self.status
reason.webMessage = self.message
if hasattr(self, 'status'):
reason.webStatus = self.status
if hasattr(self, 'message'):
reason.webMessage = self.message
client.HTTPDownloader.noPage(self, reason)

93
cowrie/core/artifact.py Normal file
View File

@@ -0,0 +1,93 @@
# Copyright (c) 2016 Michel Oosterhof <michel@oosterhof.net>
"""
This module contains code to handling saving of honeypot artifacts
These will typically be files uploaded to the honeypot and files
downloaded inside the honeypot, or input being piped in.
Code behaves like a normal Python file handle.
Example:
with Artifact(name) as f:
f.write("abc")
or:
g = Artifact("testme2")
g.write( "def" )
g.close()
"""
import hashlib
import os
import re
import time
import tempfile
from twisted.python import log
class Artifact:
"""
"""
def __init__(self, cfg, label):
"""
"""
self.label = label
self.artifactDir = cfg.get('honeypot', 'download_path')
self.fp = tempfile.NamedTemporaryFile(dir=self.artifactDir, delete=False)
self.tempFilename = self.fp.name
def __enter__(self):
"""
"""
return self.fp
def __exit__(self, exception_type, exception_value, trace):
"""
"""
self.close()
def write(self, bytes):
"""
"""
self.fp.write(bytes)
def fileno(self):
"""
"""
return self.fp.fileno()
def close(self, keepEmpty=True):
"""
"""
size = self.fp.tell()
self.fp.seek(0)
shasum = hashlib.sha256(self.fp.read()).hexdigest()
self.fp.close()
shasumFilename = self.artifactDir + "/" + shasum
if size == 0 and keepEmpty == False:
os.remove(self.fp.name)
elif os.path.exists(shasumFilename):
os.remove(self.fp.name)
else:
os.rename(self.fp.name, shasumFilename)
if size>0:
linkName = self.artifactDir + "/" \
+ time.strftime('%Y%m%dT%H%M%S') \
+ "_" + re.sub('[^-A-Za-z0-9]', '_', self.label)
os.symlink(shasum, linkName)
return shasum, shasumFilename

View File

@@ -0,0 +1,59 @@
import argparse
class OptionNotFound(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class ExitException(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class CustomParser(argparse.ArgumentParser):
def __init__(self, protocol,
prog=None,
usage=None,
description=None,
epilog=None,
version=None,
parents=[],
formatter_class=argparse.HelpFormatter,
prefix_chars='-',
fromfile_prefix_chars=None,
argument_default=None,
conflict_handler='error',
add_help=True):
self.protocol = protocol
super(CustomParser, self).__init__(prog,
usage,
description,
epilog,
version,
parents,
formatter_class,
prefix_chars,
fromfile_prefix_chars,
argument_default,
conflict_handler,
add_help)
def exit(self, status=0, message=None):
raise ExitException("Exiting...")
def _print_message(self, message, file=None):
super(CustomParser,self)._print_message(message, self.protocol)
def error(self, message):
self.print_usage(self.protocol)
raise OptionNotFound("Sorry no option found")

View File

@@ -222,8 +222,6 @@ class HoneyPotExecProtocol(HoneyPotBaseProtocol):
"""
HoneyPotBaseProtocol.connectionMade(self)
self.setTimeout(60)
self.terminal.stdinlog_open = True
self.cmdstack = [honeypot.HoneyPotShell(self, interactive=False)]
self.cmdstack[0].lineReceived(self.execcmd)

View File

@@ -68,7 +68,11 @@ class LoggingServerProtocol(insults.ServerProtocol):
self.stdinlogFile = '%s/%s-%s-%s-stdin.log' % \
(self.downloadPath,
time.strftime('%Y%m%d-%H%M%S'), transportId, channelId)
self.stdinlogOpen = False
if self.type == 'e':
self.stdinlogOpen = True
else: #i
self.stdinlogOpen = False
insults.ServerProtocol.connectionMade(self)

View File

@@ -78,7 +78,7 @@
"root@unitTest:~# "
],
"echo": [
"\"test worked correctly\"",
"test worked correctly",
"root@unitTest:~# "
],
"hostname": [

File diff suppressed because it is too large Load Diff

View File

@@ -6,3 +6,4 @@ gmpy2
service_identity
pycrypto
python-dateutil
tftpy

0
share/.gitignore vendored Normal file
View File

0
share/cowrie/.gitignore vendored Normal file
View File

58
txtcmds/bin/enable Normal file
View File

@@ -0,0 +1,58 @@
enable .
enable :
enable [
enable alias
enable bg
enable bind
enable break
enable builtin
enable caller
enable cd
enable command
enable compgen
enable complete
enable continue
enable declare
enable dirs
enable disown
enable echo
enable enable
enable eval
enable exec
enable exit
enable export
enable false
enable fc
enable fg
enable getopts
enable hash
enable help
enable history
enable jobs
enable kill
enable let
enable local
enable logout
enable popd
enable printf
enable pushd
enable pwd
enable read
enable readonly
enable return
enable set
enable shift
enable shopt
enable source
enable suspend
enable test
enable times
enable trap
enable true
enable type
enable typeset
enable ulimit
enable umask
enable unalias
enable unset
enable wait