From 90cfee5ee5341f4cc5535a73fc2376689dc7b5c0 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 16 Feb 2016 18:42:40 +0400 Subject: [PATCH] CEF / textlog output module --- cowrie.cfg.dist | 16 +++++ cowrie/core/cef.py | 111 +++++++++++++++++++++++++++++++++++ cowrie/core/output.py | 7 +-- cowrie/output/localsyslog.py | 7 ++- cowrie/output/textlog.py | 62 +++++++++++++++++++ cowrie/ssh/transport.py | 4 +- 6 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 cowrie/core/cef.py create mode 100644 cowrie/output/textlog.py diff --git a/cowrie.cfg.dist b/cowrie.cfg.dist index f3e1a20..8657640 100644 --- a/cowrie.cfg.dist +++ b/cowrie.cfg.dist @@ -288,8 +288,24 @@ logfile = log/cowrie.json # Facility can be: # KERN, USER, MAIL, DAEMON, AUTH, LPR, NEWS, UUCP, CRON, SYSLOG and LOCAL0 to LOCAL7. # +# Format can be: +# text, cef +# #[output_localsyslog] #facility = USER +#format = text + + +# Text output +# +# This writes log entries to a text file +# +# Format can be: +# text, cef +# +#[output_textlog] +#logfile = log/cowrie.textlog +#format = text # MySQL logging module (output) diff --git a/cowrie/core/cef.py b/cowrie/core/cef.py new file mode 100644 index 0000000..17fa0ea --- /dev/null +++ b/cowrie/core/cef.py @@ -0,0 +1,111 @@ +# Copyright (c) 2015 Michel Oosterhof +# All rights reserved. +# +# 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. + +""" +This module contains ... +""" + +from twisted.python import log + +# cowrie.client.fingerprint +# cowrie.client.size +# cowrie.client.var +# cowrie.client.version +# cowrie.command.failed +# cowrie.command.success +# cowrie.direct-tcpip.data +# cowrie.direct-tcpip.request +# cowrie.log.closed +# cowrie.log.open +# cowrie.login.failed +# cowrie.login.success +# cowrie.session.closed +# cowrie.session.connect +# cowrie.session.file_download +# cowrie.session.file_upload + +def formatCef(logentry): + """ + Take logentry and turn into CEF string + """ + # Jan 18 11:07:53 host CEF:Version|Device Vendor|Device Product|Device Version|Signature ID|Name|Severity|[Extension] + cefVendor = "Cowrie" + cefProduct = "Cowrie" + cefVersion = "1.0" + cefSignature = logentry["eventid"] + cefName = logentry["eventid"] + cefSeverity = "5" + + cefExtensions = { + 'app': 'SSHv2', + 'destinationServicename': 'sshd', + 'deviceExternalId': logentry['sensor'], + 'msg': log.textFromEventDict(logentry), + 'src' : logentry['src_ip'], + 'proto': 'tcp' + } + + if logentry['eventid'] == 'cowrie.session.connect': + cefExtensions['spt'] = logentry['src_port'] + cefExtensions['dpt'] = logentry['dst_port'] + cefExtensions['src'] = logentry['src_ip'] + cefExtensions['dst'] = logentry['dst_ip'] + elif logentry['eventid'] == 'cowrie.login.success': + cefExtensions['duser'] = logentry['username'] + cefExtensions['outcome'] = 'success' + elif logentry['eventid'] == 'cowrie.login.failed': + cefExtensions['duser'] = logentry['username'] + cefExtensions['outcome'] = 'failed' + elif logentry['eventid'] == 'cowrie.file.file_download': + cefExtensions['filehash'] = logentry['filehash'] + cefExtensions['filePath'] = logentry['filename'] + cefExtensions['fsize'] = logentry['size'] + elif logentry['eventid'] == 'cowrie.file.file_upload': + cefExtensions['filehash'] = logentry['filehash'] + cefExtensions['filePath'] = logentry['filename'] + cefExtensions['fsize'] = logentry['size'] + + # 'out' 'outcome' request, rt + + cefList = [] + for key in cefExtensions.keys(): + value = str(cefExtensions[key]).replace(' ', '\ ') + cefList.append(key+"="+value) + cefExtension = ' '.join(cefList) + + cefString = "CEF:0|" + \ + cefVendor + "|" + \ + cefProduct + "|" + \ + cefVersion + "|" + \ + cefSignature + "|" + \ + cefName + "|" + \ + cefSeverity + "|" + \ + cefExtension + + return cefString + diff --git a/cowrie/core/output.py b/cowrie/core/output.py index 4345c98..9657803 100644 --- a/cowrie/core/output.py +++ b/cowrie/core/output.py @@ -158,11 +158,10 @@ class Output(object): # Connection event is special. adds to session list if ev['eventid'] == 'cowrie.session.connect': - self.sessions[sessionno] = ev['id'] + self.sessions[sessionno] = ev['session'] self.ips[sessionno] = ev['src_ip'] - del ev['id'] - - ev['session'] = self.sessions[sessionno] + else: + ev['session'] = self.sessions[sessionno] self.write(ev) diff --git a/cowrie/output/localsyslog.py b/cowrie/output/localsyslog.py index 1865c68..6c7c4ab 100644 --- a/cowrie/output/localsyslog.py +++ b/cowrie/output/localsyslog.py @@ -30,6 +30,7 @@ import syslog import twisted.python.syslog import cowrie.core.output +import cowrie.core.cef class Output(cowrie.core.output.Output): @@ -38,6 +39,7 @@ class Output(cowrie.core.output.Output): """ cowrie.core.output.Output.__init__(self, cfg) facilityString = cfg.get('output_localsyslog', 'facility') + self.format = cfg.get('output_localsyslog', 'format') self.facility = vars(syslog)['LOG_' + facilityString] self.syslog = twisted.python.syslog.SyslogObserver(prefix='cowrie', facility=self.facility) @@ -57,5 +59,8 @@ class Output(cowrie.core.output.Output): def write(self, logentry): """ """ - self.syslog.emit(logentry) + if self.format == 'cef': + self.syslog.emit(cowrie.core.cef.formatCef(logentry)) + else: + self.syslog.emit(logentry) diff --git a/cowrie/output/textlog.py b/cowrie/output/textlog.py new file mode 100644 index 0000000..f017ac9 --- /dev/null +++ b/cowrie/output/textlog.py @@ -0,0 +1,62 @@ +# Copyright (c) 2015 Michel Oosterhof +# All rights reserved. +# +# 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 cowrie.core.output +import cowrie.core.cef + +class Output(cowrie.core.output.Output): + + def __init__(self, cfg): + """ + """ + cowrie.core.output.Output.__init__(self, cfg) + self.format = cfg.get('output_textlog', 'format') + self.outfile = file(cfg.get('output_textlog', 'logfile'), 'a') + + + def start(self): + """ + """ + pass + + + def stop(self): + """ + """ + pass + + + def write(self, logentry): + """ + """ + if self.format == 'cef': + self.outfile.write(cowrie.core.cef.formatCef(logentry)+'\n') + self.outfile.flush() + else: + self.outfile.write(logentry["message"]) + diff --git a/cowrie/ssh/transport.py b/cowrie/ssh/transport.py index 3e8e710..2fa44ab 100644 --- a/cowrie/ssh/transport.py +++ b/cowrie/ssh/transport.py @@ -171,10 +171,10 @@ class HoneyPotTransport(transport.SSHServerTransport, TimeoutMixin): self.transportId = uuid.uuid4().hex[:8] log.msg(eventid='cowrie.session.connect', - format='New connection: %(src_ip)s:%(src_port)s (%(dst_ip)s:%(dst_port)s) [session: %(sessionno)s]', + format='New connection: %(src_ip)s:%(src_port)s (%(dst_ip)s:%(dst_port)s) [session: %(session)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, - id=self.transportId, sessionno=self.transport.sessionno) + session=self.transportId, sessionno=self.transport.sessionno) self.transport.write('{}\r\n'.format(self.ourVersionString)) self.currentEncryptions = transport.SSHCiphers('none', 'none', 'none', 'none')