mirror of
https://github.com/cowrie/cowrie.git
synced 2025-07-01 18:07:27 -04:00
Merge branch 'master' of http://www.github.com/micheloosterhof/cowrie
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -15,4 +15,5 @@ __pycache__/
|
|||||||
twisted/plugins/dropin.cache
|
twisted/plugins/dropin.cache
|
||||||
.DS_Store
|
.DS_Store
|
||||||
_trial_temp
|
_trial_temp
|
||||||
|
.project
|
||||||
|
.pydevproject
|
||||||
|
|||||||
@ -286,6 +286,19 @@ forward_redirect = false
|
|||||||
# forward_redirect_587 = 127.0.0.1:12525
|
# forward_redirect_587 = 127.0.0.1:12525
|
||||||
|
|
||||||
|
|
||||||
|
# This enables tunneling forwarding requests to another address
|
||||||
|
# Useful for forwarding protocols to a proxy like Squid
|
||||||
|
# (default: false)
|
||||||
|
forward_tunnel = false
|
||||||
|
|
||||||
|
|
||||||
|
# Configure where to tunnel the data to.
|
||||||
|
# forward_tunnel_<portnumber> = <tunnel ip>:<tunnel port>
|
||||||
|
|
||||||
|
# Tunnel http/https
|
||||||
|
# forward_tunnel_80 = 127.0.0.1:3128
|
||||||
|
# forward_tunnel_443 = 127.0.0.1:3128
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Telnet Specific Options
|
# Telnet Specific Options
|
||||||
@ -531,7 +544,9 @@ logfile = log/cowrie.json
|
|||||||
#
|
#
|
||||||
#[output_s3]
|
#[output_s3]
|
||||||
#
|
#
|
||||||
# The AWS credentials to use
|
# The AWS credentials to use.
|
||||||
|
# Leave these blank to use botocore's credential discovery e.g .aws/config or ENV variables.
|
||||||
|
# As per https://github.com/boto/botocore/blob/develop/botocore/credentials.py#L50-L65
|
||||||
#access_key_id = AKIDEXAMPLE
|
#access_key_id = AKIDEXAMPLE
|
||||||
#secret_access_key = wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY
|
#secret_access_key = wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY
|
||||||
#
|
#
|
||||||
@ -558,7 +573,21 @@ logfile = log/cowrie.json
|
|||||||
|
|
||||||
|
|
||||||
#[output_redis]
|
#[output_redis]
|
||||||
|
# Host of the redis server. Required
|
||||||
#host = 127.0.0.1
|
#host = 127.0.0.1
|
||||||
|
|
||||||
|
# Port of the redis server. Required
|
||||||
#port = 6379
|
#port = 6379
|
||||||
|
|
||||||
|
# DB of the redis server. Defaults to 0
|
||||||
#db = 0
|
#db = 0
|
||||||
|
|
||||||
|
# Password of the redis server. Defaults to None
|
||||||
|
#password = secret
|
||||||
|
|
||||||
|
# Name of the list to push to or the channel to publish to. Required
|
||||||
#keyname = cowrie
|
#keyname = cowrie
|
||||||
|
|
||||||
|
# Method to use when sending data to redis.
|
||||||
|
# Can be one of [lpush, rpush, publish]. Defaults to lpush
|
||||||
|
#send_method = lpush
|
||||||
|
|||||||
@ -5,6 +5,7 @@ __all__ = [
|
|||||||
'adduser',
|
'adduser',
|
||||||
'apt',
|
'apt',
|
||||||
'base',
|
'base',
|
||||||
|
'base64',
|
||||||
'busybox',
|
'busybox',
|
||||||
'curl',
|
'curl',
|
||||||
'dd',
|
'dd',
|
||||||
|
|||||||
@ -140,7 +140,6 @@ class command_echo(HoneyPotCommand):
|
|||||||
except:
|
except:
|
||||||
args = self.args
|
args = self.args
|
||||||
|
|
||||||
# FIXME: Wrap in exception, Python escape cannot handle single digit \x codes (e.g. \x1)
|
|
||||||
try:
|
try:
|
||||||
# replace r'\\x' with r'\x'
|
# replace r'\\x' with r'\x'
|
||||||
s = ' '.join(args).replace('\\\\x', '\\x')
|
s = ' '.join(args).replace('\\\\x', '\\x')
|
||||||
@ -156,13 +155,14 @@ class command_echo(HoneyPotCommand):
|
|||||||
s = s[:-2]
|
s = s[:-2]
|
||||||
newline = False
|
newline = False
|
||||||
|
|
||||||
|
if newline is True:
|
||||||
|
s += '\n'
|
||||||
|
|
||||||
self.write(escape_fn(s))
|
self.write(escape_fn(s))
|
||||||
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
log.msg("echo command received Python incorrect hex escape")
|
log.msg("echo command received Python incorrect hex escape")
|
||||||
|
|
||||||
if newline is True:
|
|
||||||
self.write('\n')
|
|
||||||
|
|
||||||
commands['/bin/echo'] = command_echo
|
commands['/bin/echo'] = command_echo
|
||||||
|
|
||||||
@ -705,4 +705,4 @@ commands['/bin/chgrp'] = command_nop
|
|||||||
commands['/usr/bin/chattr'] = command_nop
|
commands['/usr/bin/chattr'] = command_nop
|
||||||
commands[':'] = command_nop
|
commands[':'] = command_nop
|
||||||
|
|
||||||
# vim: set sw=4 et:
|
# vim: set sw=4 et:
|
||||||
126
cowrie/commands/base64.py
Normal file
126
cowrie/commands/base64.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
from __future__ import division, absolute_import
|
||||||
|
|
||||||
|
import getopt
|
||||||
|
|
||||||
|
from twisted.python import log
|
||||||
|
|
||||||
|
from cowrie.shell.honeypot import HoneyPotCommand
|
||||||
|
|
||||||
|
commands = {}
|
||||||
|
|
||||||
|
|
||||||
|
class command_base64(HoneyPotCommand):
|
||||||
|
"""
|
||||||
|
author: Ivan Korolev (@fe7ch)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.mode = 'e'
|
||||||
|
self.ignore = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
optlist, args = getopt.getopt(self.args, 'diw:', ['version', 'help', 'decode', 'ignore-garbage', 'wrap='])
|
||||||
|
except getopt.GetoptError as err:
|
||||||
|
self.errorWrite('Unrecognized option\n')
|
||||||
|
self.exit()
|
||||||
|
return
|
||||||
|
|
||||||
|
for opt in optlist:
|
||||||
|
if opt[0] == '--help':
|
||||||
|
self.write("""Usage: base64 [OPTION]... [FILE]
|
||||||
|
Base64 encode or decode FILE, or standard input, to standard output.
|
||||||
|
|
||||||
|
Mandatory arguments to long options are mandatory for short options too.
|
||||||
|
-d, --decode decode data
|
||||||
|
-i, --ignore-garbage when decoding, ignore non-alphabet characters
|
||||||
|
-w, --wrap=COLS wrap encoded lines after COLS character (default 76).
|
||||||
|
Use 0 to disable line wrapping
|
||||||
|
|
||||||
|
--help display this help and exit
|
||||||
|
--version output version information and exit
|
||||||
|
|
||||||
|
With no FILE, or when FILE is -, read standard input.
|
||||||
|
|
||||||
|
The data are encoded as described for the base64 alphabet in RFC 3548.
|
||||||
|
When decoding, the input may contain newlines in addition to the bytes of
|
||||||
|
the formal base64 alphabet. Use --ignore-garbage to attempt to recover
|
||||||
|
from any other non-alphabet bytes in the encoded stream.
|
||||||
|
|
||||||
|
Report base64 bugs to bug-coreutils@gnu.org
|
||||||
|
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
|
||||||
|
General help using GNU software: <http://www.gnu.org/gethelp/>
|
||||||
|
For complete documentation, run: info coreutils 'base64 invocation'
|
||||||
|
""")
|
||||||
|
self.exit()
|
||||||
|
return
|
||||||
|
elif opt[0] == '--version':
|
||||||
|
self.write("""base64 (GNU coreutils) 8.21
|
||||||
|
Copyright (C) 2013 Free Software Foundation, Inc.
|
||||||
|
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
|
||||||
|
This is free software: you are free to change and redistribute it.
|
||||||
|
There is NO WARRANTY, to the extent permitted by law.
|
||||||
|
|
||||||
|
Written by Simon Josefsson.
|
||||||
|
""")
|
||||||
|
self.exit()
|
||||||
|
return
|
||||||
|
elif opt[0] == '-d' or opt[0] == '--decode':
|
||||||
|
self.mode = 'd'
|
||||||
|
|
||||||
|
elif opt[0] == '-i' or opt[0] == '--ignore-garbage':
|
||||||
|
self.ignore = True
|
||||||
|
|
||||||
|
elif opt[0] == '-w' or opt[0] == 'wrap':
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self.input_data:
|
||||||
|
self.dojob(self.input_data)
|
||||||
|
else:
|
||||||
|
if len(args) > 1:
|
||||||
|
self.errorWrite(
|
||||||
|
"""base64: extra operand '%s'
|
||||||
|
Try 'base64 --help' for more information.
|
||||||
|
""" % args[0])
|
||||||
|
self.exit()
|
||||||
|
return
|
||||||
|
|
||||||
|
pname = self.fs.resolve_path(args[0], self.protocol.cwd)
|
||||||
|
if not self.fs.isdir(pname):
|
||||||
|
try:
|
||||||
|
self.dojob(self.fs.file_contents(pname))
|
||||||
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
|
self.errorWrite('base64: {}: No such file or directory\n'.format(args[0]))
|
||||||
|
else:
|
||||||
|
self.errorWrite('base64: read error: Is a directory\n')
|
||||||
|
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def dojob(self, s):
|
||||||
|
if self.ignore:
|
||||||
|
s = ''.join([i for i in s if i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='])
|
||||||
|
|
||||||
|
if self.mode == 'e':
|
||||||
|
self.write(s.encode('base64'))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.write(s.decode('base64'))
|
||||||
|
except:
|
||||||
|
self.errorWrite("base64: invalid input\n")
|
||||||
|
|
||||||
|
def lineReceived(self, line):
|
||||||
|
log.msg(eventid='cowrie.session.input',
|
||||||
|
realm='base64',
|
||||||
|
input=line,
|
||||||
|
format='INPUT (%(realm)s): %(input)s')
|
||||||
|
|
||||||
|
self.dojob(line)
|
||||||
|
|
||||||
|
def handle_CTRL_D(self):
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
|
||||||
|
commands['/usr/bin/base64'] = command_base64
|
||||||
@ -11,11 +11,10 @@ from cowrie.core.config import CONFIG
|
|||||||
|
|
||||||
commands = {}
|
commands = {}
|
||||||
|
|
||||||
|
|
||||||
class command_last(HoneyPotCommand):
|
class command_last(HoneyPotCommand):
|
||||||
|
|
||||||
def call(self):
|
def call(self):
|
||||||
fn = '%s/lastlog.txt' % CONFIG.get('honeypot', 'log_path')
|
|
||||||
if not os.path.exists(fn):
|
|
||||||
return
|
|
||||||
l = list(self.args)
|
l = list(self.args)
|
||||||
numlines = 25
|
numlines = 25
|
||||||
while len(l):
|
while len(l):
|
||||||
@ -26,8 +25,16 @@ class command_last(HoneyPotCommand):
|
|||||||
numlines = int(arg[1:])
|
numlines = int(arg[1:])
|
||||||
elif arg == '-n' and len(l) and l[0].isdigit():
|
elif arg == '-n' and len(l) and l[0].isdigit():
|
||||||
numlines = int(l.pop(0))
|
numlines = int(l.pop(0))
|
||||||
data = utils.tail(file(fn), numlines)
|
|
||||||
self.write(''.join(data))
|
self.write('%-8s %-12s %-16s %s still logged in\n' % \
|
||||||
|
(self.protocol.user.username, "pts/0", self.protocol.clientIP,
|
||||||
|
time.strftime('%a %b %d %H:%M', time.localtime(self.protocol.logintime)) ))
|
||||||
|
|
||||||
|
self.write("\n")
|
||||||
|
self.write("wtmp begins %s\n" % \
|
||||||
|
time.strftime('%a %b %d %H:%M:%S %Y', time.localtime(self.protocol.logintime // (3600*24) * (3600*24) + 63 )) )
|
||||||
|
|
||||||
|
|
||||||
commands['/usr/bin/last'] = command_last
|
commands['/usr/bin/last'] = command_last
|
||||||
|
|
||||||
# vim: set sw=4 et:
|
# vim: set sw=4 et:
|
||||||
|
|||||||
@ -19,6 +19,7 @@ from twisted.python import log, compat
|
|||||||
from cowrie.shell.honeypot import HoneyPotCommand
|
from cowrie.shell.honeypot import HoneyPotCommand
|
||||||
from cowrie.shell.fs import *
|
from cowrie.shell.fs import *
|
||||||
|
|
||||||
|
from cowrie.core.artifact import Artifact
|
||||||
from cowrie.core.config import CONFIG
|
from cowrie.core.config import CONFIG
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -120,6 +121,7 @@ class command_wget(HoneyPotCommand):
|
|||||||
if not path or not self.fs.exists(path) or not self.fs.isdir(path):
|
if not path or not self.fs.exists(path) or not self.fs.isdir(path):
|
||||||
self.errorWrite('wget: %s: Cannot open: No such file or directory\n' % outfile)
|
self.errorWrite('wget: %s: Cannot open: No such file or directory\n' % outfile)
|
||||||
self.exit()
|
self.exit()
|
||||||
|
return
|
||||||
|
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
@ -128,9 +130,8 @@ class command_wget(HoneyPotCommand):
|
|||||||
self.limit_size = CONFIG.getint('honeypot', 'download_limit_size')
|
self.limit_size = CONFIG.getint('honeypot', 'download_limit_size')
|
||||||
self.downloadPath = CONFIG.get('honeypot', 'download_path')
|
self.downloadPath = CONFIG.get('honeypot', 'download_path')
|
||||||
|
|
||||||
self.artifactFile = tempfile.NamedTemporaryFile(dir=self.downloadPath, delete=False)
|
self.artifactFile = Artifact(outfile)
|
||||||
# HTTPDownloader will close() the file object so need to preserve the name
|
# HTTPDownloader will close() the file object so need to preserve the name
|
||||||
self.artifactName = self.artifactFile.name
|
|
||||||
|
|
||||||
d = self.download(url, outfile, self.artifactFile)
|
d = self.download(url, outfile, self.artifactFile)
|
||||||
if d:
|
if d:
|
||||||
@ -139,7 +140,6 @@ class command_wget(HoneyPotCommand):
|
|||||||
else:
|
else:
|
||||||
self.exit()
|
self.exit()
|
||||||
|
|
||||||
|
|
||||||
def download(self, url, fakeoutfile, outputfile, *args, **kwargs):
|
def download(self, url, fakeoutfile, outputfile, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
url - URL to download
|
url - URL to download
|
||||||
@ -182,49 +182,42 @@ class command_wget(HoneyPotCommand):
|
|||||||
|
|
||||||
return factory.deferred
|
return factory.deferred
|
||||||
|
|
||||||
|
|
||||||
def handle_CTRL_C(self):
|
def handle_CTRL_C(self):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
self.errorWrite('^C\n')
|
self.errorWrite('^C\n')
|
||||||
self.connection.transport.loseConnection()
|
self.connection.transport.loseConnection()
|
||||||
|
|
||||||
|
|
||||||
def success(self, data, outfile):
|
def success(self, data, outfile):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
if not os.path.isfile(self.artifactName):
|
if not os.path.isfile(self.artifactFile.shasumFilename):
|
||||||
log.msg("there's no file " + self.artifactName)
|
log.msg("there's no file " + self.artifactFile.shasumFilename)
|
||||||
self.exit()
|
self.exit()
|
||||||
|
|
||||||
with open(self.artifactName, 'rb') as f:
|
# log to cowrie.log
|
||||||
filedata = f.read()
|
log.msg(format='Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s',
|
||||||
shasum = hashlib.sha256(filedata).hexdigest()
|
url=self.url,
|
||||||
hash_path = os.path.join(self.downloadPath, shasum)
|
outfile=self.artifactFile.shasumFilename,
|
||||||
|
shasum=self.artifactFile.shasum)
|
||||||
# Rename temp file to the hash
|
|
||||||
if not os.path.exists(hash_path):
|
|
||||||
os.rename(self.artifactName, hash_path)
|
|
||||||
unique = True
|
|
||||||
else:
|
|
||||||
os.remove(self.artifactName)
|
|
||||||
log.msg("Not storing duplicate content " + shasum)
|
|
||||||
unique = False
|
|
||||||
|
|
||||||
|
# log to output modules
|
||||||
self.protocol.logDispatch(eventid='cowrie.session.file_download',
|
self.protocol.logDispatch(eventid='cowrie.session.file_download',
|
||||||
format='Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s',
|
format='Downloaded URL (%(url)s) with SHA-256 %(shasum)s to %(outfile)s',
|
||||||
url=self.url, outfile=hash_path, shasum=shasum, unique=unique)
|
url=self.url,
|
||||||
|
outfile=self.artifactFile.shasumFilename,
|
||||||
|
shasum=self.artifactFile.shasum)
|
||||||
|
|
||||||
# Update honeyfs to point to downloaded file or write to screen
|
# Update honeyfs to point to downloaded file or write to screen
|
||||||
if outfile != '-':
|
if outfile != '-':
|
||||||
self.fs.update_realfile(self.fs.getfile(outfile), hash_path)
|
self.fs.update_realfile(self.fs.getfile(outfile), self.artifactFile.shasumFilename)
|
||||||
self.fs.chown(outfile, self.protocol.user.uid, self.protocol.user.gid)
|
self.fs.chown(outfile, self.protocol.user.uid, self.protocol.user.gid)
|
||||||
else:
|
else:
|
||||||
self.write(filedata)
|
with open(self.artifactFile.shasumFilename, 'rb') as f:
|
||||||
|
self.write(f.read())
|
||||||
|
|
||||||
self.exit()
|
self.exit()
|
||||||
|
|
||||||
|
|
||||||
def error(self, error, url):
|
def error(self, error, url):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
@ -233,14 +226,22 @@ class command_wget(HoneyPotCommand):
|
|||||||
self.errorWrite(errorMessage + '\n')
|
self.errorWrite(errorMessage + '\n')
|
||||||
# Real wget also adds this:
|
# Real wget also adds this:
|
||||||
if hasattr(error, 'webStatus') and hasattr(error, 'webMessage'): # exceptions
|
if hasattr(error, 'webStatus') and hasattr(error, 'webMessage'): # exceptions
|
||||||
self.errorWrite('{} ERROR {}: {}\n'.format(time.strftime('%Y-%m-%d %T'), error.webStatus.decode(), error.webMessage.decode()))
|
self.errorWrite('{} ERROR {}: {}\n'.format(time.strftime('%Y-%m-%d %T'), error.webStatus.decode(),
|
||||||
|
error.webMessage.decode()))
|
||||||
else:
|
else:
|
||||||
self.errorWrite('{} ERROR 404: Not Found.\n'.format(time.strftime('%Y-%m-%d %T')))
|
self.errorWrite('{} ERROR 404: Not Found.\n'.format(time.strftime('%Y-%m-%d %T')))
|
||||||
self.protocol.logDispatch(eventid='cowrie.session.file_download.failed',
|
|
||||||
format='Attempt to download file(s) from URL (%(url)s) failed',
|
# prevent cowrie from crashing if the terminal have been already destroyed
|
||||||
url=self.url)
|
try:
|
||||||
|
self.protocol.logDispatch(eventid='cowrie.session.file_download.failed',
|
||||||
|
format='Attempt to download file(s) from URL (%(url)s) failed',
|
||||||
|
url=self.url)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
self.exit()
|
self.exit()
|
||||||
|
|
||||||
|
|
||||||
commands['/usr/bin/wget'] = command_wget
|
commands['/usr/bin/wget'] = command_wget
|
||||||
commands['/usr/bin/dget'] = command_wget
|
commands['/usr/bin/dget'] = command_wget
|
||||||
|
|
||||||
@ -258,7 +259,6 @@ class HTTPProgressDownloader(client.HTTPDownloader):
|
|||||||
self.nomore = False
|
self.nomore = False
|
||||||
self.quiet = self.wget.quiet
|
self.quiet = self.wget.quiet
|
||||||
|
|
||||||
|
|
||||||
def noPage(self, reason): # Called for non-200 responses
|
def noPage(self, reason): # Called for non-200 responses
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
@ -272,7 +272,6 @@ class HTTPProgressDownloader(client.HTTPDownloader):
|
|||||||
|
|
||||||
client.HTTPDownloader.noPage(self, reason)
|
client.HTTPDownloader.noPage(self, reason)
|
||||||
|
|
||||||
|
|
||||||
def gotHeaders(self, headers):
|
def gotHeaders(self, headers):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
@ -292,9 +291,9 @@ class HTTPProgressDownloader(client.HTTPDownloader):
|
|||||||
if self.totallength > 0:
|
if self.totallength > 0:
|
||||||
if not self.quiet:
|
if not self.quiet:
|
||||||
self.wget.errorWrite('Length: %d (%s) [%s]\n' % \
|
self.wget.errorWrite('Length: %d (%s) [%s]\n' % \
|
||||||
(self.totallength,
|
(self.totallength,
|
||||||
sizeof_fmt(self.totallength),
|
sizeof_fmt(self.totallength),
|
||||||
self.contenttype))
|
self.contenttype))
|
||||||
else:
|
else:
|
||||||
if not self.quiet:
|
if not self.quiet:
|
||||||
self.wget.errorWrite('Length: unspecified [{}]\n'.format(self.contenttype))
|
self.wget.errorWrite('Length: unspecified [{}]\n'.format(self.contenttype))
|
||||||
@ -309,7 +308,6 @@ class HTTPProgressDownloader(client.HTTPDownloader):
|
|||||||
|
|
||||||
return client.HTTPDownloader.gotHeaders(self, headers)
|
return client.HTTPDownloader.gotHeaders(self, headers)
|
||||||
|
|
||||||
|
|
||||||
def pagePart(self, data):
|
def pagePart(self, data):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
@ -348,17 +346,17 @@ class HTTPProgressDownloader(client.HTTPDownloader):
|
|||||||
if self.totallength != 0 and self.currentlength != self.totallength:
|
if self.totallength != 0 and self.currentlength != self.totallength:
|
||||||
return client.HTTPDownloader.pageEnd(self)
|
return client.HTTPDownloader.pageEnd(self)
|
||||||
if not self.quiet:
|
if not self.quiet:
|
||||||
self.wget.errorWrite('\r100%%[%s] %s %dK/s' % \
|
self.wget.errorWrite('\r100%%[%s] %s %dK/s' %
|
||||||
('%s>' % (38 * '='),
|
('%s>' % (38 * '='),
|
||||||
splitthousands(str(int(self.totallength))).ljust(12),
|
splitthousands(str(int(self.totallength))).ljust(12),
|
||||||
self.speed / 1000))
|
self.speed / 1000))
|
||||||
self.wget.errorWrite('\n\n')
|
self.wget.errorWrite('\n\n')
|
||||||
self.wget.errorWrite(
|
self.wget.errorWrite(
|
||||||
'%s (%d KB/s) - `%s\' saved [%d/%d]\n\n' % \
|
'%s (%d KB/s) - `%s\' saved [%d/%d]\n\n' %
|
||||||
(time.strftime('%Y-%m-%d %H:%M:%S'),
|
(time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
self.speed / 1000,
|
self.speed / 1000,
|
||||||
self.fakeoutfile, self.currentlength, self.totallength))
|
self.fakeoutfile, self.currentlength, self.totallength))
|
||||||
|
if self.fakeoutfile != '-':
|
||||||
self.wget.fs.mkfile(self.fakeoutfile, 0, 0, self.totallength, 33188)
|
self.wget.fs.mkfile(self.fakeoutfile, 0, 0, self.totallength, 33188)
|
||||||
|
|
||||||
return client.HTTPDownloader.pageEnd(self)
|
return client.HTTPDownloader.pageEnd(self)
|
||||||
|
|||||||
@ -24,14 +24,13 @@ from __future__ import division, absolute_import
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import time
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from twisted.python import log
|
from twisted.python import log
|
||||||
|
|
||||||
from cowrie.core.config import CONFIG
|
from cowrie.core.config import CONFIG
|
||||||
|
|
||||||
|
|
||||||
class Artifact:
|
class Artifact:
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
@ -45,6 +44,8 @@ class Artifact:
|
|||||||
self.fp = tempfile.NamedTemporaryFile(dir=self.artifactDir, delete=False)
|
self.fp = tempfile.NamedTemporaryFile(dir=self.artifactDir, delete=False)
|
||||||
self.tempFilename = self.fp.name
|
self.tempFilename = self.fp.name
|
||||||
|
|
||||||
|
self.shasum = ''
|
||||||
|
self.shasumFilename = ''
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
"""
|
"""
|
||||||
@ -75,26 +76,23 @@ class Artifact:
|
|||||||
"""
|
"""
|
||||||
size = self.fp.tell()
|
size = self.fp.tell()
|
||||||
self.fp.seek(0)
|
self.fp.seek(0)
|
||||||
shasum = hashlib.sha256(self.fp.read()).hexdigest()
|
data = self.fp.read()
|
||||||
self.fp.close()
|
self.fp.close()
|
||||||
shasumFilename = self.artifactDir + "/" + shasum
|
self.shasum = hashlib.sha256(data).hexdigest()
|
||||||
|
self.shasumFilename = os.path.join(self.artifactDir, self.shasum)
|
||||||
|
|
||||||
if size == 0 and keepEmpty == False:
|
if size == 0 and not keepEmpty:
|
||||||
|
log.msg("Not storing empty file")
|
||||||
os.remove(self.fp.name)
|
os.remove(self.fp.name)
|
||||||
elif os.path.exists(shasumFilename):
|
elif os.path.exists(self.shasumFilename):
|
||||||
|
log.msg("Not storing duplicate content " + self.shasum)
|
||||||
os.remove(self.fp.name)
|
os.remove(self.fp.name)
|
||||||
else:
|
else:
|
||||||
os.rename(self.fp.name, shasumFilename)
|
os.rename(self.fp.name, self.shasumFilename)
|
||||||
umask = os.umask(0)
|
umask = os.umask(0)
|
||||||
os.umask(umask)
|
os.umask(umask)
|
||||||
os.chmod(shasumFilename, 0o666 & ~umask)
|
os.chmod(self.shasumFilename, 0o666 & ~umask)
|
||||||
|
|
||||||
# if size>0:
|
return self.shasum, self.shasumFilename
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import division, absolute_import
|
from __future__ import division, absolute_import
|
||||||
|
|
||||||
import pyes
|
from elasticsearch import Elasticsearch
|
||||||
|
|
||||||
import cowrie.core.output
|
import cowrie.core.output
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ class Output(cowrie.core.output.Output):
|
|||||||
"""
|
"""
|
||||||
self.host = CONFIG.get('output_elasticsearch', 'host')
|
self.host = CONFIG.get('output_elasticsearch', 'host')
|
||||||
self.port = CONFIG.get('output_elasticsearch', 'port')
|
self.port = CONFIG.get('output_elasticsearch', 'port')
|
||||||
self.index =CONFIGg.get('output_elasticsearch', 'index')
|
self.index =CONFIG.get('output_elasticsearch', 'index')
|
||||||
self.type = CONFIG.get('output_elasticsearch', 'type')
|
self.type = CONFIG.get('output_elasticsearch', 'type')
|
||||||
cowrie.core.output.Output.__init__(self)
|
cowrie.core.output.Output.__init__(self)
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class Output(cowrie.core.output.Output):
|
|||||||
def start(self):
|
def start(self):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
self.es = pyes.ES('{0}:{1}'.format(self.host, self.port))
|
self.es = Elasticsearch('{0}:{1}'.format(self.host, self.port))
|
||||||
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
@ -44,5 +44,4 @@ class Output(cowrie.core.output.Output):
|
|||||||
if i.startswith('log_'):
|
if i.startswith('log_'):
|
||||||
del logentry[i]
|
del logentry[i]
|
||||||
|
|
||||||
self.es.index(logentry, self.index, self.type)
|
self.es.index(index=self.index, doc_type=self.type, body=logentry)
|
||||||
|
|
||||||
|
|||||||
@ -158,6 +158,13 @@ class Output(cowrie.core.output.Output):
|
|||||||
' VALUES (%s, FROM_UNIXTIME(%s), %s, %s, %s)',
|
' VALUES (%s, FROM_UNIXTIME(%s), %s, %s, %s)',
|
||||||
(entry["session"], entry["time"],
|
(entry["session"], entry["time"],
|
||||||
entry['url'], entry['outfile'], entry['shasum']))
|
entry['url'], entry['outfile'], entry['shasum']))
|
||||||
|
|
||||||
|
elif entry["eventid"] == 'cowrie.session.file_download.failed':
|
||||||
|
self.simpleQuery('INSERT INTO `downloads`' + \
|
||||||
|
' (`session`, `timestamp`, `url`, `outfile`, `shasum`)' + \
|
||||||
|
' VALUES (%s, FROM_UNIXTIME(%s), %s, %s, %s)',
|
||||||
|
(entry["session"], entry["time"],
|
||||||
|
entry['url'], 'NULL', 'NULL'))
|
||||||
|
|
||||||
elif entry["eventid"] == 'cowrie.session.file_upload':
|
elif entry["eventid"] == 'cowrie.session.file_upload':
|
||||||
self.simpleQuery('INSERT INTO `downloads`' + \
|
self.simpleQuery('INSERT INTO `downloads`' + \
|
||||||
|
|||||||
@ -5,6 +5,13 @@ from cowrie.core.config import CONFIG
|
|||||||
|
|
||||||
import redis
|
import redis
|
||||||
import json
|
import json
|
||||||
|
from ConfigParser import NoOptionError
|
||||||
|
|
||||||
|
SEND_METHODS = {
|
||||||
|
'lpush': lambda redis_client, key, message: redis_client.lpush(key, message),
|
||||||
|
'rpush': lambda redis_client, key, message: redis_client.rpush(key, message),
|
||||||
|
'publish': lambda redis_client, key, message: redis_client.publish(key, message),
|
||||||
|
}
|
||||||
|
|
||||||
class Output(cowrie.core.output.Output):
|
class Output(cowrie.core.output.Output):
|
||||||
|
|
||||||
@ -15,11 +22,29 @@ class Output(cowrie.core.output.Output):
|
|||||||
"""
|
"""
|
||||||
Initialize pymisp module and ObjectWrapper (Abstract event and object creation)
|
Initialize pymisp module and ObjectWrapper (Abstract event and object creation)
|
||||||
"""
|
"""
|
||||||
self.host = CONFIG.get('output_redis', 'host')
|
host = CONFIG.get('output_redis', 'host')
|
||||||
self.port = CONFIG.get('output_redis', 'port')
|
port = CONFIG.get('output_redis', 'port')
|
||||||
self.db = CONFIG.get('output_redis', 'db')
|
|
||||||
|
try:
|
||||||
|
db = CONFIG.get('output_redis', 'db')
|
||||||
|
except NoOptionError:
|
||||||
|
db = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
password = CONFIG.get('output_redis', 'password')
|
||||||
|
except NoOptionError:
|
||||||
|
password = None
|
||||||
|
|
||||||
|
self.redis = redis.StrictRedis(host=host, port=port, db=db,
|
||||||
|
password=password)
|
||||||
|
|
||||||
self.keyname = CONFIG.get('output_redis', 'keyname')
|
self.keyname = CONFIG.get('output_redis', 'keyname')
|
||||||
self.redis = redis.StrictRedis(self.host, self.port, self.db)
|
|
||||||
|
try:
|
||||||
|
self.send_method = SEND_METHODS[CONFIG.get('output_redis', 'send_method')]
|
||||||
|
except (NoOptionError, KeyError):
|
||||||
|
self.send_method = SEND_METHODS['lpush']
|
||||||
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
pass
|
pass
|
||||||
@ -33,4 +58,4 @@ class Output(cowrie.core.output.Output):
|
|||||||
# Remove twisted 15 legacy keys
|
# Remove twisted 15 legacy keys
|
||||||
if i.startswith('log_'):
|
if i.startswith('log_'):
|
||||||
del logentry[i]
|
del logentry[i]
|
||||||
self.redis.lpush(self.keyname, json.dumps(logentry))
|
self.send_method(self.redis, self.keyname, json.dumps(logentry))
|
||||||
|
|||||||
@ -7,6 +7,8 @@ from __future__ import division, absolute_import
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from twisted.internet import defer, threads
|
from twisted.internet import defer, threads
|
||||||
|
from twisted.python import log
|
||||||
|
|
||||||
|
|
||||||
from botocore.session import get_session
|
from botocore.session import get_session
|
||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
@ -14,6 +16,7 @@ from botocore.exceptions import ClientError
|
|||||||
import cowrie.core.output
|
import cowrie.core.output
|
||||||
|
|
||||||
from cowrie.core.config import CONFIG
|
from cowrie.core.config import CONFIG
|
||||||
|
from configparser import NoOptionError
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -23,10 +26,16 @@ class Output(cowrie.core.output.Output):
|
|||||||
self.seen = set()
|
self.seen = set()
|
||||||
|
|
||||||
self.session = get_session()
|
self.session = get_session()
|
||||||
self.session.set_credentials(
|
|
||||||
CONFIG.get("output_s3", "access_key_id"),
|
try:
|
||||||
CONFIG.get("output_s3", "secret_access_key"),
|
if CONFIG.get("output_s3", "access_key_id") and CONFIG.get("output_s3", "secret_access_key"):
|
||||||
)
|
self.session.set_credentials(
|
||||||
|
CONFIG.get("output_s3", "access_key_id"),
|
||||||
|
CONFIG.get("output_s3", "secret_access_key"),
|
||||||
|
)
|
||||||
|
except NoOptionError:
|
||||||
|
log.msg("No AWS credentials found in config - using botocore global settings.")
|
||||||
|
|
||||||
self.client = self.session.create_client(
|
self.client = self.session.create_client(
|
||||||
's3',
|
's3',
|
||||||
region_name=CONFIG.get("output_s3", "region"),
|
region_name=CONFIG.get("output_s3", "region"),
|
||||||
|
|||||||
@ -111,7 +111,7 @@ class Output(cowrie.core.output.Output):
|
|||||||
' VALUES (?, ?, ?, ?)',
|
' VALUES (?, ?, ?, ?)',
|
||||||
(entry["session"], entry["timestamp"],
|
(entry["session"], entry["timestamp"],
|
||||||
0, entry["input"]))
|
0, entry["input"]))
|
||||||
|
|
||||||
elif entry["eventid"] == 'cowrie.session.file_download':
|
elif entry["eventid"] == 'cowrie.session.file_download':
|
||||||
self.simpleQuery('INSERT INTO `downloads`' + \
|
self.simpleQuery('INSERT INTO `downloads`' + \
|
||||||
' (`session`, `timestamp`, `url`, `outfile`, `shasum`)' + \
|
' (`session`, `timestamp`, `url`, `outfile`, `shasum`)' + \
|
||||||
@ -119,6 +119,13 @@ class Output(cowrie.core.output.Output):
|
|||||||
(entry["session"], entry["timestamp"],
|
(entry["session"], entry["timestamp"],
|
||||||
entry['url'], entry['outfile'], entry['shasum']))
|
entry['url'], entry['outfile'], entry['shasum']))
|
||||||
|
|
||||||
|
elif entry["eventid"] == 'cowrie.session.file_download.failed':
|
||||||
|
self.simpleQuery('INSERT INTO `downloads`' + \
|
||||||
|
' (`session`, `timestamp`, `url`, `outfile`, `shasum`)' + \
|
||||||
|
' VALUES (?, ?, ?, ?, ?)',
|
||||||
|
(entry["session"], entry["timestamp"],
|
||||||
|
entry['url'], 'NULL', 'NULL'))
|
||||||
|
|
||||||
elif entry["eventid"] == 'cowrie.session.file_download':
|
elif entry["eventid"] == 'cowrie.session.file_download':
|
||||||
self.simpleQuery('INSERT INTO `input`' + \
|
self.simpleQuery('INSERT INTO `input`' + \
|
||||||
' (`session`, `timestamp`, `realm`, `input`)' + \
|
' (`session`, `timestamp`, `realm`, `input`)' + \
|
||||||
|
|||||||
@ -314,24 +314,9 @@ class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLin
|
|||||||
HoneyPotBaseProtocol.timeoutConnection(self)
|
HoneyPotBaseProtocol.timeoutConnection(self)
|
||||||
|
|
||||||
|
|
||||||
def lastlogExit(self):
|
|
||||||
"""
|
|
||||||
"""
|
|
||||||
starttime = time.strftime('%a %b %d %H:%M',
|
|
||||||
time.localtime(self.logintime))
|
|
||||||
endtime = time.strftime('%H:%M',
|
|
||||||
time.localtime(time.time()))
|
|
||||||
duration = utils.durationHuman(time.time() - self.logintime)
|
|
||||||
with open( '%s/lastlog.txt' % (CONFIG.get('honeypot',
|
|
||||||
'log_path'),), 'a') as f:
|
|
||||||
f.write('root\tpts/0\t%s\t%s - %s (%s)\n' % \
|
|
||||||
(self.clientIP, starttime, endtime, duration))
|
|
||||||
|
|
||||||
|
|
||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
self.lastlogExit()
|
|
||||||
HoneyPotBaseProtocol.connectionLost(self, reason)
|
HoneyPotBaseProtocol.connectionLost(self, reason)
|
||||||
recvline.HistoricRecvLine.connectionLost(self, reason)
|
recvline.HistoricRecvLine.connectionLost(self, reason)
|
||||||
self.keyHandlers = None
|
self.keyHandlers = None
|
||||||
|
|||||||
@ -14,7 +14,7 @@ from cowrie.core.config import CONFIG
|
|||||||
|
|
||||||
def cowrieOpenConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avatar):
|
def cowrieOpenConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avatar):
|
||||||
"""
|
"""
|
||||||
This function will redirect an SSH forward request to a another address
|
This function will redirect an SSH forward request to another address
|
||||||
or will log the request and do nothing
|
or will log the request and do nothing
|
||||||
"""
|
"""
|
||||||
remoteHP, origHP = forwarding.unpackOpen_direct_tcpip(data)
|
remoteHP, origHP = forwarding.unpackOpen_direct_tcpip(data)
|
||||||
@ -24,6 +24,7 @@ def cowrieOpenConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avata
|
|||||||
dst_ip=remoteHP[0], dst_port=remoteHP[1],
|
dst_ip=remoteHP[0], dst_port=remoteHP[1],
|
||||||
src_ip=origHP[0], src_port=origHP[1])
|
src_ip=origHP[0], src_port=origHP[1])
|
||||||
|
|
||||||
|
# Forward redirect
|
||||||
try:
|
try:
|
||||||
if CONFIG.getboolean('ssh', 'forward_redirect') == True:
|
if CONFIG.getboolean('ssh', 'forward_redirect') == True:
|
||||||
redirectEnabled = True
|
redirectEnabled = True
|
||||||
@ -50,6 +51,33 @@ def cowrieOpenConnectForwardingClient(remoteWindow, remoteMaxPacket, data, avata
|
|||||||
return SSHConnectForwardingChannel(remoteHPNew,
|
return SSHConnectForwardingChannel(remoteHPNew,
|
||||||
remoteWindow=remoteWindow, remoteMaxPacket=remoteMaxPacket)
|
remoteWindow=remoteWindow, remoteMaxPacket=remoteMaxPacket)
|
||||||
|
|
||||||
|
# TCP tunnel
|
||||||
|
try:
|
||||||
|
if CONFIG.getboolean('ssh', 'forward_tunnel') == True:
|
||||||
|
tunnelEnabled = True
|
||||||
|
else:
|
||||||
|
tunnelEnabled = False
|
||||||
|
except:
|
||||||
|
tunnelEnabled = False
|
||||||
|
|
||||||
|
if tunnelEnabled:
|
||||||
|
tunnels = {}
|
||||||
|
items = CONFIG.items('ssh')
|
||||||
|
for i in items:
|
||||||
|
if i[0].startswith('forward_tunnel_'):
|
||||||
|
destPort = i[0].split('_')[-1]
|
||||||
|
tunnelHP = i[1].split(':')
|
||||||
|
tunnels[int(destPort)] = (tunnelHP[0], int(tunnelHP[1]))
|
||||||
|
if remoteHP[1] in tunnels:
|
||||||
|
remoteHPNew = tunnels[remoteHP[1]]
|
||||||
|
log.msg(eventid='cowrie.direct-tcpip.tunnel',
|
||||||
|
format='tunneled direct-tcp connection request %(src_ip)s:%(src_port)d->%(dst_ip)s:%(dst_port)d to %(new_ip)s:%(new_port)d',
|
||||||
|
new_ip=remoteHPNew[0], new_port=remoteHPNew[1],
|
||||||
|
dst_ip=remoteHP[0], dst_port=remoteHP[1],
|
||||||
|
src_ip=origHP[0], src_port=origHP[1])
|
||||||
|
return TCPTunnelForwardingChannel(remoteHPNew, remoteHP,
|
||||||
|
remoteWindow=remoteWindow, remoteMaxPacket=remoteMaxPacket)
|
||||||
|
|
||||||
return FakeForwardingChannel(remoteHP,
|
return FakeForwardingChannel(remoteHP,
|
||||||
remoteWindow=remoteWindow, remoteMaxPacket=remoteMaxPacket)
|
remoteWindow=remoteWindow, remoteMaxPacket=remoteMaxPacket)
|
||||||
|
|
||||||
@ -87,3 +115,64 @@ class FakeForwardingChannel(forwarding.SSHConnectForwardingChannel):
|
|||||||
dst_ip=self.hostport[0], dst_port=self.hostport[1], data=repr(data))
|
dst_ip=self.hostport[0], dst_port=self.hostport[1], data=repr(data))
|
||||||
self._close("Connection refused")
|
self._close("Connection refused")
|
||||||
|
|
||||||
|
|
||||||
|
class TCPTunnelForwardingChannel(forwarding.SSHConnectForwardingChannel):
|
||||||
|
"""
|
||||||
|
This class modifies the original to perform TCP tunneling via the CONNECT method
|
||||||
|
"""
|
||||||
|
name = b'cowrie-tunneled-direct-tcpip'
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, hostport, dstport, *args, **kw):
|
||||||
|
"""
|
||||||
|
Modifies the original to store where the data was originally going to go
|
||||||
|
"""
|
||||||
|
forwarding.SSHConnectForwardingChannel.__init__(self, hostport, *args, **kw)
|
||||||
|
self.dstport = dstport
|
||||||
|
self.tunnel_established = False
|
||||||
|
|
||||||
|
|
||||||
|
def channelOpen(self, specificData):
|
||||||
|
"""
|
||||||
|
Modifies the original to send a TCP tunnel request via the CONNECT method
|
||||||
|
"""
|
||||||
|
forwarding.SSHConnectForwardingChannel.channelOpen(self, specificData)
|
||||||
|
dst = self.dstport[0] + b':' + str(self.dstport[1])
|
||||||
|
connect_hdr = b'CONNECT ' + dst + b" HTTP/1.1\r\n\r\n"
|
||||||
|
forwarding.SSHConnectForwardingChannel.dataReceived(self, connect_hdr)
|
||||||
|
|
||||||
|
|
||||||
|
def dataReceived(self, data):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
log.msg(eventid='cowrie.tunnelproxy-tcpip.data',
|
||||||
|
format='sending via tunnel proxy %(data)s', data=repr(data))
|
||||||
|
forwarding.SSHConnectForwardingChannel.dataReceived(self, data)
|
||||||
|
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
"""
|
||||||
|
Modifies the original to stip off the TCP tunnel response
|
||||||
|
"""
|
||||||
|
if not self.tunnel_established and data[:4].lower() == b'http':
|
||||||
|
# Check proxy response code
|
||||||
|
try:
|
||||||
|
res_code = int(data.split(' ')[1], 10)
|
||||||
|
except ValueError:
|
||||||
|
log.err('Failed to parse TCP tunnel response code')
|
||||||
|
self._close("Connection refused")
|
||||||
|
if res_code != 200:
|
||||||
|
log.err('Unexpected response code: {}'.format(res_code))
|
||||||
|
self._close("Connection refused")
|
||||||
|
# Strip off rest of packet
|
||||||
|
eop = data.find("\r\n\r\n")
|
||||||
|
if eop > -1:
|
||||||
|
data = data[eop + 4:]
|
||||||
|
# This only happens once when the channel is opened
|
||||||
|
self.tunnel_established = True
|
||||||
|
|
||||||
|
forwarding.SSHConnectForwardingChannel.write(self, data)
|
||||||
|
|
||||||
|
|
||||||
|
def eofReceived(self):
|
||||||
|
self.loseConnection()
|
||||||
|
|||||||
@ -56,7 +56,7 @@ CREATE TABLE IF NOT EXISTS `downloads` (
|
|||||||
`session` CHAR( 32 ) NOT NULL,
|
`session` CHAR( 32 ) NOT NULL,
|
||||||
`timestamp` datetime NOT NULL,
|
`timestamp` datetime NOT NULL,
|
||||||
`url` text NOT NULL,
|
`url` text NOT NULL,
|
||||||
`outfile` text NOT NULL,
|
`outfile` text default NULL,
|
||||||
`shasum` varchar(64) default NULL,
|
`shasum` varchar(64) default NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `session` (`session`,`timestamp`)
|
KEY `session` (`session`,`timestamp`)
|
||||||
|
|||||||
@ -50,7 +50,7 @@ CREATE TABLE IF NOT EXISTS `downloads` (
|
|||||||
`session` CHAR( 32 ) NOT NULL,
|
`session` CHAR( 32 ) NOT NULL,
|
||||||
`timestamp` datetime NOT NULL,
|
`timestamp` datetime NOT NULL,
|
||||||
`url` text NOT NULL,
|
`url` text NOT NULL,
|
||||||
`outfile` text NOT NULL,
|
`outfile` text default NULL,
|
||||||
`shasum` varchar(64) default NULL
|
`shasum` varchar(64) default NULL
|
||||||
) ;
|
) ;
|
||||||
CREATE INDEX downloads_index ON downloads(session, timestamp);
|
CREATE INDEX downloads_index ON downloads(session, timestamp);
|
||||||
|
|||||||
1
doc/sql/update12.sql
Normal file
1
doc/sql/update12.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `downloads` MODIFY `outfile` text default NULL;
|
||||||
40
doc/squid/README.md
Normal file
40
doc/squid/README.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Using TCP tunneling with Squid
|
||||||
|
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
* Working Cowrie installation
|
||||||
|
* Working Squid installation with CONNECT allowed
|
||||||
|
* (optional) Rate limit and black/white lists in Squid
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo apt-get install squid
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Squid Configuration
|
||||||
|
|
||||||
|
See `squid.conf` for an example configuration.
|
||||||
|
|
||||||
|
|
||||||
|
## Cowrie Configuration
|
||||||
|
|
||||||
|
Uncomment and update the following entries to ~/cowrie/cowrie.cfg under the SSH section:
|
||||||
|
|
||||||
|
```
|
||||||
|
forward_tunnel = true
|
||||||
|
|
||||||
|
forward_tunnel_80 = 127.0.0.1:3128
|
||||||
|
forward_tunnel_443 = 127.0.0.1:3128
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Restart Cowrie
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd ~/cowrie/bin/
|
||||||
|
$ ./cowrie restart
|
||||||
|
```
|
||||||
19
doc/squid/squid.conf
Normal file
19
doc/squid/squid.conf
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
## Allow HTTP(S) (Ports 80, 443)
|
||||||
|
acl Safe_ports port 80 # http
|
||||||
|
acl Safe_ports port 443 # https
|
||||||
|
## Allow TCP tunneling via the CONNECT method
|
||||||
|
acl CONNECT method CONNECT
|
||||||
|
|
||||||
|
## Restrict bandwidth to ~128 kbps
|
||||||
|
delay_pools 1
|
||||||
|
delay_class 1 1
|
||||||
|
delay_parameters 1 16000/128000
|
||||||
|
delay_access 1 allow localhost
|
||||||
|
|
||||||
|
## Only allow localhost to tunnel Safe_ports
|
||||||
|
http_access deny CONNECT !Safe_ports
|
||||||
|
http_access allow localhost
|
||||||
|
http_access deny all
|
||||||
|
|
||||||
|
## Bind Squid to localhost
|
||||||
|
http_port 127.0.0.1:3128
|
||||||
@ -5,7 +5,7 @@ csirtgsdk>=0.0.0a17 # Specify version because pip won't install pre-release vers
|
|||||||
requests
|
requests
|
||||||
|
|
||||||
# elasticsearch
|
# elasticsearch
|
||||||
pyes
|
elasticsearch
|
||||||
|
|
||||||
# mysql
|
# mysql
|
||||||
# If this fails, see documentation /home/cowrie/cowrie/doc/sql/README.md
|
# If this fails, see documentation /home/cowrie/cowrie/doc/sql/README.md
|
||||||
|
|||||||
Reference in New Issue
Block a user