зеркало из https://github.com/mozilla/MozDef.git
Fixing merge conflict
This commit is contained in:
Коммит
b6d5d1b57c
|
@ -29,8 +29,9 @@ class broNSM(AlertTask):
|
|||
search_query.add_must([
|
||||
PhraseMatch("details.note", "MozillaAlive::Bro_Is_Watching_You"),
|
||||
PhraseMatch("details.peer_descr", host),
|
||||
TermMatch('category', 'bronotice'),
|
||||
TermMatch('_type', 'bro')
|
||||
TermMatch('category', 'bro'),
|
||||
TermMatch('type', 'notice'),
|
||||
TermMatch('_type', 'nsm')
|
||||
])
|
||||
|
||||
self.filtersManual(search_query)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# host_regex path
|
||||
.*\.ignorehosts\.com /home/user/.ssh/id_rsa
|
|
@ -0,0 +1,101 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2017 Mozilla Corporation
|
||||
#
|
||||
# Contributors:
|
||||
# Aaron Meihm <ameihm@mozilla.com>
|
||||
|
||||
from lib.alerttask import AlertTask
|
||||
from query_models import SearchQuery, TermMatch
|
||||
import re
|
||||
|
||||
# This alert consumes data produced by the MIG sshkey module and mig-runner.
|
||||
# ssh key related events are compared against a whitelist which is the
|
||||
# alerts configuration file (sshkey.conf). The format of this whitelist
|
||||
# is as follows:
|
||||
#
|
||||
# <host regex> <path>
|
||||
#
|
||||
# If a host matches the regex, and the detected key matches the path, the
|
||||
# alert will not generate an alert event. If the detected key is not in
|
||||
# the whitelist, an alert will be created.
|
||||
|
||||
class SSHKey(AlertTask):
|
||||
def __init__(self):
|
||||
# _whitelist contains all whitelisted key paths, loaded from the
|
||||
# configuration file for the alert plugin
|
||||
self._whitelist = []
|
||||
|
||||
AlertTask.__init__(self)
|
||||
self._parse_whitelist('ssh_key.conf')
|
||||
|
||||
def main(self):
|
||||
search_query = SearchQuery(minutes=30)
|
||||
|
||||
search_query.add_must([
|
||||
TermMatch('_type', 'event'),
|
||||
TermMatch('tags', 'mig-runner-sshkey'),
|
||||
])
|
||||
|
||||
self.filtersManual(search_query)
|
||||
|
||||
self.searchEventsSimple()
|
||||
self.walkEvents()
|
||||
|
||||
# Load whitelist from file system and store in object, path specifies the
|
||||
# path to load the whitelist from
|
||||
def _parse_whitelist(self, path):
|
||||
with open(path) as fd:
|
||||
lns = [x.strip() for x in fd.readlines()]
|
||||
for entry in lns:
|
||||
try:
|
||||
pindex = entry.index(' ')
|
||||
except ValueError:
|
||||
continue
|
||||
self._whitelist.append({
|
||||
'hostre': entry[:pindex],
|
||||
'path': entry[pindex + 1:]
|
||||
})
|
||||
|
||||
# Return false if the key path is present in the whitelist, otherwise return
|
||||
# true
|
||||
def _whitelist_check(self, hostname, privkey):
|
||||
for went in self._whitelist:
|
||||
try:
|
||||
rem = re.compile(went['hostre'])
|
||||
except:
|
||||
continue
|
||||
if rem.match(hostname) == None:
|
||||
continue
|
||||
if privkey['path'] == went['path']:
|
||||
return False
|
||||
return True
|
||||
|
||||
def onEvent(self, event):
|
||||
# Each event could potentially contain a number of identified private keys, since
|
||||
# mig-runner will send an event per host. Compare the private keys in the event
|
||||
# to the keys in the whitelist, if any are missing from the whitelist we will include
|
||||
# the paths in an alert and return the alert, otherwise no alert is returned.
|
||||
ev = event['_source']
|
||||
|
||||
if 'details' not in ev:
|
||||
return None
|
||||
if 'private' not in ev['details'] or 'agent' not in ev['details']:
|
||||
return None
|
||||
hostname = ev['details']['agent']
|
||||
alertkeys = [x for x in ev['details']['private'] if self._whitelist_check(hostname, x)]
|
||||
if len(alertkeys) == 0:
|
||||
return None
|
||||
|
||||
category = 'sshkey'
|
||||
tags = ['sshkey']
|
||||
severity = 'WARNING'
|
||||
summary = 'Private keys detected on {} missing from whitelist'.format(hostname)
|
||||
ret = self.createAlertDict(summary, category, tags, [event], severity)
|
||||
ret['details'] = {
|
||||
'private': alertkeys
|
||||
}
|
||||
return ret
|
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2017 Mozilla Corporation
|
||||
|
||||
from lib.alerttask import AlertTask
|
||||
from query_models import SearchQuery, TermMatch, PhraseMatch
|
||||
|
||||
|
||||
class AlertSSHPasswordAuthViolation(AlertTask):
|
||||
def main(self):
|
||||
search_query = SearchQuery(hours=4)
|
||||
|
||||
search_query.add_must([
|
||||
TermMatch('_type', 'event'),
|
||||
PhraseMatch('tags', 'ssh_password_auth_policy_violation'),
|
||||
])
|
||||
|
||||
self.filtersManual(search_query)
|
||||
|
||||
# Search aggregations on field 'sourceipaddress', keep X samples of
|
||||
# events at most
|
||||
self.searchEventsAggregated('details.destinationipaddress', samplesLimit=100)
|
||||
# alert when >= X matching events in an aggregation
|
||||
self.walkAggregations(threshold=1)
|
||||
|
||||
# Set alert properties
|
||||
def onAggregation(self, aggreg):
|
||||
# aggreg['count']: number of items in the aggregation, ex: number of failed login attempts
|
||||
# aggreg['value']: value of the aggregation field, ex: toto@example.com
|
||||
# aggreg['events']: list of events in the aggregation
|
||||
category = 'ssh_password_auth_policy_violation'
|
||||
tags = ['ssh_password_auth_policy_violation']
|
||||
severity = 'WARNING'
|
||||
summary = 'SSH password authentication allowed on {0}'.format(aggreg['value'])
|
||||
|
||||
# Create the alert object based on these properties
|
||||
return self.createAlertDict(summary, category, tags, aggreg['events'], severity)
|
|
@ -1,23 +1,8 @@
|
|||
if $programname == 'mozdefbot-worker' then /var/log/mozdef/mozdefbot.log
|
||||
if $programname == 'loginput-worker' then /var/log/mozdef/loginput.log
|
||||
if $programname == 'infosecsqs-worker' then /var/log/mozdef/infosecsqs.log
|
||||
if $programname == 'restapi-worker' then /var/log/mozdef/restapi.log
|
||||
if $programname == 'eventtask-worker' then /var/log/mozdef/eventtask.log
|
||||
if $programname == 'nubis-worker' then /var/log/mozdef/nubis.log
|
||||
if $programname == 'bro-worker' then /var/log/mozdef/bro.log
|
||||
if $programname == 'migsqs-worker' then /var/log/mozdef/migsqs.log
|
||||
if $programname == 'parsyssqs-worker' then /var/log/mozdef/parsyssqs.log
|
||||
if $programname == 'autoland-worker' then /var/log/mozdef/autoland.log
|
||||
if $programname == 'contegix-worker' then /var/log/mozdef/contegix.log
|
||||
if $programname == 'deis-worker' then /var/log/mozdef/deis.log
|
||||
if $programname == 'releng-worker' then /var/log/mozdef/releng.log
|
||||
if $programname == 'fxa-worker' then /var/log/mozdef/fxa.log
|
||||
if $programname == 'httpobs-worker' then /var/log/mozdef/httpobs.log
|
||||
if $programname == 'riskheatmap-worker' then /var/log/mozdef/riskheatmap.log
|
||||
if $programname == 'ssosqs-worker' then /var/log/mozdef/sso.log
|
||||
if $programname == 'cloudtrail-worker' then /var/log/mozdef/cloudtrail.log
|
||||
if $programname == 'alertplugins-worker' then /var/log/mozdef/alertplugins.log
|
||||
if $programname == 'contegix-auditd-worker' then /var/log/mozdef/contegix-auditd.log
|
||||
if $programname == 'mongod.3002' then /var/log/mozdef/mongo/meteor-mongo.log
|
||||
if $programname == 'mongod' then /var/log/mozdef/mongo/mongo.log
|
||||
if $programname == 'kibana4' then /var/log/mozdef/kibana.log
|
||||
|
|
|
@ -125,6 +125,11 @@
|
|||
"type" : "string",
|
||||
"index" : "not_analyzed",
|
||||
"doc_values" : true
|
||||
},
|
||||
"version" : {
|
||||
"index" : "not_analyzed",
|
||||
"type" : "string",
|
||||
"doc_values" : true
|
||||
}
|
||||
},
|
||||
"_all" : {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/var/log/mozdef/mongo/*.log
|
||||
{
|
||||
rotate 17
|
||||
rotate 4
|
||||
weekly
|
||||
missingok
|
||||
notifempty
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/var/log/mozdef/*.log
|
||||
{
|
||||
rotate 17
|
||||
rotate 4
|
||||
weekly
|
||||
missingok
|
||||
notifempty
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/var/log/mozdef/nginx/*.log {
|
||||
/var/log/mozdef/nginx/*.error_log {
|
||||
weekly
|
||||
missingok
|
||||
rotate 17
|
||||
rotate 4
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
copytruncate
|
||||
dateext
|
||||
rotate 17
|
||||
rotate 4
|
||||
weekly
|
||||
missingok
|
||||
notifempty
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# Aaron Meihm ameihm@mozilla.com
|
||||
|
||||
# Reads from papertrail using the API and inserts log data into ES in
|
||||
# the same manner as esworker.py
|
||||
# the same manner as esworker_eventtask.py
|
||||
|
||||
|
||||
import json
|
||||
|
|
|
@ -115,7 +115,7 @@ class taskConsumer(object):
|
|||
message_json = json.loads(message_value)
|
||||
for inside_message_key, inside_message_value in message_json.iteritems():
|
||||
if inside_message_key in ('processid', 'pid'):
|
||||
processid = inside_message_value
|
||||
processid = str(inside_message_value)
|
||||
processid = processid.replace('[', '')
|
||||
processid = processid.replace(']', '')
|
||||
event['processid'] = processid
|
||||
|
|
|
@ -6,8 +6,12 @@
|
|||
# Contributors:
|
||||
# Jeff Bryner jbryner@mozilla.com
|
||||
# Brandon Myers bmyers@mozilla.com
|
||||
# Michal Purzynski mpurzynski@mozilla.com
|
||||
|
||||
import netaddr
|
||||
from utilities.toUTC import toUTC
|
||||
from datetime import datetime
|
||||
from platform import node
|
||||
|
||||
|
||||
def isIPv4(ip):
|
||||
|
@ -41,39 +45,519 @@ class message(object):
|
|||
|
||||
self.registration = ['bro', 'nsm']
|
||||
self.priority = 5
|
||||
try:
|
||||
self.mozdefhostname = u'{0}'.format(node())
|
||||
except:
|
||||
self.mozdefhostname = 'failed to fetch mozdefhostname'
|
||||
pass
|
||||
|
||||
|
||||
def onMessage(self, message, metadata):
|
||||
|
||||
# make sure I really wanted to see this message
|
||||
# bail out early if not
|
||||
if u'customendpoint' not in message:
|
||||
return message, metadata
|
||||
if u'category' not in message:
|
||||
return message, metadata
|
||||
if u'type' not in message:
|
||||
return message, metadata
|
||||
if message['category'] != 'bro':
|
||||
return message, metadata
|
||||
|
||||
|
||||
# set the doc type to bro
|
||||
# to avoid data type conflicts with other doc types
|
||||
# (int v string, etc)
|
||||
metadata['doc_type']= 'bro'
|
||||
# index holds documents of type 'type'
|
||||
# index -> type -> doc
|
||||
metadata['doc_type']= 'nsm'
|
||||
|
||||
# move Bro specific fields under 'details' while preserving metadata
|
||||
newmessage = dict()
|
||||
|
||||
newmessage['details'] = message
|
||||
|
||||
newmessage['customendpoint'] = 'bro'
|
||||
|
||||
# move some fields that are expected at the event 'root' where they belong
|
||||
if 'hostname' in newmessage['details']:
|
||||
newmessage['hostname'] = newmessage['details']['hostname']
|
||||
del(newmessage['details']['hostname'])
|
||||
if 'tags' in newmessage['details']:
|
||||
newmessage['tags'] = newmessage['details']['tags']
|
||||
del(newmessage['details']['tags'])
|
||||
if 'category' in newmessage['details']:
|
||||
newmessage['category'] = newmessage['details']['category']
|
||||
del(newmessage['details']['category'])
|
||||
if 'customendpoint' in newmessage['details']:
|
||||
del(newmessage['details']['customendpoint'])
|
||||
if 'type' in newmessage['details']:
|
||||
newmessage['type'] = newmessage['details']['type']
|
||||
del(newmessage['details']['type'])
|
||||
|
||||
|
||||
# add mandatory fields
|
||||
if 'ts' in newmessage['details']:
|
||||
newmessage[u'utctimestamp'] = toUTC(newmessage['details']['ts']).isoformat()
|
||||
newmessage[u'timestamp'] = toUTC(newmessage['details']['ts']).isoformat()
|
||||
else:
|
||||
# a malformed message somehow managed to crawl to us, let's put it somewhat together
|
||||
newmessage[u'utctimestamp'] = toUTC(datetime.now()).isoformat()
|
||||
newmessage[u'timestamp'] = toUTC(datetime.now()).isoformat()
|
||||
|
||||
newmessage[u'receivedtimestamp'] = toUTC(datetime.now()).isoformat()
|
||||
newmessage[u'eventsource'] = u'nsm'
|
||||
newmessage[u'severity'] = u'INFO'
|
||||
newmessage[u'mozdefhostname'] = self.mozdefhostname
|
||||
|
||||
|
||||
# re-arrange the position of some fields
|
||||
# {} vs {'details':{}}
|
||||
if 'details' in message.keys():
|
||||
# details.tags -> tags
|
||||
if 'tags' in message['details'].keys():
|
||||
message['tags'] = message['details']['tags']
|
||||
del message['details']['tags']
|
||||
if 'details' in newmessage:
|
||||
|
||||
# details.summary -> summary
|
||||
if 'summary' in message['details'].keys():
|
||||
message['summary'] = message['details']['summary']
|
||||
del message['details']['summary']
|
||||
# All Bro logs need special treatment, so we provide it
|
||||
# Not a known log type? Mark it as such and return
|
||||
if 'type' not in newmessage:
|
||||
newmessage['type'] = u'unknown'
|
||||
return newmessage, metadata
|
||||
else:
|
||||
logtype = newmessage['type']
|
||||
|
||||
# clean up the action notice IP addresses
|
||||
if 'actions' in message['details'].keys():
|
||||
if message['details']['actions'] == "Notice::ACTION_LOG":
|
||||
# retrieve indicator ip addresses from the sub field
|
||||
# "sub": "Indicator: 1.2.3.4, Indicator: 5.6.7.8"
|
||||
message['details']['indicators'] = [ip for ip
|
||||
in findIPv4(message['details']['sub'])]
|
||||
if logtype == 'conn':
|
||||
newmessage[u'details'][u'originipbytes'] = newmessage['details']['orig_ip_bytes']
|
||||
newmessage[u'details'][u'responseipbytes'] = newmessage['details']['resp_ip_bytes']
|
||||
del(newmessage['details']['orig_ip_bytes'])
|
||||
del(newmessage['details']['resp_ip_bytes'])
|
||||
if 'history' not in newmessage['details']:
|
||||
newmessage['details'][u'history'] = ''
|
||||
newmessage[u'summary'] = (
|
||||
u'{sourceipaddress}:'+
|
||||
u'{sourceport} -> '+
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '+
|
||||
u'{history} '+
|
||||
u'{originipbytes} bytes / '
|
||||
u'{responseipbytes} bytes'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'files':
|
||||
if 'rx_hosts' in newmessage['details']:
|
||||
newmessage['details'][u'sourceipaddress'] = u'{0}'.format(newmessage['details']['rx_hosts'][0])
|
||||
if 'tx_hosts' in newmessage['details']:
|
||||
newmessage['details'][u'destinationipaddress'] = u'{0}'.format(newmessage['details']['tx_hosts'][0])
|
||||
if 'mime_type' not in newmessage['details']:
|
||||
newmessage['details'][u'mime_type'] = u'unknown'
|
||||
if 'filename' not in newmessage['details']:
|
||||
newmessage['details'][u'filename'] = u'unknown'
|
||||
if 'total_bytes' not in newmessage['details']:
|
||||
newmessage['details'][u'total_bytes'] = u'0'
|
||||
if 'md5' not in newmessage['details']:
|
||||
newmessage['details'][u'md5'] = u'None'
|
||||
if 'source' not in newmessage['details']:
|
||||
newmessage['details'][u'source'] = u'None'
|
||||
newmessage[u'summary'] = (
|
||||
u'{rx_hosts[0]} '
|
||||
u'downloaded (MD5) '
|
||||
u'{md5} '
|
||||
u'filename {filename} '
|
||||
u'MIME {mime_type} '
|
||||
u'({total_bytes} bytes) '
|
||||
u'from {tx_hosts[0]} '
|
||||
u'via {source}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'dns':
|
||||
if 'qtype_name' not in newmessage['details']:
|
||||
newmessage['details'][u'qtype_name'] = u''
|
||||
if 'query' not in newmessage['details']:
|
||||
newmessage['details'][u'query'] = u''
|
||||
if 'rcode_name' not in newmessage['details']:
|
||||
newmessage['details'][u'rcode_name'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'{sourceipaddress} -> '
|
||||
u'{destinationipaddress}:{destinationport} '
|
||||
u'{qtype_name} '
|
||||
u'{query} '
|
||||
u'{rcode_name}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'http':
|
||||
if 'method' not in newmessage['details']:
|
||||
newmessage['details'][u'method'] = u''
|
||||
if 'host' not in newmessage['details']:
|
||||
newmessage['details'][u'host'] = u''
|
||||
if 'uri' not in newmessage['details']:
|
||||
newmessage['details'][u'uri'] = u''
|
||||
if 'status_code' not in newmessage['details']:
|
||||
newmessage['details'][u'status_code'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'{method} '
|
||||
u'{host} '
|
||||
u'{uri} '
|
||||
u'{status_code}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'ssl':
|
||||
if 'server_name' not in newmessage['details']:
|
||||
# fake it till you make it
|
||||
newmessage['details'][u'server_name'] = newmessage['details']['destinationipaddress']
|
||||
newmessage[u'summary'] = (
|
||||
u'SSL: {sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'{server_name}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'dhcp':
|
||||
newmessage[u'summary'] = (
|
||||
'{assigned_ip} assigned to '
|
||||
'{mac}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'ftp':
|
||||
if 'command' not in newmessage['details']:
|
||||
newmessage['details'][u'command'] = u''
|
||||
if 'user' not in newmessage['details']:
|
||||
newmessage['details'][u'user'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'FTP: {sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'{command} '
|
||||
u'{user}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'pe':
|
||||
if 'os' not in newmessage['details']:
|
||||
newmessage['details']['os'] = ''
|
||||
if 'subsystem' not in newmessage['details']:
|
||||
newmessage['details']['subsystem'] = ''
|
||||
newmessage[u'summary'] = (
|
||||
u'PE file: {os} '
|
||||
u'{subsystem}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'smtp':
|
||||
if 'from' in newmessage['details']:
|
||||
from_decoded = newmessage['details'][u'from'].decode('unicode-escape')
|
||||
newmessage['details'][u'from'] = from_decoded
|
||||
else:
|
||||
newmessage['details'][u'from'] = u''
|
||||
if 'to' not in newmessage['details']:
|
||||
newmessage['details'][u'to'] = [u'']
|
||||
if 'msg_id' not in newmessage['details']:
|
||||
newmessage['details'][u'msg_id'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'SMTP: {sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'from {from} '
|
||||
u'to '
|
||||
u'{to[0]} '
|
||||
u'ID {msg_id}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'ssh':
|
||||
if 'auth_success' not in newmessage['details']:
|
||||
newmessage['details'][u'auth_success'] = u'unknown'
|
||||
newmessage[u'summary'] = (
|
||||
u'SSH: {sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'success {auth_success}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'tunnel':
|
||||
if 'tunnel_type' not in newmessage['details']:
|
||||
newmessage['details'][u'tunnel_type'] = u''
|
||||
if 'action' not in newmessage['details']:
|
||||
newmessage['details'][u'action'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'{sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'{tunnel_type} '
|
||||
u'{action}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'intel':
|
||||
if 'seenindicator' not in newmessage['details']:
|
||||
newmessage['details'][u'seenindicator'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'Bro intel match: '
|
||||
u'{seenindicator}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'knowncerts':
|
||||
if 'serial' not in newmessage['details']:
|
||||
newmessage['details'][u'serial'] = u'0'
|
||||
newmessage[u'summary'] = (
|
||||
u'Certificate seen from: '
|
||||
u'{host}:'
|
||||
u'{port_num} '
|
||||
u'serial {serial}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'knowndevices':
|
||||
if 'mac' not in newmessage['details']:
|
||||
newmessage['details'][u'mac'] = u''
|
||||
if 'dhcp_host_name' not in newmessage['details']:
|
||||
newmessage['details'][u'dhcp_host_name'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'New host: '
|
||||
u'{mac} '
|
||||
u'{dhcp_host_name}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'knownhosts':
|
||||
if 'host' not in newmessage['details']:
|
||||
newmessage['details'][u'host'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'New host: '
|
||||
u'{host}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'knownservices':
|
||||
if 'service' not in newmessage['details']:
|
||||
newmessage['details']['service'] = []
|
||||
if not newmessage['details']['service']:
|
||||
newmessage['details'][u'service'] = [u'Unknown']
|
||||
if 'host' not in newmessage['details']:
|
||||
newmessage['details'][u'host'] = u'unknown'
|
||||
if 'port_num' not in newmessage['details']:
|
||||
newmessage['details'][u'port_num'] = u'0'
|
||||
if 'port_proto' not in newmessage['details']:
|
||||
newmessage['details'][u'port_proto'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'New service: '
|
||||
u'{service[0]} '
|
||||
u'on host '
|
||||
u'{host}:'
|
||||
u'{port_num} / '
|
||||
u'{port_proto}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'notice':
|
||||
newmessage['details'][u'indicators'] = []
|
||||
if 'sub' not in newmessage['details']:
|
||||
newmessage['details'][u'sub'] = u''
|
||||
if 'msg' not in newmessage['details']:
|
||||
newmessage['details'][u'msg'] = u''
|
||||
if 'note' not in newmessage['details']:
|
||||
newmessage['details'][u'note'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'{note} '
|
||||
u'{msg} '
|
||||
u'{sub}'
|
||||
).format(**newmessage['details'])
|
||||
# clean up the action notice IP addresses
|
||||
if 'actions' in newmessage['details']:
|
||||
if newmessage['details']['actions'] == "Notice::ACTION_LOG":
|
||||
# retrieve indicator ip addresses from the sub field
|
||||
# "sub": "Indicator: 1.2.3.4, Indicator: 5.6.7.8"
|
||||
newmessage['details']['indicators'] = [ip for ip
|
||||
in findIPv4(newmessage['details']['sub'])]
|
||||
# remove the details.src field and add it to indicators
|
||||
# as it may not be the actual source.
|
||||
if 'src' in message['details'].keys():
|
||||
if isIPv4(message['details']['src']):
|
||||
message['details']['indicators'].append(message['details']['src'])
|
||||
del message['details']['src']
|
||||
if 'src' in newmessage['details']:
|
||||
if isIPv4(newmessage['details']['src']):
|
||||
newmessage['details']['indicators'].append(newmessage['details']['src'])
|
||||
del newmessage['details']['src']
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'rdp':
|
||||
if 'cookie' not in newmessage['details']:
|
||||
newmessage['details'][u'cookie'] = u'unknown'
|
||||
newmessage[u'summary'] = (
|
||||
u'RDP: {sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'cookie {cookie}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
return (message, metadata)
|
||||
if logtype == 'sip':
|
||||
if 'status_msg' not in newmessage['details']:
|
||||
newmessage['details'][u'status_msg'] = u'unknown'
|
||||
if 'uri' not in newmessage['details']:
|
||||
newmessage['details'][u'uri'] = u'unknown'
|
||||
if 'method' not in newmessage['details']:
|
||||
newmessage['details'][u'method'] = u'unknown'
|
||||
newmessage[u'summary'] = (
|
||||
u'SIP: {sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'method {method} '
|
||||
u'uri {uri} '
|
||||
u'status {status_msg}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'software':
|
||||
if 'name' not in newmessage['details']:
|
||||
newmessage['details'][u'name'] = u'unparsed'
|
||||
if 'software_type' not in newmessage['details']:
|
||||
newmessage['details'][u'software_type'] = u'unknown software'
|
||||
if 'host' not in newmessage['details']:
|
||||
newmessage['details'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'Found {software_type} '
|
||||
u'name {name} '
|
||||
u'on {host}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'socks':
|
||||
if 'version' not in newmessage['details']:
|
||||
newmessage['details'][u'version'] = u'0'
|
||||
if 'status' not in newmessage['details']:
|
||||
newmessage['details'][u'status'] = u'unknown'
|
||||
newmessage[u'summary'] = (
|
||||
u'SOCKSv{version}: '
|
||||
u'{sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'status {status}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'dcerpc':
|
||||
if 'endpoint' not in newmessage['details']:
|
||||
newmessage['details'][u'endpoint'] = u'unknown'
|
||||
if 'operation' not in newmessage['details']:
|
||||
newmessage['details'][u'operation'] = u'unknown'
|
||||
newmessage[u'summary'] = (
|
||||
u'DCERPC: {sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'endpoint {endpoint} '
|
||||
u'operation {operation}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'kerberos':
|
||||
if 'request_type' not in newmessage['details']:
|
||||
newmessage['details'][u'request_type'] = u'unknown'
|
||||
if 'client' not in newmessage['details']:
|
||||
newmessage['details'][u'client'] = u'unknown'
|
||||
if 'service' not in newmessage['details']:
|
||||
newmessage['details'][u'service'] = u'unknown'
|
||||
if 'success' not in newmessage['details']:
|
||||
newmessage['details'][u'success'] = u'unknown'
|
||||
if 'error_msg' not in newmessage['details']:
|
||||
newmessage['details'][u'error_msg'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
u'{sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'client {client} '
|
||||
u'request {request_type} '
|
||||
u'service {service} '
|
||||
u'success {success} '
|
||||
u'{error_msg}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'ntlm':
|
||||
if 'ntlmdomainname' not in newmessage['details']:
|
||||
newmessage['details'][u'ntlmdomainname'] = u'unknown'
|
||||
if 'ntlmhostname' not in newmessage['details']:
|
||||
newmessage['details'][u'ntlmhostname'] = u'unknown'
|
||||
if 'ntlmusername' not in newmessage['details']:
|
||||
newmessage['details'][u'ntlmusername'] = u'unknown'
|
||||
if 'success' not in newmessage['details']:
|
||||
newmessage['details'][u'success'] = u'unknown'
|
||||
if 'status' not in newmessage['details']:
|
||||
newmessage['details'][u'status'] = u'unknown'
|
||||
newmessage[u'summary'] = (
|
||||
u'NTLM: {sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'user {ntlmusername} '
|
||||
u'host {ntlmhostname} '
|
||||
u'domain {ntlmdomainname} '
|
||||
u'success {success} '
|
||||
u'status {status}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'smbfiles':
|
||||
if 'path' not in newmessage['details']:
|
||||
newmessage['details'][u'path'] = u''
|
||||
if 'name' not in newmessage['details']:
|
||||
newmessage['details'][u'name'] = u''
|
||||
if 'action' not in newmessage['details']:
|
||||
newmessage['details'][u'action'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
'SMB file: '
|
||||
u'{sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'{action}'
|
||||
).format(**newmessage['details'])
|
||||
return(newmessage, metadata)
|
||||
|
||||
if logtype == 'smbmapping':
|
||||
if 'share_type' not in newmessage['details']:
|
||||
newmessage['details'][u'share_type'] = u''
|
||||
if 'path' not in newmessage['details']:
|
||||
newmessage['details'][u'path'] = u''
|
||||
newmessage[u'summary'] = (
|
||||
'SMB mapping: '
|
||||
u'{sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'{share_type}'
|
||||
).format(**newmessage['details'])
|
||||
return(newmessage, metadata)
|
||||
|
||||
if logtype == 'snmp':
|
||||
if 'version' not in newmessage['details']:
|
||||
newmessage['details'][u'version'] = u'Unknown'
|
||||
if 'get_bulk_requests' not in newmessage['details']:
|
||||
newmessage['details']['get_bulk_requests'] = 0
|
||||
if 'get_requests' not in newmessage['details']:
|
||||
newmessage['details']['get_requests'] = 0
|
||||
if 'set_requests' not in newmessage['details']:
|
||||
newmessage['details']['set_requests'] = 0
|
||||
if 'get_responses' not in newmessage['details']:
|
||||
newmessage['details']['get_responses'] = 0
|
||||
newmessage['details']['getreqestssum'] = u'{0}'.format(newmessage['details']['get_bulk_requests'] + newmessage['details']['get_requests'])
|
||||
newmessage[u'summary'] = (
|
||||
u'SNMPv{version}: '
|
||||
u'{sourceipaddress} -> '
|
||||
u'{destinationipaddress}:'
|
||||
u'{destinationport} '
|
||||
u'({getreqestssum} get / '
|
||||
u'{set_requests} set requests '
|
||||
u'{get_responses} get responses)'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
if logtype == 'x509':
|
||||
if 'certificateserial' not in newmessage['details']:
|
||||
newmessage['details'][u'certificateserial'] = u'0'
|
||||
newmessage[u'summary'] = (
|
||||
'Certificate seen serial {certificateserial}'
|
||||
).format(**newmessage['details'])
|
||||
return (newmessage, metadata)
|
||||
|
||||
|
||||
return (newmessage, metadata)
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2017 Mozilla Corporation
|
||||
#
|
||||
# Contributors:
|
||||
# Arzhel Younsi arzhel@mozilla.com
|
||||
# Jeff Bryner jbryner@mozilla.com
|
||||
# Brandon Myers bmyers@mozilla.com
|
||||
|
||||
import os
|
||||
from configlib import getConfig
|
||||
|
||||
|
||||
class message(object):
|
||||
def __init__(self):
|
||||
'''
|
||||
this plugin takes a source hostname of form
|
||||
host.private.site.mozilla.com
|
||||
extracts the site, adds it and compares the site
|
||||
to a list of known datacenters or offices and adds that metadata
|
||||
'''
|
||||
self.registration = ['network', 'netflow']
|
||||
self.priority = 5
|
||||
|
||||
config_location = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mozillaLocation.conf")
|
||||
self.dc_code_list = getConfig('dc_code_list', '', config_location).split(',')
|
||||
self.offices_code_list = getConfig('offices_code_list', '', config_location).split(',')
|
||||
|
||||
def onMessage(self, message, metadata):
|
||||
if 'details' in message.keys() and 'hostname' in message['details'].keys():
|
||||
hostnamesplit = str.lower(message['details']['hostname'].encode('ascii', 'ignore')).split('.')
|
||||
if len(hostnamesplit) == 5:
|
||||
if 'mozilla' == hostnamesplit[-2]:
|
||||
message['details']['site'] = hostnamesplit[-3]
|
||||
if message['details']['site'] in self.dc_code_list:
|
||||
message['details']['sitetype'] = 'datacenter'
|
||||
elif message['details']['site'] in self.offices_code_list:
|
||||
message['details']['sitetype'] = 'office'
|
||||
else:
|
||||
message['details']['sitetype'] = 'unknown'
|
||||
return (message, metadata)
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2017 Mozilla Corporation
|
||||
#
|
||||
# Contributors:
|
||||
# Arzhel Younsi arzhel@mozilla.com
|
||||
# Jeff Bryner jbryner@mozilla.com
|
||||
# Brandon Myers bmyers@mozilla.com
|
||||
|
||||
import os
|
||||
from configlib import getConfig
|
||||
|
||||
|
||||
class message(object):
|
||||
def __init__(self):
|
||||
'''
|
||||
this plugin takes a source hostname of form
|
||||
host.private.site.mozilla.com
|
||||
extracts the site, adds it and compares the site
|
||||
to a list of known datacenters or offices and adds that metadata
|
||||
'''
|
||||
self.registration = ['network', 'netflow']
|
||||
self.priority = 5
|
||||
|
||||
config_location = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mozilla_location.conf")
|
||||
self.dc_code_list = getConfig('dc_code_list', '', config_location).split(',')
|
||||
self.offices_code_list = getConfig('offices_code_list', '', config_location).split(',')
|
||||
|
||||
def onMessage(self, message, metadata):
|
||||
if 'details' in message.keys() and 'hostname' in message['details'].keys():
|
||||
hostnamesplit = str.lower(message['details']['hostname'].encode('ascii', 'ignore')).split('.')
|
||||
if len(hostnamesplit) == 5:
|
||||
if 'mozilla' == hostnamesplit[-2]:
|
||||
message['details']['site'] = hostnamesplit[-3]
|
||||
if message['details']['site'] in self.dc_code_list:
|
||||
message['details']['sitetype'] = 'datacenter'
|
||||
elif message['details']['site'] in self.offices_code_list:
|
||||
message['details']['sitetype'] = 'office'
|
||||
else:
|
||||
message['details']['sitetype'] = 'unknown'
|
||||
return (message, metadata)
|
|
@ -0,0 +1,101 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2014 Mozilla Corporation
|
||||
#
|
||||
# Contributors:
|
||||
# Jeff Bryner jbryner@mozilla.com
|
||||
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from configlib import getConfig, OptionParser
|
||||
|
||||
class message(object):
|
||||
def __init__(self):
|
||||
'''register our criteria for being passed a message
|
||||
as a list of lower case strings to match with an rest endpoint
|
||||
(i.e. blockip matches /blockip)
|
||||
set the priority if you have a preference for order of plugins
|
||||
0 goes first, 100 is assumed/default if not sent
|
||||
|
||||
Plugins will register in Meteor with attributes:
|
||||
name: (as below)
|
||||
description: (as below)
|
||||
priority: (as below)
|
||||
file: "plugins.filename" where filename.py is the plugin code.
|
||||
|
||||
Plugin gets sent main rest options as:
|
||||
self.restoptions
|
||||
self.restoptions['configfile'] will be the .conf file
|
||||
used by the restapi's index.py file.
|
||||
|
||||
'''
|
||||
|
||||
self.registration = ['ipintel']
|
||||
self.priority = 5
|
||||
self.name = "cymon"
|
||||
self.description = "IP intel from the cymon.io api"
|
||||
|
||||
# set my own conf file
|
||||
# relative path to the rest index.py file
|
||||
self.configfile = './plugins/cymon.conf'
|
||||
self.options = None
|
||||
if os.path.exists(self.configfile):
|
||||
sys.stdout.write('found conf file {0}\n'.format(self.configfile))
|
||||
self.initConfiguration()
|
||||
|
||||
|
||||
def onMessage(self, request, response):
|
||||
'''
|
||||
request: http://bottlepy.org/docs/dev/api.html#the-request-object
|
||||
response: http://bottlepy.org/docs/dev/api.html#the-response-object
|
||||
|
||||
'''
|
||||
if request.body:
|
||||
arequest = request.body.read()
|
||||
request.body.close()
|
||||
try:
|
||||
requestDict = json.loads(arequest)
|
||||
except ValueError as e:
|
||||
response.status = 500
|
||||
|
||||
|
||||
print(requestDict, requestDict.keys())
|
||||
if 'ipaddress' in requestDict.keys():
|
||||
url="https://cymon.io/api/nexus/v1/ip/{0}/events?combined=true&format=json".format(requestDict['ipaddress'])
|
||||
|
||||
# add the cymon api key?
|
||||
if self.options is not None:
|
||||
headers = {'Authorization': 'Token {0}'.format(self.options.cymonapikey)}
|
||||
|
||||
else:
|
||||
headers = None
|
||||
|
||||
dresponse = requests.get(url, headers=headers)
|
||||
if dresponse.status_code == 200:
|
||||
response.content_type = "application/json"
|
||||
response.body = dresponse.content
|
||||
response.status=200
|
||||
else:
|
||||
response.status = dresponse.status_code
|
||||
|
||||
else:
|
||||
response.status = 500
|
||||
|
||||
return (request, response)
|
||||
|
||||
|
||||
def initConfiguration(self):
|
||||
myparser = OptionParser()
|
||||
# setup self.options by sending empty list [] to parse_args
|
||||
(self.options, args) = myparser.parse_args([])
|
||||
|
||||
# fill self.options with plugin-specific options
|
||||
|
||||
# cymon options
|
||||
self.options.cymonapikey = getConfig('cymonapikey',
|
||||
'',
|
||||
self.configfile)
|
||||
|
|
@ -64,7 +64,7 @@ class message(object):
|
|||
|
||||
# set my own conf file
|
||||
# relative path to the rest index.py file
|
||||
self.configfile = './plugins/facebookThreatExchange.conf'
|
||||
self.configfile = './plugins/facebook_threatexchange.conf'
|
||||
self.options = None
|
||||
if os.path.exists(self.configfile):
|
||||
sys.stdout.write('found conf file {0}\n'.format(self.configfile))
|
|
@ -43,18 +43,17 @@ class message(object):
|
|||
(i.e. blockip matches /blockip)
|
||||
set the priority if you have a preference for order of plugins
|
||||
0 goes first, 100 is assumed/default if not sent
|
||||
|
||||
|
||||
Plugins will register in Meteor with attributes:
|
||||
name: (as below)
|
||||
description: (as below)
|
||||
priority: (as below)
|
||||
file: "plugins.filename" where filename.py is the plugin code.
|
||||
|
||||
|
||||
Plugin gets sent main rest options as:
|
||||
self.restoptions
|
||||
self.restoptions['configfile'] will be the .conf file
|
||||
used by the restapi's index.py file.
|
||||
|
||||
'''
|
||||
|
||||
self.registration = ['blockip']
|
||||
|
@ -64,13 +63,12 @@ class message(object):
|
|||
|
||||
# set my own conf file
|
||||
# relative path to the rest index.py file
|
||||
self.configfile = './plugins/vpcblackhole.conf'
|
||||
self.configfile = './plugins/vpc_blackhole.conf'
|
||||
self.options = None
|
||||
self.multioptions = []
|
||||
if os.path.exists(self.configfile):
|
||||
sys.stdout.write('found conf file {0}\n'.format(self.configfile))
|
||||
self.initConfiguration()
|
||||
|
||||
|
||||
def initConfiguration(self):
|
||||
myparser = ConfigParser.ConfigParser()
|
||||
|
@ -81,7 +79,7 @@ class message(object):
|
|||
cur_options = myparser.options(cur_section)
|
||||
if cur_options is not None:
|
||||
self.multioptions.append({ 'region': myparser.get(cur_section, 'region'), 'aws_access_key_id': myparser.get(cur_section, 'aws_access_key_id'), 'aws_secret_access_key': myparser.get(cur_section, 'aws_secret_access_key') } )
|
||||
|
||||
|
||||
def addBlackholeEntry(self,
|
||||
ipaddress=None):
|
||||
try:
|
||||
|
@ -154,7 +152,7 @@ class message(object):
|
|||
else:
|
||||
sys.stdout.write('Skipping route table {0} in the VPC {1} - blackhole ENI could not be found\n'.format(rt_id, vpc_id))
|
||||
continue
|
||||
|
||||
|
||||
except Exception as e:
|
||||
sys.stderr.write('Error while creating a blackhole entry %s: %r\n' % (ipaddress, e))
|
||||
|
||||
|
@ -163,16 +161,16 @@ class message(object):
|
|||
'''
|
||||
request: http://bottlepy.org/docs/dev/api.html#the-request-object
|
||||
response: http://bottlepy.org/docs/dev/api.html#the-response-object
|
||||
|
||||
'''
|
||||
|
||||
'''
|
||||
# format/validate request.json:
|
||||
ipaddress = None
|
||||
CIDR = None
|
||||
sendToBHVPC = False
|
||||
|
||||
|
||||
# loop through the fields of the form
|
||||
# and fill in our values
|
||||
try:
|
||||
try:
|
||||
for i in request.json:
|
||||
# were we checked?
|
||||
if self.name in i.keys():
|
||||
|
@ -197,5 +195,5 @@ class message(object):
|
|||
sys.stdout.write ('Blackholed {0}\n'.format(ipaddress))
|
||||
except Exception as e:
|
||||
sys.stderr.write('Error handling request.json %r \n'% (e))
|
||||
|
||||
|
||||
return (request, response)
|
|
@ -1,5 +1,5 @@
|
|||
[Unit]
|
||||
Description=uWSGI mozdefloginput
|
||||
Description=uWSGI MozDef Loginput Service
|
||||
After=rabbitmq-server.service
|
||||
|
||||
[Service]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[Unit]
|
||||
Description=uWSGI MozDef Log Input Service
|
||||
Description=uWSGI MozDef Loginput Service
|
||||
After=rabbitmq-server.service
|
||||
|
||||
[Service]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[Unit]
|
||||
Description=uWSGI eventtask mworker
|
||||
Description=uWSGI MozDef Eventtask mworker Service
|
||||
After=rabbitmq-server.service
|
||||
|
||||
[Service]
|
||||
|
@ -9,7 +9,7 @@ ExecStartPre=-/usr/bin/mkdir -p /var/run/mozdef_mq_worker_pids
|
|||
ExecStartPre=/usr/bin/chown -R mozdef:mozdef /var/run/mozdef_mq_worker_pids
|
||||
User=mozdef
|
||||
Group=mozdef
|
||||
ExecStart=/bin/bash -c 'cd /opt/mozdef/envs/mozdef/mq/; source /opt/mozdef/envs/mozdef/bin/activate; uwsgi --ini eventtask.ini'
|
||||
ExecStart=/bin/bash -c 'cd /opt/mozdef/envs/mozdef/mq; source /opt/mozdef/envs/mozdef/bin/activate; uwsgi --ini eventtask.ini'
|
||||
Restart=always
|
||||
KillSignal=SIGQUIT
|
||||
Type=notify
|
||||
|
@ -18,4 +18,5 @@ NotifyAccess=all
|
|||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=eventtask
|
||||
Alias=eventtask
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[Unit]
|
||||
Description=uWSGI mozdef restapi
|
||||
Description=uWSGI MozDef Rest API Service
|
||||
After=mozdefweb.service
|
||||
|
||||
[Service]
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
from positive_alert_test_case import PositiveAlertTestCase
|
||||
from negative_alert_test_case import NegativeAlertTestCase
|
||||
|
||||
from alert_test_suite import AlertTestSuite
|
||||
|
||||
|
||||
class TestSSHKey(AlertTestSuite):
|
||||
alert_filename = 'ssh_key'
|
||||
alert_classname = 'SSHKey'
|
||||
|
||||
# This event is the default positive event that will cause the
|
||||
# alert to trigger
|
||||
default_event = {
|
||||
'_type': 'event',
|
||||
'_source': {
|
||||
'category': 'event',
|
||||
'processid': '19690',
|
||||
'severity': 'INFO',
|
||||
'tags': [
|
||||
'mig-runner-sshkey'
|
||||
],
|
||||
'hostname': 'mig-runner-host.mozilla.com',
|
||||
'mozdefhostname': 'mozdef-host.mozilla.com',
|
||||
'summary': 'MIG sshkey module result for key-host.mozilla.com',
|
||||
'processname': '/home/migrunner/runner/plugins/sshkeyplugin.py',
|
||||
'details': {
|
||||
'authorizedkeys': [
|
||||
{
|
||||
'fingerprint_sha256': 'SHA256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
||||
'encrypted': False,
|
||||
'fingerprint_md5': '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00',
|
||||
'path': '/home/user/.ssh/authorized_keys'
|
||||
}
|
||||
],
|
||||
'private': [
|
||||
{
|
||||
'fingerprint_sha256': 'SHA256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
||||
'encrypted': False,
|
||||
'fingerprint_md5': '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00',
|
||||
'path': '/home/user/.ssh/id_rsa'
|
||||
},
|
||||
{
|
||||
'fingerprint_sha256': 'SHA256:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB',
|
||||
'encrypted': False,
|
||||
'fingerprint_md5': '11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11',
|
||||
'path': '/home/user2/.ssh/id_rsa'
|
||||
}
|
||||
],
|
||||
'agent': 'key-host.mozilla.com',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# This alert is the expected result from running this task
|
||||
default_alert = {
|
||||
'category': 'sshkey',
|
||||
'severity': 'WARNING',
|
||||
'summary': 'Private keys detected on key-host.mozilla.com missing from whitelist',
|
||||
'tags': ['sshkey'],
|
||||
}
|
||||
|
||||
test_cases = []
|
||||
|
||||
test_cases.append(
|
||||
PositiveAlertTestCase(
|
||||
description='Positive test case with good event',
|
||||
events=[AlertTestSuite.create_event(default_event)],
|
||||
expected_alert=default_alert
|
||||
)
|
||||
)
|
||||
|
||||
event = AlertTestSuite.create_event(default_event)
|
||||
event['_type'] = 'audit'
|
||||
test_cases.append(
|
||||
NegativeAlertTestCase(
|
||||
description="Negative test case with bad event type",
|
||||
events=[event],
|
||||
)
|
||||
)
|
||||
|
||||
event = AlertTestSuite.create_event(default_event)
|
||||
event['_source']['details']['agent'] = 'somehost.ignorehosts.com'
|
||||
event['_source']['details']['private'] = [
|
||||
{
|
||||
'fingerprint_sha256': 'SHA256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
||||
'encrypted': False,
|
||||
'fingerprint_md5': '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00',
|
||||
'path': '/home/user/.ssh/id_rsa'
|
||||
}
|
||||
]
|
||||
test_cases.append(
|
||||
NegativeAlertTestCase(
|
||||
description='Whitelist test with default configuration file',
|
||||
events=[event],
|
||||
)
|
||||
)
|
||||
|
||||
event = AlertTestSuite.create_event(default_event)
|
||||
event['_source']['details']['agent'] = 'somehost.ignorehosts.com'
|
||||
event['_source']['details']['private'] = [
|
||||
{
|
||||
'fingerprint_sha256': 'SHA256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
||||
'encrypted': False,
|
||||
'fingerprint_md5': '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00',
|
||||
'path': '/home/user2/.ssh/id_rsa'
|
||||
}
|
||||
]
|
||||
specific_alert = default_alert.copy()
|
||||
specific_alert['summary'] = 'Private keys detected on somehost.ignorehosts.com missing from whitelist'
|
||||
test_cases.append(
|
||||
PositiveAlertTestCase(
|
||||
description='Whitelist test with default configuration file, only host matches',
|
||||
events=[event],
|
||||
expected_alert=specific_alert
|
||||
)
|
||||
)
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2017 Mozilla Corporation
|
||||
|
||||
from positive_alert_test_case import PositiveAlertTestCase
|
||||
from negative_alert_test_case import NegativeAlertTestCase
|
||||
|
||||
from alert_test_suite import AlertTestSuite
|
||||
|
||||
|
||||
class TestAlertSSHPasswordAuthViolation(AlertTestSuite):
|
||||
alert_filename = "ssh_password_auth_violation"
|
||||
|
||||
# This event is the default positive event that will cause the
|
||||
# alert to trigger
|
||||
default_event = {
|
||||
"_type": "event",
|
||||
"_source": {
|
||||
"tags": ["ssh_password_auth_policy_violation"],
|
||||
"details": {
|
||||
"destinationipaddress": "1.2.3.4",
|
||||
"destinationport": 22,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# This alert is the expected result from running this task
|
||||
default_alert = {
|
||||
"category": "ssh_password_auth_policy_violation",
|
||||
"tags": ['ssh_password_auth_policy_violation'],
|
||||
"severity": "WARNING",
|
||||
"summary": 'SSH password authentication allowed on 1.2.3.4',
|
||||
}
|
||||
|
||||
test_cases = []
|
||||
|
||||
test_cases.append(
|
||||
PositiveAlertTestCase(
|
||||
description="Positive test with default events and default alert expected",
|
||||
events=AlertTestSuite.create_events(default_event, 1),
|
||||
expected_alert=default_alert
|
||||
)
|
||||
)
|
||||
|
||||
events = AlertTestSuite.create_events(default_event, 10)
|
||||
for event in events:
|
||||
event['_type'] = 'bad'
|
||||
test_cases.append(
|
||||
NegativeAlertTestCase(
|
||||
description="Negative test case with events with incorrect _type",
|
||||
events=events,
|
||||
)
|
||||
)
|
||||
|
||||
events = AlertTestSuite.create_events(default_event, 10)
|
||||
for event in events:
|
||||
event['_source']['tags'] = 'bad tag example'
|
||||
test_cases.append(
|
||||
NegativeAlertTestCase(
|
||||
description="Negative test case with events with incorrect tags",
|
||||
events=events,
|
||||
)
|
||||
)
|
||||
|
||||
events = AlertTestSuite.create_events(default_event, 10)
|
||||
for event in events:
|
||||
event['_source']['utctimestamp'] = AlertTestSuite.subtract_from_timestamp_lambda({'minutes': 241})
|
||||
event['_source']['receivedtimestamp'] = AlertTestSuite.subtract_from_timestamp_lambda({'minutes': 241})
|
||||
test_cases.append(
|
||||
NegativeAlertTestCase(
|
||||
description="Negative test case with old timestamp",
|
||||
events=events,
|
||||
)
|
||||
)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче