зеркало из https://github.com/mozilla/MozDef.git
Remove attackers cron and visualization
This commit is contained in:
Родитель
b203c087f1
Коммит
8e78be60b2
|
@ -241,9 +241,6 @@ Resources:
|
|||
cookiename=sesmeteor
|
||||
# Increase the AWS ES total fields limit from 1000 to 4000
|
||||
OPTIONS_MAPPING_TOTAL_FIELDS_LIMIT=4000
|
||||
# Set thresholds for attack dataviz lower means more ogres
|
||||
OPTIONS_IPV4ATTACKERHITCOUNT=5
|
||||
OPTIONS_IPV4ATTACKERPREFIXLENGTH=24
|
||||
OPTIONS_ALERTSQSQUEUEURL=${AlertQueueUrl}
|
||||
OPTIONS_MQPROTOCOL=sqs
|
||||
DEFAULT_AWS_REGION=${AWS::Region}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
[options]
|
||||
esservers=http://localhost:9200
|
||||
mongoport=3002
|
||||
autocategorize = True
|
||||
categorymapping = [{"bruteforce":"bruteforcer"},{"nothing":"nothing"}]
|
||||
broadcastattackers = True
|
||||
mqserver=localhost
|
||||
mquser=guest
|
||||
mqpassword=guest
|
|
@ -1,473 +0,0 @@
|
|||
#!/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 https://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2014 Mozilla Corporation
|
||||
|
||||
import collections
|
||||
import json
|
||||
import random
|
||||
import netaddr
|
||||
import sys
|
||||
from bson.son import SON
|
||||
from datetime import datetime
|
||||
from configlib import getConfig, OptionParser
|
||||
from pymongo import MongoClient
|
||||
from collections import Counter
|
||||
from kombu import Connection, Exchange
|
||||
|
||||
from mozdef_util.utilities.logger import logger
|
||||
from mozdef_util.utilities.toUTC import toUTC
|
||||
from mozdef_util.elasticsearch_client import ElasticsearchClient
|
||||
from mozdef_util.query_models import SearchQuery, PhraseMatch
|
||||
|
||||
|
||||
def isIPv4(ip):
|
||||
try:
|
||||
# netaddr on it's own considers 1 and 0 to be valid_ipv4
|
||||
# so a little sanity check prior to netaddr.
|
||||
# Use IPNetwork instead of valid_ipv4 to allow CIDR
|
||||
if '.' in ip and len(ip.split('.'))==4:
|
||||
# some ips are quoted
|
||||
netaddr.IPNetwork(ip.strip("'").strip('"'))
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def genMeteorID():
|
||||
return('%024x' % random.randrange(16**24))
|
||||
|
||||
|
||||
def keypaths(nested):
|
||||
''' return a list of nested dict key paths
|
||||
like: [u'_source', u'details', u'hostname']
|
||||
'''
|
||||
for key, value in nested.items():
|
||||
if isinstance(value, collections.Mapping):
|
||||
for subkey, subvalue in keypaths(value):
|
||||
yield [key] + subkey, subvalue
|
||||
else:
|
||||
yield [key], value
|
||||
|
||||
|
||||
def dictpath(path):
|
||||
''' split a string representing a
|
||||
nested dictionary path key.subkey.subkey
|
||||
'''
|
||||
for i in path.split('.'):
|
||||
yield '{0}'.format(i)
|
||||
|
||||
|
||||
def mostCommon(listofdicts,dictkeypath):
|
||||
"""
|
||||
Given a list containing dictionaries,
|
||||
return the most common entries
|
||||
along a key path separated by .
|
||||
i.e. dictkey.subkey.subkey
|
||||
returned as a list of tuples
|
||||
[(value,count),(value,count)]
|
||||
"""
|
||||
inspectlist=list()
|
||||
path=list(dictpath(dictkeypath))
|
||||
for i in listofdicts:
|
||||
for k in list(keypaths(i)):
|
||||
if not (set(k[0]).symmetric_difference(path)):
|
||||
inspectlist.append(k[1])
|
||||
|
||||
return Counter(inspectlist).most_common()
|
||||
|
||||
|
||||
def searchESForBROAttackers(es, threshold):
|
||||
search_query = SearchQuery(hours=2)
|
||||
search_query.add_must([
|
||||
PhraseMatch('category', 'bro'),
|
||||
PhraseMatch('details.note', 'MozillaHTTPErrors::Excessive_HTTP_Errors_Attacker')
|
||||
])
|
||||
full_results = search_query.execute(es)
|
||||
results = full_results['hits']
|
||||
|
||||
# Hit count is buried in the 'sub' field
|
||||
# as: 'sub': u'6 in 1.0 hr, eps: 0'
|
||||
# cull the records for hitcounts over the threshold before returning
|
||||
attackers = list()
|
||||
for r in results:
|
||||
hitcount = int(r['_source']['details']['sub'].split()[0])
|
||||
if hitcount > threshold:
|
||||
attackers.append(r)
|
||||
return attackers
|
||||
|
||||
|
||||
def searchMongoAlerts(mozdefdb):
|
||||
attackers = mozdefdb['attackers']
|
||||
alerts = mozdefdb['alerts']
|
||||
# search the last X alerts for IP addresses
|
||||
# aggregated by CIDR mask/24
|
||||
|
||||
# aggregate IPv4 addresses in the most recent alerts
|
||||
# to find common attackers.
|
||||
ipv4TopHits = alerts.aggregate([
|
||||
# reverse sort the current alerts
|
||||
{"$sort": {"utcepoch": -1}},
|
||||
# most recent 100
|
||||
{"$limit": 100},
|
||||
# must have an ip address
|
||||
{"$match": {"events.documentsource.details.sourceipaddress": {"$exists": True}}},
|
||||
# must not be already related to an attacker
|
||||
{"$match": {"attackerid": {"$exists": False}}},
|
||||
# make each event into it's own doc
|
||||
{"$unwind": "$events"},
|
||||
{"$project": {
|
||||
"_id": 0,
|
||||
# emit the source ip only
|
||||
"sourceip": "$events.documentsource.details.sourceipaddress"
|
||||
}},
|
||||
# count by ip
|
||||
{"$group": {"_id": "$sourceip", "hitcount": {"$sum": 1}}},
|
||||
# limit to those with X observances
|
||||
{"$match": {"hitcount": {"$gt": options.ipv4attackerhitcount}}},
|
||||
# sort
|
||||
{"$sort": SON([("hitcount", -1), ("_id", -1)])},
|
||||
# top 10
|
||||
{"$limit": 10}
|
||||
])
|
||||
for ip in ipv4TopHits:
|
||||
# sanity check ip['_id'] which should be the ipv4 address
|
||||
if isIPv4(ip['_id']) and ip['_id'] not in netaddr.IPSet(['0.0.0.0']):
|
||||
ipcidr = netaddr.IPNetwork(ip['_id'])
|
||||
# set CIDR
|
||||
# todo: lookup ipwhois for asn_cidr value
|
||||
# potentially with a max mask value (i.e. asn is /8, limit attackers to /24)
|
||||
ipcidr.prefixlen = options.ipv4attackerprefixlength
|
||||
|
||||
# append to or create attacker.
|
||||
# does this match an existing attacker's indicators
|
||||
if not ipcidr.ip.is_loopback() and not ipcidr.ip.is_private() and not ipcidr.ip.is_reserved():
|
||||
logger.debug('Searching for existing attacker with ip ' + str(ipcidr))
|
||||
attacker = attackers.find_one({'indicators.ipv4address': str(ipcidr)})
|
||||
|
||||
if attacker is None:
|
||||
logger.debug('Attacker not found, creating new one')
|
||||
# new attacker
|
||||
# generate a meteor-compatible ID
|
||||
# save the ES document type, index, id
|
||||
newAttacker = genNewAttacker()
|
||||
|
||||
# str to get the ip/cidr rather than netblock cidr.
|
||||
# i.e. '1.2.3.4/24' not '1.2.3.0/24'
|
||||
newAttacker['indicators'].append(dict(ipv4address=str(ipcidr)))
|
||||
matchingalerts = alerts.find(
|
||||
{"events.documentsource.details.sourceipaddress":
|
||||
str(ipcidr.ip),
|
||||
})
|
||||
total_events = 0
|
||||
if matchingalerts is not None:
|
||||
# update list of alerts this attacker matched.
|
||||
for alert in matchingalerts:
|
||||
newAttacker['alerts'].append(
|
||||
dict(alertid=alert['_id'])
|
||||
)
|
||||
# update alert with attackerID
|
||||
alert['attackerid'] = newAttacker['_id']
|
||||
alerts.save(alert)
|
||||
|
||||
total_events += len(alert['events'])
|
||||
if len(alert['events']) > 0:
|
||||
newAttacker['lastseentimestamp'] = toUTC(alert['events'][-1]['documentsource']['utctimestamp'])
|
||||
newAttacker['alertscount'] = len(newAttacker['alerts'])
|
||||
newAttacker['eventscount'] = total_events
|
||||
attackers.insert(newAttacker)
|
||||
# update geoIP info
|
||||
latestGeoIP = [a['events'] for a in alerts.find(
|
||||
{"events.documentsource.details.sourceipaddress":
|
||||
str(ipcidr.ip),
|
||||
})][-1][0]['documentsource']
|
||||
updateAttackerGeoIP(mozdefdb, newAttacker['_id'], latestGeoIP)
|
||||
|
||||
if options.broadcastattackers:
|
||||
broadcastAttacker(newAttacker)
|
||||
|
||||
else:
|
||||
logger.debug('Found existing attacker')
|
||||
# if alert not present in this attackers list
|
||||
# append this to the list
|
||||
# todo: trim the list at X (i.e. last 100)
|
||||
# search alerts without attackerid
|
||||
matchingalerts = alerts.find(
|
||||
{"events.documentsource.details.sourceipaddress":
|
||||
str(ipcidr.ip),
|
||||
"attackerid":{"$exists": False}
|
||||
})
|
||||
if matchingalerts is not None:
|
||||
logger.debug('Matched alert with attacker')
|
||||
|
||||
# update list of alerts this attacker matched.
|
||||
for alert in matchingalerts:
|
||||
attacker['alerts'].append(
|
||||
dict(alertid=alert['_id'])
|
||||
)
|
||||
# update alert with attackerID
|
||||
alert['attackerid'] = attacker['_id']
|
||||
alerts.save(alert)
|
||||
|
||||
attacker['eventscount'] += len(alert['events'])
|
||||
attacker['lastseentimestamp'] = toUTC(alert['events'][-1]['documentsource']['utctimestamp'])
|
||||
|
||||
# geo ip could have changed, update it to the latest
|
||||
updateAttackerGeoIP(mozdefdb, attacker['_id'], alert['events'][-1]['documentsource'])
|
||||
|
||||
# update counts
|
||||
attacker['alertscount'] = len(attacker['alerts'])
|
||||
attackers.save(attacker)
|
||||
|
||||
# should we autocategorize the attacker
|
||||
# based on their alerts?
|
||||
if attacker['category'] == 'unknown' and options.autocategorize:
|
||||
# take a look at recent alerts for this attacker
|
||||
# and if they are all the same category
|
||||
# auto-categorize the attacker
|
||||
matchingalerts = alerts.find(
|
||||
{"attackerid": attacker['_id']}
|
||||
).sort('utcepoch', -1).limit(50)
|
||||
# summarize the alert categories
|
||||
# returns list of tuples: [(u'bruteforce', 8)]
|
||||
categoryCounts= mostCommon(matchingalerts,'category')
|
||||
# are the alerts all the same category?
|
||||
|
||||
if len(categoryCounts) == 1:
|
||||
# is the alert category mapped to an attacker category?
|
||||
for category in options.categorymapping:
|
||||
if list(category.keys())[0] == categoryCounts[0][0]:
|
||||
attacker['category'] = category[list(category.keys())[0]]
|
||||
attackers.save(attacker)
|
||||
|
||||
|
||||
def broadcastAttacker(attacker):
|
||||
'''
|
||||
send this attacker info to our message queue
|
||||
'''
|
||||
try:
|
||||
connString = 'amqp://{0}:{1}@{2}:{3}/{4}'.format(options.mquser,
|
||||
options.mqpassword,
|
||||
options.mqserver,
|
||||
options.mqport,
|
||||
options.mqvhost)
|
||||
if options.mqprotocol == 'amqps':
|
||||
mqSSL = True
|
||||
else:
|
||||
mqSSL = False
|
||||
mqConn = Connection(connString, ssl=mqSSL)
|
||||
|
||||
alertExchange = Exchange(
|
||||
name=options.alertexchange,
|
||||
type='topic',
|
||||
durable=True)
|
||||
alertExchange(mqConn).declare()
|
||||
mqproducer = mqConn.Producer(serializer='json')
|
||||
|
||||
logger.debug('Kombu configured')
|
||||
except Exception as e:
|
||||
logger.error('Exception while configuring kombu for alerts: {0}'.format(e))
|
||||
try:
|
||||
# generate an 'alert' structure for this attacker:
|
||||
mqAlert = dict(severity='NOTICE', category='attacker')
|
||||
|
||||
if 'datecreated' in attacker:
|
||||
mqAlert['utctimestamp'] = attacker['datecreated'].isoformat()
|
||||
|
||||
mqAlert['summary'] = 'New Attacker: {0} events: {1}, alerts: {2}'.format(attacker['indicators'], attacker['eventscount'], attacker['alertscount'])
|
||||
logger.debug(mqAlert)
|
||||
ensurePublish = mqConn.ensure(
|
||||
mqproducer,
|
||||
mqproducer.publish,
|
||||
max_retries=10)
|
||||
ensurePublish(
|
||||
mqAlert,
|
||||
exchange=alertExchange,
|
||||
routing_key=options.routingkey
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error('Exception while publishing attacker: {0}'.format(e))
|
||||
|
||||
|
||||
def genNewAttacker():
|
||||
newAttacker = dict()
|
||||
newAttacker['_id'] = genMeteorID()
|
||||
newAttacker['lastseentimestamp'] = toUTC(datetime.now())
|
||||
newAttacker['firstseentimestamp'] = toUTC(datetime.now())
|
||||
newAttacker['eventscount'] = 0
|
||||
newAttacker['alerts'] = list()
|
||||
newAttacker['alertscount'] = 0
|
||||
newAttacker['category'] = 'unknown'
|
||||
newAttacker['score'] = 0
|
||||
newAttacker['geocoordinates'] = dict(countrycode='', longitude=0, latitude=0)
|
||||
newAttacker['tags'] = list()
|
||||
newAttacker['notes'] = list()
|
||||
newAttacker['indicators'] = list()
|
||||
newAttacker['attackphase'] = 'unknown'
|
||||
newAttacker['datecreated'] = toUTC(datetime.now())
|
||||
newAttacker['creator'] = sys.argv[0]
|
||||
|
||||
return newAttacker
|
||||
|
||||
|
||||
def updateAttackerGeoIP(mozdefdb, attackerID, eventDictionary):
|
||||
'''given an attacker ID and a dictionary of an elastic search event
|
||||
look for a valid geoIP in the dict and update the attacker's geo coordinates
|
||||
'''
|
||||
|
||||
# geo ip should be in eventDictionary['details']['sourceipgeolocation']
|
||||
# "sourceipgeolocation": {
|
||||
# "city": "Polska",
|
||||
# "region_code": "73",
|
||||
# "area_code": 0,
|
||||
# "time_zone": "Europe/Warsaw",
|
||||
# "dma_code": 0,
|
||||
# "metro_code": null,
|
||||
# "country_code3": "POL",
|
||||
# "latitude": 52.59309999999999,
|
||||
# "postal_code": null,
|
||||
# "longitude": 19.089400000000012,
|
||||
# "country_code": "PL",
|
||||
# "country_name": "Poland",
|
||||
# "continent": "EU"
|
||||
# }
|
||||
# logger.debug(eventDictionary)
|
||||
if 'details' in eventDictionary:
|
||||
if 'sourceipgeolocation' in eventDictionary['details']:
|
||||
attackers=mozdefdb['attackers']
|
||||
attacker = attackers.find_one({'_id': attackerID})
|
||||
if attacker is not None:
|
||||
attacker['geocoordinates'] = dict(countrycode='',
|
||||
longitude=0,
|
||||
latitude=0)
|
||||
if 'country_code' in eventDictionary['details']['sourceipgeolocation']:
|
||||
attacker['geocoordinates']['countrycode'] = eventDictionary['details']['sourceipgeolocation']['country_code']
|
||||
if 'longitude' in eventDictionary['details']['sourceipgeolocation']:
|
||||
attacker['geocoordinates']['longitude'] = eventDictionary['details']['sourceipgeolocation']['longitude']
|
||||
if 'latitude' in eventDictionary['details']['sourceipgeolocation']:
|
||||
attacker['geocoordinates']['latitude'] = eventDictionary['details']['sourceipgeolocation']['latitude']
|
||||
attackers.save(attacker)
|
||||
else:
|
||||
logger.debug('no details in the dictionary')
|
||||
logger.debug(eventDictionary)
|
||||
|
||||
|
||||
def updateMongoWithESEvents(mozdefdb, results):
|
||||
logger.debug('Looping through events identified as malicious from bro')
|
||||
attackers = mozdefdb['attackers']
|
||||
for r in results:
|
||||
if 'sourceipaddress' in r['_source']['details']:
|
||||
if netaddr.valid_ipv4(r['_source']['details']['sourceipaddress']):
|
||||
sourceIP = netaddr.IPNetwork(r['_source']['details']['sourceipaddress'])
|
||||
# expand it to a /24 CIDR
|
||||
# todo: lookup ipwhois for asn_cidr value
|
||||
# potentially with a max mask value (i.e. asn is /8, limit attackers to /24)
|
||||
sourceIP.prefixlen = 24
|
||||
if not sourceIP.ip.is_loopback() and not sourceIP.ip.is_private() and not sourceIP.ip.is_reserved():
|
||||
esrecord = dict(
|
||||
documentid=r['_id'],
|
||||
documentindex=r['_index'],
|
||||
documentsource=r['_source']
|
||||
)
|
||||
|
||||
logger.debug('Trying to find existing attacker at ' + str(sourceIP))
|
||||
attacker = attackers.find_one({'indicators.ipv4address': str(sourceIP)})
|
||||
if attacker is None:
|
||||
# new attacker
|
||||
# generate a meteor-compatible ID
|
||||
# save the ES document type, index, id
|
||||
# and add a sub list for future events
|
||||
logger.debug('Creating new attacker from ' + str(sourceIP))
|
||||
newAttacker = genNewAttacker()
|
||||
|
||||
# expand the source ip to a /24 for the indicator match.
|
||||
sourceIP.prefixlen = 24
|
||||
# str sourceIP to get the ip/cidr rather than netblock cidr.
|
||||
newAttacker['indicators'].append(dict(ipv4address=str(sourceIP)))
|
||||
newAttacker['eventscount'] = 1
|
||||
newAttacker['lastseentimestamp'] = esrecord['documentsource']['utctimestamp']
|
||||
attackers.insert(newAttacker)
|
||||
updateAttackerGeoIP(mozdefdb, newAttacker['_id'], esrecord['documentsource'])
|
||||
else:
|
||||
logger.debug('Attacker found, increasing eventscount and modding geoip')
|
||||
attacker['eventscount'] += 1
|
||||
attacker['lastseentimestamp'] = esrecord['documentsource']['utctimestamp']
|
||||
attackers.save(attacker)
|
||||
# geo ip could have changed, update it
|
||||
updateAttackerGeoIP(mozdefdb, attacker['_id'], esrecord['documentsource'])
|
||||
|
||||
|
||||
def main():
|
||||
logger.debug('starting')
|
||||
logger.debug(options)
|
||||
try:
|
||||
es = ElasticsearchClient((list('{0}'.format(s) for s in options.esservers)))
|
||||
client = MongoClient(options.mongohost, options.mongoport)
|
||||
# use meteor db
|
||||
mozdefdb = client.meteor
|
||||
esResults = searchESForBROAttackers(es, 100)
|
||||
updateMongoWithESEvents(mozdefdb, esResults)
|
||||
searchMongoAlerts(mozdefdb)
|
||||
|
||||
except ValueError as e:
|
||||
logger.error("Exception %r collecting attackers to mongo" % e)
|
||||
|
||||
|
||||
def initConfig():
|
||||
# output our log to stdout or syslog
|
||||
options.output = getConfig('output', 'stdout', options.configfile)
|
||||
# syslog hostname
|
||||
options.sysloghostname = getConfig('sysloghostname',
|
||||
'localhost',
|
||||
options.configfile)
|
||||
# syslog port
|
||||
options.syslogport = getConfig('syslogport', 514, options.configfile)
|
||||
|
||||
# elastic search server settings
|
||||
options.esservers = list(getConfig('esservers',
|
||||
'http://localhost:9200',
|
||||
options.configfile).split(','))
|
||||
options.mongohost = getConfig('mongohost', 'localhost', options.configfile)
|
||||
options.mongoport = getConfig('mongoport', 3001, options.configfile)
|
||||
|
||||
# should we automatically categorize
|
||||
# new attackers based on their alerts?
|
||||
options.autocategorize = getConfig('autocategorize', False, options.configfile)
|
||||
# get the mapping of alert category to attacker category
|
||||
# supply as a list of dicts:
|
||||
# [{"bruteforce":"bruteforcer"},{"alertcategory":"attackercategory"}]
|
||||
options.categorymapping = json.loads(getConfig('categorymapping', "[]", options.configfile))
|
||||
|
||||
# should we broadcast new attackers
|
||||
# to a message queue?
|
||||
options.broadcastattackers = getConfig('broadcastattackers', False, options.configfile)
|
||||
# message queue options
|
||||
options.mqserver = getConfig('mqserver', 'localhost', options.configfile)
|
||||
options.alertexchange = getConfig('alertexchange', 'alerts', options.configfile)
|
||||
options.routingkey = getConfig('routingkey', 'mozdef.alert', options.configfile)
|
||||
options.mquser = getConfig('mquser', 'guest', options.configfile)
|
||||
options.mqpassword = getConfig('mqpassword', 'guest', options.configfile)
|
||||
options.mqport = getConfig('mqport', 5672, options.configfile)
|
||||
options.mqvhost = getConfig('mqvhost', '/', options.configfile)
|
||||
# set to either amqp or amqps for ssl
|
||||
options.mqprotocol = getConfig('mqprotocol', 'amqp', options.configfile)
|
||||
|
||||
# Set these settings to change the correlation for attackers
|
||||
options.ipv4attackerprefixlength = getConfig('ipv4attackerprefixlength', 32, options.configfile)
|
||||
options.ipv4attackerhitcount = getConfig('ipv4ipv4attackerhitcount', 5, options.configfile)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = OptionParser()
|
||||
parser.add_option(
|
||||
"-c",
|
||||
dest='configfile',
|
||||
default=sys.argv[0].replace('.py', '.conf'),
|
||||
help="configuration file to use")
|
||||
(options, args) = parser.parse_args()
|
||||
initConfig()
|
||||
main()
|
|
@ -1,9 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# 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 https://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2014 Mozilla Corporation
|
||||
|
||||
source /opt/mozdef/envs/python/bin/activate
|
||||
/opt/mozdef/envs/mozdef/cron/collectAttackers.py -c /opt/mozdef/envs/mozdef/cron/collectAttackers.conf
|
|
@ -13,7 +13,6 @@ COPY docker/compose/mozdef_cron/files/cron_entries.txt /cron_entries.txt
|
|||
|
||||
# Copy config files for crons
|
||||
COPY --chown=mozdef:mozdef docker/compose/mozdef_cron/files/backup.conf /opt/mozdef/envs/mozdef/cron/backup.conf
|
||||
COPY --chown=mozdef:mozdef docker/compose/mozdef_cron/files/collectAttackers.conf /opt/mozdef/envs/mozdef/cron/collectAttackers.conf
|
||||
COPY --chown=mozdef:mozdef docker/compose/mozdef_cron/files/eventStats.conf /opt/mozdef/envs/mozdef/cron/eventStats.conf
|
||||
COPY --chown=mozdef:mozdef docker/compose/mozdef_cron/files/healthAndStatus.conf /opt/mozdef/envs/mozdef/cron/healthAndStatus.conf
|
||||
COPY --chown=mozdef:mozdef docker/compose/mozdef_cron/files/healthToMongo.conf /opt/mozdef/envs/mozdef/cron/healthToMongo.conf
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
[options]
|
||||
esservers=http://elasticsearch:9200
|
||||
mongohost=mongodb
|
||||
mongoport=3002
|
||||
autocategorize = True
|
||||
categorymapping = [{"bruteforce":"bruteforcer"},{"nothing":"nothing"}]
|
||||
broadcastattackers = True
|
||||
mqserver=rabbitmq
|
||||
mquser=guest
|
||||
mqpassword=guest
|
|
@ -1,7 +1,6 @@
|
|||
BASH_ENV=/env
|
||||
* * * * * /opt/mozdef/envs/mozdef/cron/healthAndStatus.sh
|
||||
* * * * * /opt/mozdef/envs/mozdef/cron/healthToMongo.sh
|
||||
* * * * * /opt/mozdef/envs/mozdef/cron/collectAttackers.sh
|
||||
* * * * * /opt/mozdef/envs/mozdef/cron/syncAlertsToMongo.sh
|
||||
* * * * * /opt/mozdef/envs/mozdef/cron/eventStats.sh
|
||||
0 0 * * * /opt/mozdef/envs/mozdef/cron/esMaint.sh
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
<!--
|
||||
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 https://mozilla.org/MPL/2.0/.
|
||||
Copyright (c) 2014 Mozilla Corporation
|
||||
-->
|
||||
|
||||
<template name="attackerdetails">
|
||||
<div class="container">
|
||||
<div class="row center">
|
||||
<div id="attacker" >
|
||||
<table class="table table-reactive table-hover table-condensed" id="alert-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="upperwhite">Last Seen</td>
|
||||
<td class="upperwhite">Created</td>
|
||||
<td class="upperwhite">Events</td>
|
||||
<td class="upperwhite">Alerts</td>
|
||||
<td class="upperwhite">Category</td>
|
||||
<td class="upperwhite">Summary</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{lastseentimestamp}}</td>
|
||||
<td>{{dateCreated}}</td>
|
||||
<td>{{eventscount}}</td>
|
||||
<td>{{alertscount}}</td>
|
||||
<td>
|
||||
<select id="attackerCategory" name="category" class="form-control" title="category of this attacker">
|
||||
<option {{ isselected 'unknown' category }} value='unknown'>Unknown</option>
|
||||
<option {{ isselected 'skiddie' category }} value='skiddie'>Script Kiddie</option>
|
||||
<option {{ isselected 'bruteforcer' category }} value='bruteforcer'>Brute Forcer</option>
|
||||
<option {{ isselected 'bountyhunter' category }} value='bountyhunter'>Bounty Hunter</option>
|
||||
<option {{ isselected 'apt' category }} value='apt'>APT</option>
|
||||
<option {{ isselected 'falsepositive' category }} value='falsepositive'>False Positive</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>{{{ipDecorate summary}}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row center">
|
||||
<caption><h5>Alerts</h5></caption>
|
||||
<table class="table table-reactive table-hover table-condensed " id="attacker-alerts-table">
|
||||
<tbody class="table-striped">
|
||||
{{#each this.alerts}}
|
||||
{{#with thisalert this.alertid}}
|
||||
{{>attacker_alert}}
|
||||
{{/with}}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<!--each individual alert -->
|
||||
<template name="attacker_alert">
|
||||
|
||||
<tr>
|
||||
<td><a href="{{getAlertURL _id}}" target="_blank">MozDef</a></td>
|
||||
<td>{{utctimestamp}}</td>
|
||||
<td>{{severity}}</td>
|
||||
<td>{{category}}</td>
|
||||
<td>{{{ ipDecorate summary}}}</td>
|
||||
</tr>
|
||||
|
||||
</template>
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
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 https://mozilla.org/MPL/2.0/.
|
||||
Copyright (c) 2014 Mozilla Corporation
|
||||
*/
|
||||
import { Meteor } from 'meteor/meteor'
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Session } from 'meteor/session';
|
||||
import { Tracker } from 'meteor/tracker';
|
||||
import { tooltip } from 'meteor/twbs:bootstrap';
|
||||
|
||||
if (Meteor.isClient) {
|
||||
|
||||
Template.attackerdetails.events({
|
||||
"change #attackerCategory": function(e,t){
|
||||
attackers.update(Session.get('attackerID'), {$set: {'category':$('#attackerCategory').val()}});
|
||||
}
|
||||
});
|
||||
|
||||
//details helpers
|
||||
Template.attackerdetails.thisalert = function (mongoalertid) {
|
||||
//attackers store a ref mongo id to alerts attacker.alerts.alertid
|
||||
//return an alert given it's mongo id
|
||||
return alerts.findOne({'_id': mongoalertid},
|
||||
{fields:{
|
||||
events:0}
|
||||
});
|
||||
};
|
||||
|
||||
Template.attackerdetails.rendered = function() {
|
||||
Deps.autorun(function(comp) {
|
||||
//subscribe to the alerts data we need
|
||||
Meteor.subscribe('attacker-details', Session.get('attackerID'),onReady=function(){
|
||||
|
||||
attackerAlerts=attackers.findOne({"_id":Session.get('attackerID')}).alerts;
|
||||
//subscribe to each alert minus the event details since
|
||||
//we already have sample events in the attacker data structure.
|
||||
attackerAlerts.forEach(function(a){
|
||||
Meteor.subscribe('alerts-details',a.alertid,false);
|
||||
});
|
||||
});
|
||||
|
||||
}); //end deps.autorun
|
||||
};
|
||||
|
||||
};
|
|
@ -1,81 +0,0 @@
|
|||
<!--
|
||||
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 https://mozilla.org/MPL/2.0/.
|
||||
Copyright (c) 2014 Mozilla Corporation
|
||||
-->
|
||||
|
||||
<template name="attackers">
|
||||
<!-- menu -->
|
||||
<sidenav>
|
||||
<form class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="attackerLimit">Limit </label>
|
||||
<div class="col-md-6">
|
||||
<select name="attackerLimit" id="attackerLimit" class="form-control input-small"
|
||||
title="limit how many attackers are retrieved">
|
||||
<option value='5'>5</option>
|
||||
<option value='10'>10</option>
|
||||
<option value='15'>15</option>
|
||||
<option value='20'>20</option>
|
||||
<option value='25'>25</option>
|
||||
<option value='30'>30</option>
|
||||
<option value='35'>35</option>
|
||||
<option value='40'>40</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="attackersearchIP">IP</label>
|
||||
<div class="col-md-8">
|
||||
<input type="search" name="attackersearchIP" class="form-control" id="attackersearchIP"
|
||||
placeholder="ip address regex search">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-xs-8 col-xs-offset-2">
|
||||
<button class="btn btn-xs attackerssearch">search</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="row reset-filter">
|
||||
<button id="btnResetFilter" class="btn">Reset Filters</button>
|
||||
</div>
|
||||
<div class="col-md-6 filters-col">
|
||||
<div class="row">
|
||||
<div id="ringChart-category" class="text-center">
|
||||
<h5 class="upperwhite" id="Categories">Categories:</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div id="ringChart-lastseen" class="text-center">
|
||||
<h5 class="upperwhite" id="LastSeen">Last Seen:</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 filters-col">
|
||||
<div class="row">
|
||||
<div id="ringChart-country" class="text-center">
|
||||
<h5 class="upperwhite" id="Countries">Country:</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div id="ringChart-ip" class="text-center">
|
||||
<h5 class="upperwhite" id="IPs">IP Address:</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</sidenav>
|
||||
<div class="attackers-wrapper" id="attackers-wrapper">
|
||||
|
||||
<div id="bottom-toolbar">
|
||||
<center>
|
||||
<ul class="list-inline">
|
||||
<li><button id="btnReset" class="btn btn-primary">Reset Layout</button></li>
|
||||
<li><button id="btnWireFrame" class="btn btn-primary">WireFrame</button></li>
|
||||
</ul>
|
||||
</center>
|
||||
</div>
|
||||
{{>blockIPModal}}
|
||||
</div>
|
||||
</template>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -23,10 +23,6 @@ import '/client/alertdetails.js';
|
|||
import '/client/alertssummary.html';
|
||||
import '/client/alertsummary.js';
|
||||
import '/client/alertTableItem.js'
|
||||
import '/client/attackerdetails.html';
|
||||
import '/client/attackerdetails.js';
|
||||
import '/client/attackers.html';
|
||||
import '/client/attackers.js';
|
||||
import '/client/blockFQDN.html';
|
||||
import '/client/blockFQDN.js';
|
||||
import '/client/blockIP.html';
|
||||
|
@ -60,8 +56,6 @@ import '/client/manageAlertsTable.html';
|
|||
import '/client/manageAlertsTable.js';
|
||||
import '/client/mozdefhealth.html';
|
||||
import '/client/mozdefhealth.js';
|
||||
import '/client/nameplate.html';
|
||||
import '/client/nameplate.js';
|
||||
import '/client/verisTags.html';
|
||||
import '/client/preferences.html';
|
||||
import '/client/preferences.js'
|
||||
|
|
|
@ -62,9 +62,6 @@ Copyright (c) 2014 Mozilla Corporation
|
|||
{{#if isFeature "logincounts"}}
|
||||
<li><a href="/logincounts/">logincounts</a></li>
|
||||
{{/if}}
|
||||
{{#if isFeature "attackers"}}
|
||||
<li><a href="/attackers">attackers</a></li>
|
||||
{{/if}}
|
||||
{{#if isFeature "globe"}}
|
||||
<li><a href="/globe">globe</a></li>
|
||||
{{/if}}
|
||||
|
|
|
@ -18,8 +18,6 @@ if ( Meteor.isClient ) {
|
|||
Session.set( 'alertssearchtext', '' );
|
||||
Session.set( 'alertssearchtime', '1 days' );
|
||||
Session.set( 'alertsfiltertext', '' );
|
||||
Session.set( 'attackerlimit', '10' );
|
||||
Session.set( 'attackersearchIP', '' );
|
||||
Session.set( 'blockIPipaddress', '' );
|
||||
Session.set( 'blockFQDN', '' );
|
||||
Session.set( 'watchItemwatchcontent', '' );
|
||||
|
@ -191,16 +189,6 @@ if ( Meteor.isClient ) {
|
|||
}
|
||||
} );
|
||||
|
||||
UI.registerHelper( 'getAttackerURL', function( attackerid ) {
|
||||
//return the router URL for a specific attacker
|
||||
return ( getSetting( 'rootURL' ) + '/attacker/' + attackerid );
|
||||
} );
|
||||
|
||||
UI.registerHelper( 'getAttackerIndicator', function( attackerid ) {
|
||||
//return the first indicator from a specific attacker
|
||||
return ( attackers.findOne( { '_id': attackerid } ).indicators[0].ipv4address );
|
||||
} );
|
||||
|
||||
UI.registerHelper( 'isselected', function( optionvalue, datavalue ) {
|
||||
if ( optionvalue == datavalue ) {
|
||||
return 'selected'
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<template name="nameplate">
|
||||
<div class="row-fluid">
|
||||
<span class="id">{{category}}</span>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<ul class="details">
|
||||
<li class="indicator">{{getAttackerIndicator _id}}</li>
|
||||
<li>Last Seen: {{lastseentimestamp}}</li>
|
||||
<li><a href="{{getAttackerURL _id}}" target="_blank">alerts: {{alertscount}}</a></li>
|
||||
<li><a href="{{getAttackerURL _id}}" target="_blank">events: {{eventscount}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
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 https://mozilla.org/MPL/2.0/.
|
||||
Copyright (c) 2014 Mozilla Corporation
|
||||
*/
|
||||
|
||||
if (Meteor.isClient) {
|
||||
|
||||
Template.nameplate.rendered = function() {
|
||||
Deps.autorun(function(comp) {
|
||||
//subscribe to the data we need
|
||||
Meteor.subscribe('attackers-summary');
|
||||
}); //end deps.autorun
|
||||
};
|
||||
|
||||
};
|
|
@ -142,26 +142,6 @@ Router.map(function() {
|
|||
layoutTemplate: 'layout'
|
||||
});
|
||||
|
||||
|
||||
this.route('attackers', {
|
||||
path: '/attackers',
|
||||
template: 'attackers',
|
||||
layoutTemplate: 'layout'
|
||||
});
|
||||
|
||||
this.route('attackerdetails', {
|
||||
path: '/attacker/:attackerid',
|
||||
template: 'attackerdetails',
|
||||
waitOn: function() {
|
||||
Session.set('attackerID', this.params.attackerid);
|
||||
return Meteor.subscribe('attacker-details', Session.get('attackerID'))
|
||||
},
|
||||
data: function() {
|
||||
return attackers.findOne({ '_id': Session.get('attackerID') });
|
||||
},
|
||||
layoutTemplate: 'layout'
|
||||
});
|
||||
|
||||
this.route('globe', {
|
||||
path: '/globe',
|
||||
template: 'globe',
|
||||
|
|
|
@ -62,9 +62,6 @@ Copyright (c) 2014 Mozilla Corporation
|
|||
{{#if isFeature "logincounts"}}
|
||||
<li><a href="/logincounts/">logincounts</a></li>
|
||||
{{/if}}
|
||||
{{#if isFeature "attackers"}}
|
||||
<li><a href="/attackers">attackers</a></li>
|
||||
{{/if}}
|
||||
{{#if isFeature "globe"}}
|
||||
<li><a href="/globe">globe</a></li>
|
||||
{{/if}}
|
||||
|
|
|
@ -132,11 +132,6 @@ Copyright (c) 2014 Mozilla Corporation
|
|||
<a href="/logincounts/">logincounts</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if isFeature "attackers"}}
|
||||
<li>
|
||||
<a href="/attackers">attackers</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if isFeature "globe"}}
|
||||
<li>
|
||||
<a href="/globe">globe</a>
|
||||
|
|
|
@ -25,7 +25,6 @@ Meteor.startup( () => {
|
|||
healthescluster = new Mongo.Collection( "healthescluster" );
|
||||
healthesnodes = new Mongo.Collection( "healthesnodes" );
|
||||
healtheshotthreads = new Mongo.Collection( "healtheshotthreads" );
|
||||
attackers = new Mongo.Collection( "attackers" );
|
||||
actions = new Mongo.Collection( "actions" );
|
||||
userActivity = new Mongo.Collection( "userActivity" );
|
||||
ipblocklist = new Mongo.Collection( "ipblocklist" );
|
||||
|
@ -219,33 +218,6 @@ Meteor.startup( () => {
|
|||
} );
|
||||
} );
|
||||
|
||||
Meteor.publish( "attacker-details", function( attackerid ) {
|
||||
return attackers.find( { '_id': attackerid },
|
||||
{
|
||||
fields: {
|
||||
events: { $slice: -20 },
|
||||
alerts: { $slice: -10 }
|
||||
},
|
||||
sort: { 'events.documentsource.utctimestamp': -1 },
|
||||
reactive: false
|
||||
}
|
||||
);
|
||||
} );
|
||||
|
||||
Meteor.publish( "attackers-summary", function() {
|
||||
//limit to the last 100 records by default
|
||||
//to ease the sync transfer to dc.js/crossfilter
|
||||
return attackers.find( {},
|
||||
{
|
||||
fields: {
|
||||
events: 0,
|
||||
alerts: 0,
|
||||
},
|
||||
sort: { lastseentimestamp: -1 },
|
||||
limit: 100
|
||||
} );
|
||||
} );
|
||||
|
||||
Meteor.publish( "investigation-details", function( investigationid ) {
|
||||
return investigations.find( { '_id': investigationid } );
|
||||
} );
|
||||
|
@ -321,13 +293,6 @@ Meteor.startup( () => {
|
|||
fetch: ['creator']
|
||||
} );
|
||||
|
||||
attackers.allow( {
|
||||
update: function( userId, doc, fields, modifier ) {
|
||||
// the user must be logged in
|
||||
return ( userId );
|
||||
}
|
||||
} );
|
||||
|
||||
alerts.allow( {
|
||||
update: function( userId, doc, fields, modifier ) {
|
||||
// the user must be logged in
|
||||
|
|
|
@ -138,27 +138,6 @@ models = {
|
|||
'lastModifier': ''
|
||||
};
|
||||
},
|
||||
attacker: function() {
|
||||
return {
|
||||
'_id': uuid(),
|
||||
'lastseentimestamp': today,
|
||||
'firstseentimestamp': '',
|
||||
'events': [],
|
||||
'eventscount': 0,
|
||||
'alerts': [],
|
||||
'alertscount': 0,
|
||||
'category': 'unknown',
|
||||
'score': 0,
|
||||
'geocoordinates': { 'countrycode': '', 'longitude': 0, 'lattitude': 0 },
|
||||
'tags': [],
|
||||
'notes': [],
|
||||
'indicators': [],
|
||||
'attackphase': 'unknown',
|
||||
'datecreated': today,
|
||||
'creator': '',
|
||||
'lastModifier': ''
|
||||
};
|
||||
},
|
||||
|
||||
credential: function() {
|
||||
return {
|
||||
|
|
|
@ -265,50 +265,6 @@ caption, legend {
|
|||
border-radius: 6px 0 6px 6px;
|
||||
}
|
||||
|
||||
.attackercallout {
|
||||
width: 120px;
|
||||
height: 160px;
|
||||
box-shadow: 0px 0px 12px rgba(0, 255, 255, 0.5);
|
||||
border: 1px solid rgba(127, 255, 255, 0.25);
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.attackercallout .id {
|
||||
font-size: 12px;
|
||||
color: rgba(127, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.attackercallout .details {
|
||||
text-align: left;
|
||||
left: 2px;
|
||||
font-size: 12px;
|
||||
color: rgba(127, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.attackercallout .blockip {
|
||||
color: var(--txt-primary-color);
|
||||
text-transform: uppercase;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.attackercallout ul {
|
||||
list-style: none;
|
||||
float: left;
|
||||
left: auto;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.attackercallout .indicator {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.attackercallout a {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.veris-wrapper td {
|
||||
color: black
|
||||
}
|
||||
|
@ -540,8 +496,6 @@ circle:hover {
|
|||
margin: .5em;
|
||||
}
|
||||
|
||||
/* attacker side nav pull out styling */
|
||||
|
||||
/* http://www.sitepoint.com/css3-sliding-menu/ */
|
||||
|
||||
sidenav {
|
||||
|
|
|
@ -258,50 +258,6 @@ legend {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.attackercallout {
|
||||
width: 120px;
|
||||
height: 160px;
|
||||
box-shadow: 0px 0px 12px rgba(0, 255, 255, 0.5);
|
||||
border: 1px solid rgba(127, 255, 255, 0.25);
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.attackercallout .id {
|
||||
font-size: 12px;
|
||||
color: rgba(127, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.attackercallout .details {
|
||||
text-align: left;
|
||||
left: 2px;
|
||||
font-size: 12px;
|
||||
color: rgba(127, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.attackercallout .blockip {
|
||||
color: #FFF;
|
||||
text-transform: uppercase;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.attackercallout ul {
|
||||
list-style: none;
|
||||
float: left;
|
||||
left: auto;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.attackercallout .indicator {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.attackercallout a {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.veris-wrapper td {
|
||||
color: black
|
||||
}
|
||||
|
|
|
@ -260,51 +260,6 @@ div.dc-chart {
|
|||
border-radius: 6px 0 6px 6px;
|
||||
}
|
||||
|
||||
.attackercallout {
|
||||
width: 120px;
|
||||
height: 160px;
|
||||
box-shadow: 0px 0px 12px rgba(0, 255, 255, 0.5);
|
||||
border: 1px solid rgba(127, 255, 255, 0.25);
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.attackercallout .id {
|
||||
font-size: 12px;
|
||||
color: rgba(127, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.attackercallout .details {
|
||||
text-align: left;
|
||||
left: 2px;
|
||||
font-size: 12px;
|
||||
color: rgba(127, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.attackercallout .blockip {
|
||||
color: var(--txt-primary-color);
|
||||
;
|
||||
text-transform: uppercase;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.attackercallout ul {
|
||||
list-style: none;
|
||||
float: left;
|
||||
left: auto;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.attackercallout .indicator {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.attackercallout a {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.veris-wrapper td {
|
||||
color: black
|
||||
}
|
||||
|
@ -557,10 +512,6 @@ circle:hover {
|
|||
margin: .5em;
|
||||
}
|
||||
|
||||
|
||||
/* attacker side nav pull out styling */
|
||||
|
||||
|
||||
/* http://www.sitepoint.com/css3-sliding-menu/ */
|
||||
|
||||
sidenav {
|
||||
|
|
|
@ -263,52 +263,6 @@ footer .footer__link:hover svg {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.attackercallout {
|
||||
width: 120px;
|
||||
height: 160px;
|
||||
box-shadow: 0px 0px 12px rgba(0, 255, 255, 0.5);
|
||||
opacity: .5;
|
||||
background: black;
|
||||
border: 1px solid rgba(127, 255, 255, 0.25);
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.attackercallout .id {
|
||||
font-size: 12px;
|
||||
color: rgba(127, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.attackercallout .details {
|
||||
text-align: left;
|
||||
left: 2px;
|
||||
font-size: 12px;
|
||||
color: rgba(127, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.attackercallout .blockip {
|
||||
color: #FFF;
|
||||
text-transform: uppercase;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.attackercallout ul {
|
||||
list-style: none;
|
||||
float: left;
|
||||
left: auto;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.attackercallout .indicator {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.attackercallout a {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.veris-wrapper td {
|
||||
color: black
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ investigations /investigations
|
|||
incidents /incidents
|
||||
visualizations /visualizations
|
||||
logincounts /logincounts
|
||||
attackers /attackers
|
||||
globe /globe
|
||||
about /about
|
||||
blockip /blockip
|
||||
|
|
Загрузка…
Ссылка в новой задаче