def get_rule_name_for_allowed_accounts_windows(self, events: list, _self_params, correlation_rule_name): """Генерирует новое имя для правила корреляции, с целью определения, разрешена ли авторизация конкретной УЗ на критичном хосте""" rule_name = self.get_rule_name_for_crit_hosts(events, _self_params, correlation_rule_name) if '-ON-CRITICAL-HOST' in rule_name: with open('./lists/allowed_accounts_windows.txt', 'r') as f: file = f.read().splitlines() file_set = set(file) for event in events: _organization = getval(_self_params, event, "collector.organization") _host_ip = getval(_self_params, event, "eventSource.location.ip") _user_domain = getval(_self_params, event, "subject.domain") _username = getval(_self_params, event, "subject.name") _logon_type = getval(_self_params, event, "interaction.logonType") _composite = f'{_organization}{_host_ip}{_user_domain}{_username}{_logon_type}'.lower( ) if _composite not in file_set: _new_corr_rule_name = correlation_rule_name + '-UNAUTHORIZED-ACCOUNT' return _new_corr_rule_name else: return
def filtering_authorized_unix_account(_selfParams, _eventsList: list): """Удалеяет предварительно сагрегированные по subject.id события, если для subject.id указано соответсвие eventSource.location.host в индексе unix_authorization """ _filteredEventList = [] if len(_eventsList) < 1: return _filteredEventList _ES = _selfParams.ESInstance _firstEvent = _eventsList[0] _organization = getval(_selfParams, _firstEvent, "collector.organization").lower() _host = getval(_selfParams, _firstEvent, "eventSource.location.host").lower() _subjectID = getval(_selfParams, _firstEvent, "subject.id") _docCount = 0 _uidList = [] _searchQuery = { "query": { "query_string": { "query": f'organization: "{_organization}" AND (hostname: "{_host}" OR ipaddr: "{_host}")' } } } _searchResult = _ES.search(index='unix_authorization', body=_searchQuery) if isinstance(_searchResult['hits']['total'], dict): if 'value' in _searchResult['hits']['total']: _docCount = _searchResult['hits']['total']['value'] elif isinstance(_searchResult['hits']['total'], int): _docCount = _searchResult['hits']['total'] if _docCount > 0: for doc in _searchResult['hits']['hits']: _uidList.append(doc['_source']['uid']) if not _subjectID in _uidList: _filteredEventList = _eventsList return _filteredEventList
def get_rule_name_for_crit_hosts(events: list, _selfParams, original_corr_rule_name) -> str: """Генерирует новое имя для правила корреляции, с целью определения, является ли хост критичным или нет""" _is_included = False with open('./lists/critical_hosts.txt', 'r') as f: _file = f.read().splitlines() _file_set = set(_file) for event in events: _organization = getval(_selfParams, event, "collector.organization") _host_ip = getval(_selfParams, event, "eventSource.location.ip") _hostname = getval(_selfParams, event, "eventSource.location.hostname") _composite_ip = _organization + _host_ip _composite_hostname = _organization + _hostname if _composite_hostname in _file_set: _is_included = True elif _composite_ip in _file_set: _is_included = True if _is_included: _new_corr_rule_name = original_corr_rule_name + '-ON-CRITICAL-HOST' else: _new_corr_rule_name = original_corr_rule_name + '-ON-NON-CRITICAL-HOST' return _new_corr_rule_name
def filtering_not_different_subject_and_object(_selfParams, event_list: list): """Из массива со скорелированными событиями удаляет события, в которых subject.name и object.name - одинаковы""" _filteredEventList = [] for event in event_list: if not getval(_selfParams, event, "subject.name") == getval(_selfParams, event, "object.name"): _filteredEventList.append(event) return _filteredEventList
def filtering_legal_admin_activity_on_fortigate(_selfParams, event_list: list): """Из массива со скорелированными событиями удаляет события с легитимной активностью админ. УЗ при выполнении команд на Fortigate""" _filteredEventList = [] _eventIdWithObjectList = [ "0100044544", "0100044545", "0100044546", "0100044547", "0100044548", "0100044549", "0100044550", "0100044551", "0100044552" ] # 0100044544 - Path configured # 0100044545 - Object configured # 0100044546 - Attribute configured # 0100044547 - Object attribute configured # 0100044548 - Action performed # 0100044549 - Object attribute configured by maintainer # 0100044550 - Object configured by maintainer # 0100044551 - Attribute configured by maintainer # 0100044552 - Path configured by maintainer with open('./lists/fortigate_superadmins.txt', 'r') as _fSuper: _fileSuper = _fSuper.read() _fileSetSuper = set(_fileSuper.splitlines()) with open('./lists/fortigate_admin_forbidden_actions.txt', 'r') as _fForbidden: _fileForbidden = _fForbidden.read() _fileSetForbidden = set(_fileForbidden.splitlines()) with open('./lists/fortigate_admin_allowed_actions.txt', 'r') as _fAllowed: _fileAllowed = _fAllowed.read() _fileSetAllowed = set(_fileAllowed.splitlines()) for event in event_list: _organization = getval(_selfParams, event, "collector.organization") _device = getval(_selfParams, event, "eventSource.location.ip") _userName = getval(_selfParams, event, "subject.name") _eventId = getval(_selfParams, event, "data.msgId") _compositeSuperAdmin = f'\'{_organization}\'|\'{_device}\'|\'{_userName}\''.lower( ) if _compositeSuperAdmin not in _fileSetSuper: if _eventId in _eventIdWithObjectList: _object = event['data']['aux1'].split(' ')[1] _compositeWithObject = f'\'{_organization}\'|\'{_device}\'|\'{_userName}\'|\'{_eventId}\'|\'{_object}\''.lower( ) if (_compositeWithObject in _fileSetForbidden or _compositeWithObject not in _fileSetAllowed): _filteredEventList.append(event) else: _compositeWithoutObject = f'\'{_organization}\'|\'{_device}\'|\'{_userName}\'|\'{_eventId}\'|\'\''.lower( ) if (_compositeWithoutObject in _fileSetForbidden or _compositeWithoutObject not in _fileSetAllowed): _filteredEventList.append(event) return _filteredEventList
def filtering_unix_account_in_allowed_time(_selfParams, _eventsList: list): """Удалеяет предварительно сагрегированные по субъекту события, если сейчас для субъекта разрешенное время """ def is_time_in_middle(_firstTime, _secondTime, _comparableTime): _isTimeInMiddle = False _midnightTimeMin = datetime.time(hour=0, minute=0, second=0) _midnightTimeMax = datetime.time(hour=23, minute=59, second=59) if _firstTime < _secondTime: if _firstTime < _comparableTime < _secondTime: _isTimeInMiddle = True else: if _firstTime < _comparableTime <= _midnightTimeMax or _midnightTimeMin <= _comparableTime < _secondTime: _isTimeInMiddle = True return _isTimeInMiddle _filteredEventList = [] if len(_eventsList) < 1: return _filteredEventList _toAlert = True _ES = _selfParams.ESInstance _firstEvent = _eventsList[0] _organization = getval(_selfParams, _firstEvent, "collector.organization").lower() _host = getval(_selfParams, _firstEvent, "eventSource.location.host").lower() _subjectID = getval(_selfParams, _firstEvent, "subject.id") _fromZone = tz.gettz('UTC') _toZone = tz.gettz('Europe/Moscow') _eventTime = parser.parse(_firstEvent['@timestamp']).replace(tzinfo=_fromZone).astimezone(_toZone).time() _docCount = 0 _searchQuery = { "query": { "query_string": { "query": f'organization: "{_organization}" AND (hostname: "{_host}" OR ipaddr: "{_host}") AND uid: "{_subjectID}"' } } } _searchResult = _ES.search(index='unix_authorization', body=_searchQuery) if isinstance(_searchResult['hits']['total'], dict): if 'value' in _searchResult['hits']['total']: _docCount = _searchResult['hits']['total']['value'] elif isinstance(_searchResult['hits']['total'], int): _docCount = _searchResult['hits']['total'] if _docCount > 0: _firstDoc = _searchResult['hits']['hits'][0] _startTime = datetime.datetime.strptime(_firstDoc['_source']['hour_start'], '%H:%M:%S').replace(tzinfo=_toZone).time() _endTime = datetime.datetime.strptime(_firstDoc['_source']['hour_end'], '%H:%M:%S').replace(tzinfo=_toZone).time() if is_time_in_middle(_startTime, _endTime, _eventTime): _toAlert = False if _toAlert: _filteredEventList = _eventsList return _filteredEventList
def filtering_not_in_list_process(_selfParams, event_list: list): """Из массива со скорелированными событиями удаляет события, в которых имя процесса не прописан в списке""" _filteredEventList = [] with open('./lists/monitored_app_list.txt', 'r') as _f: _file = _f.read() _fileSet = set(_file.splitlines()) for event in event_list: _organization = getval(_selfParams, event, "collector.organization") _newProcessFileName = getval(_selfParams, event, "object.path") _compositeProcessName = f'\'{_organization}\'|\'{_newProcessFileName}\''.lower() if _compositeProcessName in _fileSet: _filteredEventList.append(event) return _filteredEventList
def filtering_not_critical_unix_host(_selfParams, _eventsList: list): """ Фильтрует те события, в которых eventSource.location.host не указан в списке unix_critical_hosts """ _filteredEventList = [] with open('./lists/unix_critical_hosts.txt', 'r') as _f: _file = _f.read().splitlines() _fileSet = set(_file) for event in _eventsList: _organization = getval(_selfParams, event, "collector.organization") _host = getval(_selfParams, event, "eventSource.location.host") _composite = f"'{_organization}'|'{_host}'".lower() if _composite in _fileSet: _filteredEventList.append(event) return _filteredEventList
def filtering_not_in_list_gpo(_selfParams, event_list: list): """Из массива со скорелированными событиями удаляет события, если GUID или DN GPO в которых не прописаны в списке""" _filteredEventList = [] with open('./lists/controlled_gpo_list.txt', 'r') as _f: _file = _f.read() _fileSet = set(_file.splitlines()) for event in event_list: _organization = getval(_selfParams, event, "collector.organization") _GPOGUID = getval(_selfParams, event, "object.id") _GPODN = getval(_selfParams, event, "object.path") _compositeGUID = f'\'{_organization}\'|\'{_GPOGUID}\''.lower() _compositeDN = f'\'{_organization}\'|\'{_GPODN}\''.lower() if _compositeGUID in _fileSet or _compositeDN in _fileSet: _filteredEventList.append(event) return _filteredEventList
def filtering_not_hidden_shared_folder(_selfParams, event_list: list): """Из массива со скорелированными событиями удаляет события, в object.name которых не указана скрытая расшаренная сетевая папка""" _hiddenSharedFolderPattern = re.compile("^\\\\\\\\\*\\\\[\w]+\$$") _filteredEventList = [] for event in event_list: if _hiddenSharedFolderPattern.match(getval(_selfParams, event, "object.name")): _filteredEventList.append(event) return _filteredEventList
def filtering_subject_system_accounts(_selfParams, event_list: list): """Из массива со скорелированными событиями удаляет события, в которых subject.name - системное УЗ""" _filteredEventList = [] for event in event_list: _subject_name = getval(_selfParams, event, "subject.name") if not _subject_name.endswith('$'): _filteredEventList.append(event) return _filteredEventList
def get_rule_name_for_critical_groups_windows( events: list, _selfParams, original_corr_rule_name) -> str: """Генерирует новое имя для правила корреляции, с целью определения, критичная ли это windows группа""" with open('./lists/critical_groups_windows.txt', 'r') as f: _file = f.read().splitlines() _file_set = set(_file) for event in events: _organization = getval(_selfParams, event, "collector.organization") _domain = getval(_selfParams, event, "object.domain") _group_name = getval(_selfParams, event, "object.name") _composite = f'{_organization}{_domain}{_group_name}'.lower() if _composite in _file_set: _new_corr_rule_name = original_corr_rule_name + '-FROM-CRITICAL-LIST' return _new_corr_rule_name return original_corr_rule_name
def get_rule_name_for_allowed_accounts_network(events: list, _selfParams, correlation_rule_name): """Генерирует новое имя для правила корреляции, с целью определения, разрешена ли авторизация конкретной УЗ на критичном хосте сетевого оборудования""" with open('./lists/allowed_accounts_network.txt', 'r') as f: _file = f.read() _file_set = set(_file.splitlines()) for event in events: _organization = getval(_selfParams, event, "collector.organization") _host_ip = getval(_selfParams, event, "eventSource.location.ip") _username = getval(_selfParams, event, "subject.name") _composite_host = f'{_organization}{_host_ip}'.lower() _composite_user = f'{_organization}{_host_ip}{_username}'.lower() if _composite_host in _file: if _composite_user not in _file_set: _new_corr_rule_name = correlation_rule_name + '-UNAUTHORIZED-ACCOUNT' # Инцидент, т.к. данному пользователю не разрешена авторизация на данный хост return _new_corr_rule_name return # Данный хост не критичный или пользователю разрешена атворизация на данный хост
def filtering_not_psexec_filenames(_selfParams, event_list: list): """Из массива со скорелированными событиями удаляет события, в которых имя файла не содержит PsExec и ее аналогов""" _filteredEventList = [] _PsExecProcessPatternList = ["psexec.*", "winexec.*", "csexec.*", "paexec.*"] _PsExecServicePatternList = ["psexe.*", "winexe.*", "csexe.*", "paexe.*"] for event in event_list: # Event ID: "4688" - A new process has been created msg_id = getval(_selfParams, event, "data.msgId") if msg_id == "4688": _newProcessFileName = ntpath.basename(getval(_selfParams, event, "object.path")).lower() for _PsExecProcessPattern in _PsExecProcessPatternList: if re.compile(_PsExecProcessPattern).match(_newProcessFileName): _filteredEventList.append(event) # Event ID: "7045" - New Service was installed elif msg_id == "7045": _newServiceFileName = ntpath.basename(getval(_selfParams, event, "object.path")).lower() for _PsExecServicePattern in _PsExecServicePatternList: if re.compile(_PsExecServicePattern).match(_newServiceFileName): _filteredEventList.append(event) return _filteredEventList
def filtering_dwm_umfd_accounts(_selfParams, event_list: list): """Из массива со скорелированными событиями удаляет события, в subject.name которых указаны DWM-x и UMFD-x""" # DWM-x Desktop Window Manager _DWMPattern = re.compile("^DWM-[0-9]+$") # UMFD-x User Mode Driver Framework _UMFDPattern = re.compile("^UMFD-[0-9]+$") _filteredEventList = [] for event in event_list: _subject_name = getval(_selfParams, event, "subject.name") if not (_DWMPattern.match(_subject_name) or _UMFDPattern.match(_subject_name)): _filteredEventList.append(event) return _filteredEventList
def get_windows_chang_time_corr_rule_name(events: list, _selfParams, corr_rule_name_in) -> str: """Генерирует новое имя для правила корреляции, с целью выявления, изменилось ли время на узле более чем на 5 минут """ _corrRuleName = "" _is_more = False for event in events: # raw_date_format: '2020-11-02T10:36:06.474026900Z' previous_time = datetime.fromisoformat( getval(_selfParams, event, "object.property").split('.')[0]) new_time = datetime.fromisoformat( getval(_selfParams, event, "object.value").split('.')[0]) delta = abs((new_time - previous_time).total_seconds()) if delta > 300: _is_more = True if _is_more: _corrRuleName = corr_rule_name_in + '-OVER-FIVE-MINUTES' else: _corrRuleName = corr_rule_name_in + '-LESS-FIVE-MINUTES' return _corrRuleName
def filtering_malicious_service_installations(_selfParams, event_list: list): """ Оставляет только те события, которые подходят под условие правила Malicious Service Installations. """ _filteredEventList = [] for event in event_list: _service_name = getval(_selfParams, event, "object.name").lower() _service_file_name = getval(_selfParams, event, "object.path").lower() if _service_name in ['wceservice', 'wce service', 'mssecsvc2.0']: _filteredEventList.append(event) elif (_service_name.startswith('pwdump') or _service_name.startswith('gsecdump') or _service_name.startswith('cachedump')): _filteredEventList.append(event) elif r'\paexec' in _service_file_name: _filteredEventList.append(event) elif _service_file_name.startswith('winexesvc.exe'): _filteredEventList.append(event) elif _service_file_name.endswith(r'\dumpsvc.exe'): _filteredEventList.append(event) elif ' net user ' in _service_file_name: _filteredEventList.append(event) return _filteredEventList
def filtering_not_cmd_password_search(_selfParams, event_list: list): """Из массива со скорелированными событиями удаляет события, в которых CommandLine в data.aux7 не содержит CMD комманд поиска паролей""" _filteredEventList = [] _passwordSearchCMDProcessPatternList = [ "find.*password.*", "dir.*pass.*", "dir.*vnc\.ini.*", "dir.*unattend\.xml.*", "dir.*sysprep\.xml.*", "dir.*sysprep\.inf.*", "reg.*query.*password.*", "reg.*query.*putty.*", ".*get-unattendedinstallfile.*", ".*get-webconfig.*", ".*get-applicationhost.*", ".*get-sitelistpassword.*", ".*get-cachedgpppassword.*", ".*get-registryautologon.*" ] for event in event_list: _newProcessCommandLine = getval(_selfParams, event, "data.aux7").lower() for _passwordSearchCMDProcessPattern in _passwordSearchCMDProcessPatternList: if re.compile(_passwordSearchCMDProcessPattern).match(_newProcessCommandLine): _filteredEventList.append(event) return _filteredEventList
def filtering_detects_rubeus_hack_tool(_selfParams, event_list: list): """ Оставляет только те события, которые подходят под условие правила Detects Rubeus Hack Tool. """ _filteredEventList = [] _indicators_list = [r' asreproast ', r' dump /service:krbtgt ', r' kerberoast ', r' createnetonly /program:', r' ptt /ticket:', r' /impersonateuser:'******' renew /ticket:', r' asktgt /user:'******' harvest /interval:' ] for event in event_list: _new_process_name = getval(_selfParams, event, "object.path").lower() for _indicator in _indicators_list: if _indicator in _new_process_name: _filteredEventList.append(event) return _filteredEventList