fix: Properly create fail2ban object

pull/213/head
Raphaël Vinot 2018-03-26 17:03:16 +02:00
parent 22c874e479
commit 9e44ec6616
4 changed files with 53 additions and 24 deletions

View File

@ -5,6 +5,9 @@ from pymisp import PyMISP, MISPEvent
from pymisp.tools import Fail2BanObject from pymisp.tools import Fail2BanObject
import argparse import argparse
from base64 import b64decode from base64 import b64decode
from datetime import date, datetime
from dateutil.parser import parse
try: try:
from keys import misp_url, misp_key, misp_verifycert from keys import misp_url, misp_key, misp_verifycert
@ -14,29 +17,52 @@ except Exception:
misp_key = True misp_key = True
def create_new_event():
me = MISPEvent()
me.info = "Fail2Ban blocking"
me.add_tag(args.tag)
start = datetime.now()
me.add_attribute('datetime', start.isoformat(), comment='Start Time')
return me
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Add Fail2ban object.') parser = argparse.ArgumentParser(description='Add Fail2ban object.')
parser.add_argument("-b", "--banned_ip", required=True, help="Banned IP address.") parser.add_argument("-b", "--banned_ip", required=True, help="Banned IP address.")
parser.add_argument("-a", "--attack_type", required=True, help="Type of attack.") parser.add_argument("-a", "--attack_type", required=True, help="Type of attack.")
parser.add_argument("-t", "--tag", required=True, help="Tag to search on MISP.")
parser.add_argument("-p", "--processing_timestamp", help="Processing timestamp.") parser.add_argument("-p", "--processing_timestamp", help="Processing timestamp.")
parser.add_argument("-f", "--failures", help="Amount of failures that lead to the ban.") parser.add_argument("-f", "--failures", help="Amount of failures that lead to the ban.")
parser.add_argument("-s", "--sensor", help="Sensor identifier.") parser.add_argument("-s", "--sensor", help="Sensor identifier.")
parser.add_argument("-v", "--victim", help="Victim identifier.") parser.add_argument("-v", "--victim", help="Victim identifier.")
parser.add_argument("-l", "--logline", help="Logline (base64 encoded).") parser.add_argument("-l", "--logline", help="Logline (base64 encoded).")
parser.add_argument("-ap", "--aggregation_period", required=True, help="Max time of the event (1d, 1h, ...).") parser.add_argument("-n", "--force_new", action='store_true', default=False, help="Force new MISP event.")
parser.add_argument("-t", "--tag", required=True, help="Tag to search on MISP.") parser.add_argument("-d", "--disable_new", action='store_true', default=False, help="Do not create a new Event.")
args = parser.parse_args() args = parser.parse_args()
pymisp = PyMISP(misp_url, misp_key, misp_verifycert, debug=True) pymisp = PyMISP(misp_url, misp_key, misp_verifycert, debug=True)
event_id = -1
response = pymisp.search(tags=args.tag, last=args.aggregation_period, published=False) me = None
me = MISPEvent() if args.force_new:
if 'response' in response and response['response']: me = create_new_event()
me.load(response['response'][0])
else: else:
me.info = "Fail2Ban blocking" response = pymisp.search_index(tag=args.tag, timestamp='1h')
me.add_tag(args.tag) if response['response']:
parameters = {'banned-ip': args.banned_ip, 'attack-type': args.attack_type, 'processing-timestamp': args.processing_timestamp} if args.disable_new:
event_id = response['response'][0]['id']
else:
last_event_date = parse(response['response'][0]['date']).date()
nb_attr = response['response'][0]['attribute_count']
if last_event_date < date.today() or int(nb_attr) > 1000:
me = create_new_event()
else:
event_id = response['response'][0]['id']
else:
me = create_new_event()
parameters = {'banned-ip': args.banned_ip, 'attack-type': args.attack_type}
if args.processing_timestamp:
parameters['processing-timestamp'] = args.processing_timestamp
if args.failures: if args.failures:
parameters['failures'] = args.failures parameters['failures'] = args.failures
if args.sensor: if args.sensor:
@ -46,5 +72,9 @@ if __name__ == '__main__':
if args.logline: if args.logline:
parameters['logline'] = b64decode(args.logline).decode() parameters['logline'] = b64decode(args.logline).decode()
f2b = Fail2BanObject(parameters=parameters, standalone=False) f2b = Fail2BanObject(parameters=parameters, standalone=False)
if me:
me.add_object(f2b) me.add_object(f2b)
pymisp.add_event(me) pymisp.add_event(me)
elif event_id:
template_id = pymisp.get_object_template_id(f2b.template_uuid)
a = pymisp.add_object(event_id, template_id, f2b)

View File

@ -953,7 +953,8 @@ class PyMISP(object):
def search_index(self, published=None, eventid=None, tag=None, datefrom=None, def search_index(self, published=None, eventid=None, tag=None, datefrom=None,
dateuntil=None, eventinfo=None, threatlevel=None, distribution=None, dateuntil=None, eventinfo=None, threatlevel=None, distribution=None,
analysis=None, attribute=None, org=None, async_callback=None, normalize=False): analysis=None, attribute=None, org=None, async_callback=None, normalize=False,
timestamp=None):
"""Search only at the index level. Use ! infront of value as NOT, default OR """Search only at the index level. Use ! infront of value as NOT, default OR
If using async, give a callback that takes 2 args, session and response: If using async, give a callback that takes 2 args, session and response:
basic usage is basic usage is
@ -971,11 +972,12 @@ class PyMISP(object):
:param org: Organisation(s) | str or list :param org: Organisation(s) | str or list
:param async_callback: Function to call when the request returns (if running async) :param async_callback: Function to call when the request returns (if running async)
:param normalize: Normalize output | True or False :param normalize: Normalize output | True or False
:param timestamp: Interval since last update (in second, or 1d, 1h, ...)
""" """
allowed = {'published': published, 'eventid': eventid, 'tag': tag, 'Dateuntil': dateuntil, allowed = {'published': published, 'eventid': eventid, 'tag': tag, 'Dateuntil': dateuntil,
'Datefrom': datefrom, 'eventinfo': eventinfo, 'threatlevel': threatlevel, 'Datefrom': datefrom, 'eventinfo': eventinfo, 'threatlevel': threatlevel,
'distribution': distribution, 'analysis': analysis, 'attribute': attribute, 'distribution': distribution, 'analysis': analysis, 'attribute': attribute,
'org': org} 'org': org, 'timestamp': timestamp}
rule_levels = {'distribution': ["0", "1", "2", "3", "!0", "!1", "!2", "!3"], rule_levels = {'distribution': ["0", "1", "2", "3", "!0", "!1", "!2", "!3"],
'threatlevel': ["1", "2", "3", "4", "!1", "!2", "!3", "!4"], 'threatlevel': ["1", "2", "3", "4", "!1", "!2", "!3", "!4"],
'analysis': ["0", "1", "2", "!0", "!1", "!2"]} 'analysis': ["0", "1", "2", "!0", "!1", "!2"]}
@ -1370,11 +1372,7 @@ class PyMISP(object):
raise Exception('Invalid parameter, org_id must be a number') raise Exception('Invalid parameter, org_id must be a number')
else: else:
org_id = "" org_id = ""
uri = 'sightings/listSightings/{}/{}/{}'.format( uri = 'sightings/listSightings/{}/{}/{}'.format(element_id, scope, org_id)
element_id,
scope,
org_id
)
url = urljoin(self.root_url, uri) url = urljoin(self.root_url, uri)
response = self.__prepare_request('POST', url) response = self.__prepare_request('POST', url)
return self._check_response(response) return self._check_response(response)

View File

@ -452,7 +452,7 @@ class MISPEvent(AbstractMISP):
def from_dict(self, **kwargs): def from_dict(self, **kwargs):
# Required value # Required value
self.info = kwargs.pop('info', None) self.info = kwargs.pop('info', None)
if not self.info: if self.info is None:
raise NewAttributeError('The info field of the new event is required.') raise NewAttributeError('The info field of the new event is required.')
# Default values for a valid event to send to a MISP instance # Default values for a valid event to send to a MISP instance

View File

@ -31,7 +31,8 @@ setup(
extras_require={'fileobjects': ['lief>=0.8', 'python-magic'], extras_require={'fileobjects': ['lief>=0.8', 'python-magic'],
'neo': ['py2neo'], 'neo': ['py2neo'],
'openioc': ['beautifulsoup4'], 'openioc': ['beautifulsoup4'],
'virustotal': ['validators']}, 'virustotal': ['validators'],
'warninglists': ['pymispwarninglists']},
tests_require=[ tests_require=[
'jsonschema', 'jsonschema',
'python-dateutil', 'python-dateutil',