def execute(block_id, block_dict, extra_args=None): """ Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "/some/path/file.txt", 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing pkg module for id: {0}'.format(block_id)) # fetch required param name = runner_utils.get_chained_param(extra_args) if not name: name = runner_utils.get_param_for_module(block_id, block_dict, 'name') installed_pkgs_dict = __mods__['pkg.list_pkgs']() filtered_pkgs_list = fnmatch.filter(installed_pkgs_dict, name) result_dict = {} for package in filtered_pkgs_list: result_dict[package] = installed_pkgs_dict[package] return runner_utils.prepare_positive_result_for_module( block_id, result_dict)
def execute(block_id, block_dict, extra_args=None): """ Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "/some/path/file.txt", 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing stat module for id: {0}'.format(block_id)) # fetch required param filepath = runner_utils.get_chained_param(extra_args) if filepath and isinstance(filepath, dict): # in case, the result is a dictionary. Fetch the path for key, value in filepath.items(): filepath = value if not filepath: filepath = runner_utils.get_param_for_module(block_id, block_dict, 'path') # check filepath existence if not os.path.exists(filepath): return runner_utils.prepare_negative_result_for_module(block_id, 'file_not_found') stat_res = __mods__['file.stats'](filepath) return runner_utils.prepare_positive_result_for_module(block_id, stat_res)
def execute(block_id, block_dict, extra_args=None): """ Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "vm.zone_reclaim_mode", 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing sysctl module for id: {0}'.format(block_id)) # fetch required param name = runner_utils.get_chained_param(extra_args) if not name: name = runner_utils.get_param_for_module(block_id, block_dict, 'name') sysctl_res = __mods__['sysctl.get'](name) result = {name: sysctl_res} if not sysctl_res or "No such file or directory" in sysctl_res: return runner_utils.prepare_negative_result_for_module( block_id, "Could not find attribute %s in the kernel" % (name)) if sysctl_res.lower().startswith("error"): return runner_utils.prepare_negative_result_for_module( block_id, "An error occurred while reading the value " "of kernel attribute %s" % (name)) return runner_utils.prepare_positive_result_for_module(block_id, result)
def _filter(block_id, seq, filter_rules): """ Filter a sequence. block_id Block id seq The input sequence to be filtered. filter_rules A dict of (comparison_type, value) pairs that dictate the type of filtering where comparison_type can be [gt, lt, eq, ne, ge, le]. For e.g. for ``seq`` = [1, 2, 3, 4, 5] ``filter_rules``={le: 4, gt: 1, ne: 2} the function outputs [3, 4] - values less than or equal to 4, greater than 1, not equal to 2. """ if not isinstance(filter_rules, dict): log.error("``filter_rules`` should be of type dict") return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') ret = seq for comp, value in filter_rules.items(): try: ret = [x for x in ret if _compare(comp, x, value)] except ArgumentValueError: return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') return runner_utils.prepare_positive_result_for_module(block_id, ret)
def execute(block_id, block_dict, extra_args=None): """ Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "/some/path/file.txt", 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing stat module for id: {0}'.format(block_id)) # fetch required param name = runner_utils.get_chained_param(extra_args) if not name: name = runner_utils.get_param_for_module(block_id, block_dict, 'name') result = [] matched_services = fnmatch.filter(__mods__['service.get_all'](), name) for matched_service in matched_services: service_status = __mods__['service.status'](matched_service) is_enabled = __mods__['service.enabled'](matched_service) result.append({ "name": matched_service, "running": service_status, "enabled": is_enabled }) return runner_utils.prepare_positive_result_for_module(block_id, result)
def _get_key(block_id, block_dict, extra_args): """ Given a dictionary, return an element by ``key``. block_id: Block id block_dict: key: (Mandatory) Key value to get starting_dict: (Optional) Starting dictionary param extend_chained: (Default True) By default, ``chained`` will have ``.update()`` called on it with ``starting_dict`` as the only argument. Set ``extend_chained`` to False to ignore ``starting_dict``. :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "Output", 'status': True}, 'caller': 'Audit'} The first return value (status) will be True if the key is found, and False othewise. The second argument will be the value found by the key or None if the key is not present in the dictionary. """ chained = runner_utils.get_chained_param(extra_args) key = runner_utils.get_param_for_module(block_id, block_dict, 'key') starting_dict = runner_utils.get_param_for_module(block_id, block_dict, 'starting_dict') update_chained = runner_utils.get_param_for_module(block_id, block_dict, 'update_chained', True) if update_chained and starting_dict: try: chained.update(starting_dict) except (TypeError, ValueError): log.error("Arguments should be of type dict.", exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') try: ret = chained[key] except KeyError: log.error("Key not found: %s", key, exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'key_not_found') return False, None except TypeError: log.error("Arguments should be of type dict.", exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') status = bool(ret) if not status: return runner_utils.prepare_negative_result_for_module( block_id, 'unknown_error') return runner_utils.prepare_positive_result_for_module(block_id, ret)
def _sort(block_id, block_dict, extra_args): """ Given a target sequence, sort it and return the sorted result. block_id: Block id block_dict: seq: (Optional) Input sequence to be sorted lexico: (Optional) (Default False) Set to True if the sorting thould be in lexicographical order. desc: (Optional) (Default False) Set to True if the sorting should be in descending order. extend_chained: (Default True) By default, ``chained`` will have ``.extend()`` or ``.update()`` or ``.format()`` called on it with ``seq`` as the only argument. Set ``extend_chained`` to False to ignore ``seq``. :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "Output", 'status': True}, 'caller': 'Audit'} The first return value (status) will be True if the sort is successful, and False othewise. The second argument will be the sorted sequence. """ chained = runner_utils.get_chained_param(extra_args) extend_chained = runner_utils.get_param_for_module(block_id, block_dict, 'extend_chained', True) lexico = runner_utils.get_param_for_module(block_id, block_dict, 'lexico', False) desc = runner_utils.get_param_for_module(block_id, block_dict, 'desc', False) seq = runner_utils.get_param_for_module(block_id, block_dict, 'seq') if extend_chained and seq: try: if isinstance(chained, (dict, set)): chained.update(seq) elif isinstance(chained, list): chained.extend(seq) elif isinstance(chained, str): chained = seq.format(chained) except (AttributeError, TypeError, ValueError): log.error("Invalid arguments type.", exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') ret = _sort_helper(chained, desc, lexico) status = bool(ret) if not status: return runner_utils.prepare_negative_result_for_module( block_id, 'unknown_error') return runner_utils.prepare_positive_result_for_module(block_id, ret)
def _get_index(block_id, block_dict, extra_args): """ Given a list list, return the item found at ``index``. block_id: Block id block_dict: index: (Mandatory) Index value for which value is needed starting_list: (Optional) Starting list param extend_chained: (Default True) By default, ``chained`` will have ``.extend()`` called on it with ``starting_list`` as the only argument. :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "Output", 'status': True}, 'caller': 'Audit'} The first return value (status) will be True if the return was successful, and False othewise. The second argument will be the requested list element. """ chained = runner_utils.get_chained_param(extra_args) index = runner_utils.get_param_for_module(block_id, block_dict, 'index', 0) starting_list = runner_utils.get_param_for_module(block_id, block_dict, 'starting_list') extend_chained = runner_utils.get_param_for_module(block_id, block_dict, 'extend_chained', True) if extend_chained and starting_list: try: chained.extend(starting_list) except (AttributeError, TypeError): log.error("Invalid argument type", exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') try: ret = chained[index] except IndexError: log.error('List index out of range %d', index, exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') except TypeError: log.error('Arguments should be of type list', exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') status = bool(ret) if status: return runner_utils.prepare_positive_result_for_module(block_id, ret) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_result')
def _join(block_id, block_dict, extra_args): """ Given a list of strings, join them into a string, using ``sep`` as delimiter. block_id: Block id block_dict: words: (Mandatory) List of string sep: (Optional) Separator, Default: '' extend_chained: (Default True) By default, ``chained`` will have ``.extend()`` called on it with ``words`` as the only argument. :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "Output", 'status': True}, 'caller': 'Audit'} The first return value (status) will be True if the join was successful, and False othewise. The second argument will be the output of the ``join`` command. ``extend_chained`` is set to True when ``chained`` should be extended with ``words``. If set to False, ``words`` is ignored. """ chained = runner_utils.get_chained_param(extra_args) extend_chained = runner_utils.get_param_for_module(block_id, block_dict, 'extend_chained', True) sep = runner_utils.get_param_for_module(block_id, block_dict, 'sep', '') words = runner_utils.get_param_for_module(block_id, block_dict, 'words', None) if extend_chained and words: try: chained.extend(words) except (AttributeError, TypeError): log.error("Arguments should be of type list.", exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') try: ret = sep.join(chained) except (TypeError, AttributeError): log.error("Invalid arguments type.", exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') status = bool(ret) if not status: return runner_utils.prepare_negative_result_for_module( block_id, 'unknown_error') return runner_utils.prepare_positive_result_for_module(block_id, ret)
def execute(block_id, block_dict, extra_args=None): """ Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "/some/path/file.txt", 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing FDG Connector module for id: {0}'.format(block_id)) fdg_file = runner_utils.get_chained_param(extra_args) if not fdg_file: fdg_file = runner_utils.get_param_for_module(block_id, block_dict, 'fdg_file') # read other params for fdg connector module starting_chained = runner_utils.get_param_for_module( block_id, block_dict, 'starting_chained') use_status = runner_utils.get_param_for_module(block_id, block_dict, 'use_status', False) consolidation_operator = runner_utils.get_param_for_module( block_id, block_dict, 'consolidation_operator', 'and') try: # fdg runner class fdg_runner = runner_factory.get_fdg_runner() fdg_runner.init_loader() # Handover to fdg_runner _, fdg_run = fdg_runner.execute(fdg_file, {'starting_chained': starting_chained}) except Exception as e: raise HubbleCheckValidationError( 'fdg_runner raised {0}: in file {1}, {2}'.format( e.__class__, fdg_file, e)) if not isinstance(fdg_run, tuple): log.debug("consolidation_operator is %s", consolidation_operator) fdg_run = _get_consolidated_result(fdg_run, consolidation_operator) fdg_result, fdg_status = fdg_run if isinstance(fdg_result, dict) and 'error' in (k.lower() for k in fdg_result): return runner_utils.prepare_negative_result_for_module(block_id, False) check_value = fdg_status if use_status else bool(fdg_result) if check_value: return runner_utils.prepare_positive_result_for_module(block_id, True) return runner_utils.prepare_negative_result_for_module(block_id, False)
def _dict_convert_none(block_id, block_dict, extra_args): """ Given a target sequence, look for dictionary keys that have empty string values and replace them with None. block_id: Block id block_dict: starting_seq: (Optional) Initial dictionary extend_chained: (Default True) By default, ``chained`` will have ``.extend()`` or ``.update()`` called on it with ``starting_seq`` as the only argument. Set ``extend_chained`` to False to ignore ``starting_seq``. :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "Output", 'status': True}, 'caller': 'Audit'} The first return value (status) will be True if the replacing is successful, and False othewise. The second argument will be the updated sequence. """ chained = runner_utils.get_chained_param(extra_args) extend_chained = runner_utils.get_param_for_module(block_id, block_dict, 'extend_chained', True) starting_seq = runner_utils.get_param_for_module(block_id, block_dict, 'starting_seq') if extend_chained and starting_seq: try: if isinstance(chained, (set, dict)): chained.update(starting_seq) elif isinstance(chained, list): chained.extend(starting_seq) except (AttributeError, TypeError, ValueError): log.error("Invalid type of arguments", exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') if isinstance(chained, dict): ret = _dict_convert_none_helper(chained) elif isinstance(chained, (set, list, tuple)): ret = _seq_convert_none_helper(chained) else: log.error("Invalid arguments type - dict or sequence expected") return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_error') status = bool(ret) if not status: return runner_utils.prepare_negative_result_for_module( block_id, 'unknown_error') return runner_utils.prepare_positive_result_for_module(block_id, ret)
def _nop(block_id, block_dict, extra_args): """ This function just returns the chained value. It is a nop/no operation. This can be useful if you want to do a pipe_on_true to filter out False values -- you can pipe_on_true to util's nop, and stick a returner on the nop operation to just return the True values. """ chained_params = runner_utils.get_chained_param(extra_args) return runner_utils.prepare_positive_result_for_module( block_id, chained_params)
def _split(block_id, block_dict, extra_args): """ Given a ``phrase`` string, split it into a list of words by a ``sep`` delimiter. block_id: Block id block_dict: phrase: (Mandatory) Input Phrase(string) to be split. sep: (Optional) Separator to split by. It can either be a delimiter or a regex. If it's None it will split by whitespace. regex: (Optional) (Default False) Set to True if ``sep`` should be treated as a regex instead of a delimiter. format_chained: (Default True) By default, the ``phrase`` will have ``.format()`` called on it with ``chained`` as the only argument. (So, use ``{0}`` in your phrase to substitute the chained value.) If you want to avoid having to escape curly braces, set ``format_chained=False``. :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "Output", 'status': True}, 'caller': 'Audit'} The first return value (status) will be True if the delimiter is found and the splitting is successful, and False othewise. The second argument will be the output of the ``split`` command. """ chained = runner_utils.get_chained_param(extra_args) format_chained = runner_utils.get_param_for_module(block_id, block_dict, 'format_chained', True) phrase = runner_utils.get_param_for_module(block_id, block_dict, 'phrase') sep = runner_utils.get_param_for_module(block_id, block_dict, 'sep') regex = runner_utils.get_param_for_module(block_id, block_dict, 'regex', False) if format_chained and chained: try: phrase = phrase.format(chained) except AttributeError: log.error("Invalid attributes type.", exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') ret = _split_helper(phrase, sep, regex) status = bool(ret) and len(ret) > 1 if not status: return runner_utils.prepare_negative_result_for_module( block_id, 'unknown_error') return runner_utils.prepare_positive_result_for_module(block_id, ret)
def execute(block_id, block_dict, extra_args=None): """ For getting params to log, in non-verbose logging :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': {'host_ip': '127.0.0.1', 'host_port': 443}, 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ start_time = time.time() log.debug('Executing ssl_certificate module for id: {0}'.format(block_id)) endpoint_chained = runner_utils.get_chained_param(extra_args) if endpoint_chained: host_ip = endpoint_chained.get('host_ip') host_port = endpoint_chained.get('host_port') else: host_ip = runner_utils.get_param_for_module(block_id, block_dict, 'host_ip') host_port = runner_utils.get_param_for_module(block_id, block_dict, 'host_port') ssl_timeout = runner_utils.get_param_for_module(block_id, block_dict, 'ssl_timeout', 3) path = runner_utils.get_param_for_module(block_id, block_dict, 'path') cert = _get_cert(host_ip, host_port, ssl_timeout=ssl_timeout) if host_ip else _get_cert( path, from_file=True) if not cert: return runner_utils.prepare_negative_result_for_module( block_id, 'unable_to_load_certificate') log.debug("ssl_certificate - cert found, parsing certificate") cert_details = _parse_cert(cert, host_ip, host_port, path) if 'error' in cert_details: log.debug('Error in parsing certificate. {0}'.format( cert_details['error'])) return runner_utils.prepare_negative_result_for_module( block_id, 'unable_to_parse_certificate') stop_time = time.time() cert_details['execution_time'] = stop_time - start_time return runner_utils.prepare_positive_result_for_module( block_id, cert_details)
def execute(block_id, block_dict, extra_args=None): """ Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Chained argument dictionary, (If any) Example: {'chaining_args': {'result': {"name": "LogFileName", "value_type": "public"}, 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing win_firewall module for id: {0}'.format(block_id)) __firewalldata__ = _import_firewall() if not __firewalldata__: return runner_utils.prepare_negative_result_for_module( block_id, "firewall data couldn't be fetched") chained_result = runner_utils.get_chained_param(extra_args) if chained_result: name = chained_result.get('name') value_type = chained_result.get('value_type') else: name = runner_utils.get_param_for_module(block_id, block_dict, 'name') value_type = runner_utils.get_param_for_module(block_id, block_dict, 'value_type') try: setting_value = __firewalldata__.get(value_type).get(name).lower() except Exception as e: log.debug( "for block id %s, setting name %s and value type %s is not " "found in firewall data", block_id, name, value_type) setting_value = "Not Found" result = { "name": name, "value_type": value_type, "setting_value": setting_value } log.debug("win_firewall module output for block_id %s, is %s", block_id, result) if not result: return runner_utils.prepare_negative_result_for_module( block_id, "firewall setting couldn't be fetched") return runner_utils.prepare_positive_result_for_module(block_id, result)
def _print_string(block_id, block_dict, extra_args): """ Given a string, return it. block_id: Block id block_dict: starting_string: (Optional) Initial string format_chained: (Default True) By default, ``starting_string`` will have ``.format()`` called on it with ``chained`` as the only argument. (So, use ``{0}`` in your pattern to substitute the chained value.) If you want to avoid having to escape curly braces, set ``format_chained=False``. :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "Output", 'status': True}, 'caller': 'Audit'} The first return value (status) will be False only if an error will occur. """ chained = runner_utils.get_chained_param(extra_args) format_chained = runner_utils.get_param_for_module(block_id, block_dict, 'format_chained', True) starting_string = runner_utils.get_param_for_module( block_id, block_dict, 'starting_string') if format_chained: try: starting_string = starting_string.format(chained) except AttributeError: log.error("Invalid type for starting_string - has to be string.", exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') if not isinstance(starting_string, str): log.error('Invalid arguments - starting_string should be a string') return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') if not bool(starting_string): return runner_utils.prepare_negative_result_for_module( block_id, 'unknown_error') return runner_utils.prepare_positive_result_for_module( block_id, starting_string)
def execute(block_id, block_dict, extra_args=None): """ Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Chained argument dictionary, (If any) Example: {'chaining_args': {'result': "Local Administrator Password Solution", 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing win_pkg module for id: {0}'.format(block_id)) try: __pkgdata__ = __mods__['pkg.list_pkgs']() except CommandExecutionError: __mods__['pkg.refresh_db']() __pkgdata__ = __mods__['pkg.list_pkgs']() if not __pkgdata__: return runner_utils.prepare_negative_result_for_module( block_id, "package list couldn't be fetched") chained_result = runner_utils.get_chained_param(extra_args) if chained_result: pkg_name = chained_result.get('name') else: pkg_name = runner_utils.get_param_for_module(block_id, block_dict, 'name') if pkg_name in __pkgdata__: audit_value = __pkgdata__.get(pkg_name) else: log.debug("for block id %s, pkg %s is not found in pkg data", block_id, pkg_name) audit_value = "Not Found" result = {"package_name": pkg_name, "package_version": audit_value} log.debug("win_pkg module output for block_id %s, is %s", block_id, result) if audit_value == "Not Found": return runner_utils.prepare_negative_result_for_module( block_id, "package information couldn't be fetched") return runner_utils.prepare_positive_result_for_module(block_id, result)
def _dict_to_list(block_id, block_dict, extra_args): """ Given a target dictionary, convert it to a list of (key, value) tuples. block_id: Block id block_dict: starting_dict: (Optional) Initial dictionary update_chained: (Default True) By default, ``chained`` will have ``.update()`` called on it with ``starting_dict`` as the only argument. Set ``update_chained`` to False to ignore ``starting_dict``. :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "Output", 'status': True}, 'caller': 'Audit'} The first return value (status) will be True if the conversion is successful, and False othewise. The second argument will be the list of tuples. """ chained = runner_utils.get_chained_param(extra_args) update_chained = runner_utils.get_param_for_module(block_id, block_dict, 'update_chained', True) starting_dict = runner_utils.get_param_for_module(block_id, block_dict, 'starting_dict') if update_chained and starting_dict: try: chained.update(starting_dict) except (AttributeError, ValueError, TypeError): log.error("Invalid arguments type.", exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') ret = [(key, value) for key, value in chained.items()] status = bool(ret) if not status: return runner_utils.prepare_negative_result_for_module( block_id, 'unknown_error') return runner_utils.prepare_positive_result_for_module(block_id, ret)
def execute(block_id, block_dict, extra_args=None): """ Function that queries a list of NTP servers and checks if the offset is bigger than `max_offset` minutes. It expects the results from at least `nb_servers` servers in the list, otherwise the check fails. :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': ['server1', 'server2'], 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing time_sync module for id: {0}'.format(block_id)) ntp_servers = _get_ntp_servers(block_id, block_dict, extra_args) time_sync_result = [] for ntp_server in ntp_servers: offset = _query_ntp_server(ntp_server) if not offset: time_sync_result.append({ 'ntp_server': ntp_server, 'replied': False }) continue time_sync_result.append({ 'ntp_server': ntp_server, 'replied': True, 'offset': offset }) return runner_utils.prepare_positive_result_for_module( block_id, time_sync_result)
def execute(block_id, block_dict, extra_args=None): r""" Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Chained argument dictionary, (If any) Example: {'chaining_args': {'result': "HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\EventLog\Application\MaxSize", 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug("Executing win_reg module for id: {0}".format(block_id)) chained_result = runner_utils.get_chained_param(extra_args) if chained_result: reg_name = chained_result.get("name") else: reg_name = runner_utils.get_param_for_module(block_id, block_dict, "name") reg_dict = _reg_path_splitter(reg_name) secret = _find_option_value_in_reg(reg_dict.get("hive"), reg_dict.get("key"), reg_dict.get("value")) result = {reg_name: secret} log.debug("win_reg module output for block_id %s, is %s", block_id, result) if secret is False: return runner_utils.prepare_negative_result_for_module( block_id, "registry value couldn't " "be fetched for reg_name {0}".format(reg_name)) return runner_utils.prepare_positive_result_for_module(block_id, result)
def _encode_base64(block_id, block_dict, extra_args): """ Given a string, base64 encode it and return it. block_id: Block id block_dict: starting_string:(Optional) Initial string format_chained: (Default True) By default, ``starting_string`` will have ``.format()`` called on it with ``chained`` as the only argument. (So, use ``{0}`` in your pattern to substitute the chained value.) If you want to avoid having to escape curly braces, set ``format_chained=False``. :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "Output", 'status': True}, 'caller': 'Audit'} The first return value (status) will be False only if an error will occur. """ chained = runner_utils.get_chained_param(extra_args) format_chained = runner_utils.get_param_for_module(block_id, block_dict, 'format_chained', True) starting_string = runner_utils.get_param_for_module( block_id, block_dict, 'starting_string') status, ret = utils_encode_base64(starting_string, format_chained=format_chained, chained=chained) if not status: return runner_utils.prepare_negative_result_for_module( block_id, 'unknown_error') return runner_utils.prepare_positive_result_for_module(block_id, ret)
def _filter_dict_helper(block_id, dct, filter_values, filter_rules): """ Filter a dictionary. block_id Block id dct The input dictionary to be filtered. filter_values ``True`` if the function should filter the values instead of keys, ``False`` otherwise. filter_rules A dict of (comparison_type, value) pairs that dictate the type of filtering where comparison_type can be [gt, lt, eq, ne, ge, le]. For e.g. for ``dct`` = {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'} ``kwargs``={le: 4, gt: 1, ne: 2} the function outputs {3: 'c', 4: 'd'} - key values less than or equal to 4, greater than 1, not equal to 2. """ ret = dct for comp, value in filter_rules.items(): try: ret = { key: val for key, val in ret.items() if (filter_values and _compare(comp, val, value)) or ( not filter_values and _compare(comp, key, value)) } except ArgumentValueError: return runner_utils.prepare_negative_result_for_module( block_id, 'invalid_format') return runner_utils.prepare_positive_result_for_module(block_id, ret)
def execute(block_id, block_dict, extra_args=None): """ Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': {'cmdline': 'app --config-file=test test'}, 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug( 'Executing command_line_parser module for id: {0}'.format(block_id)) try: command_line = None chained_param = runner_utils.get_chained_param(extra_args) if chained_param: command_line = chained_param.get('cmdline') if not command_line: # get it from args command_line = runner_utils.get_param_for_module( block_id, block_dict, 'cmdline') key_aliases = runner_utils.get_param_for_module( block_id, block_dict, 'key_aliases') delimiter = runner_utils.get_param_for_module(block_id, block_dict, 'delimiter') log.debug("command_line: {0}, key_aliases: {1}, delimeter: {2}".format( command_line, key_aliases, delimiter)) ret_match_list = [] for key_alias in key_aliases: log.debug("looping with key_alias %s", key_alias) key_alias = key_alias.lstrip("-") if key_alias not in command_line: log.info("key_alias %s not found in command line %s", key_alias, command_line) continue regex_base = "".join([ r"(?<=(\s|{))-{0,2}\"{0,1}'{0,1}", key_alias, r"\"{0,1}'{0,1}\s*", delimiter ]) regex_list = [] regex_pattern = "".join( [regex_base, r"\s*openingbracket.*closingbracket(?=(\s|$))"]) braces_list = [(r'\[', r'\]'), (r'\{', r'\}'), (r'\(', r'\)')] for item in braces_list: regex = re.sub("openingbracket", item[0], regex_pattern) regex = re.sub("closingbracket", item[1], regex) regex_list.append(regex) regex1 = "".join([regex_base, r"\s*([\"']).*?\2"]) regex2 = "".join([regex_base, r"\s*.+?(?=(\s|$))"]) regex_list.append(regex1) regex_list.append(regex2) for regex in regex_list: log.debug("looping with regex : %s", regex) match_list = _get_match_list(regex, key_alias, command_line, delimiter) if match_list: ret_match_list.extend(match_list) break log.debug("command_line_parser module output for block_id %s, is %s", block_id, ret_match_list) return runner_utils.prepare_positive_result_for_module( block_id, ret_match_list) except Exception as e: log.exception( "Some exception occurred in command_line_parser's parse_cmdline function %s", e) return runner_utils.prepare_negative_result_for_module(block_id, None)
def execute(block_id, block_dict, extra_args=None): """ Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Chained argument dictionary, (If any) Example: {'chaining_args': {'result': "SeRemoteInteractiveLogonRight", 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing win_secedit module for id: {0}'.format(block_id)) chained_result = runner_utils.get_chained_param(extra_args) if chained_result: sec_name = chained_result.get("name") else: sec_name = runner_utils.get_param_for_module(block_id, block_dict, 'name') try: value_type = runner_utils.get_param_for_module(block_id, block_dict, 'value_type') except Exception as e: log.debug( "optional param, 'value_type' not provided as input, taking '' as value type" ) value_type = '' __secdata__ = _secedit_export() coded_sec_value = __secdata__.get(sec_name) if coded_sec_value is None: result = { 'sec_name': sec_name, 'coded_sec_value': "No One", 'sec_value': [''] } log.debug("win_secedit module output for block_id %s, is %s", block_id, result) return runner_utils.prepare_positive_result_for_module( block_id, result) if 'account' == value_type: sec_value = _get_account_name(coded_sec_value) elif 'MACHINE\\' in sec_name: sec_value = _reg_value_reverse_translator(coded_sec_value) else: if ',' in coded_sec_value: sec_value = coded_sec_value.split(',') else: sec_value = [coded_sec_value] if not sec_value: return runner_utils.prepare_negative_result_for_module( block_id, "security config value couldn't be fetched") result = { 'sec_name': sec_name, 'coded_sec_value': coded_sec_value, 'sec_value': sec_value } log.debug("win_secedit module output for block_id %s, is %s", block_id, result) return runner_utils.prepare_positive_result_for_module(block_id, result)
def execute(block_id, block_dict, extra_args=None): """ For getting params to log, in non-verbose logging :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "/some/path/file.txt", 'status': True}, 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing grep module for id: {0}'.format(block_id)) # default mode=file, search in file. # In chaining, there can be two modes: # filepath from chaining # content from chaining filepath = None chained_filepath = None file_mode = True pattern = runner_utils.get_param_for_module(block_id, block_dict, 'pattern') flags = runner_utils.get_param_for_module(block_id, block_dict, 'flags') if flags is None: flags = [] if isinstance(flags, str): flags = [flags] # check if chained content is available format_chained = runner_utils.get_param_for_module(block_id, block_dict, 'format_chained', True) starting_string = runner_utils.get_param_for_module( block_id, block_dict, 'starting_string', False) chaining_file_mode = runner_utils.get_param_for_module( block_id, block_dict, 'chain_filepath', False) if chaining_file_mode: # in chain_filepath mode, filepath from chaining is mandatory chained_filepath = runner_utils.get_chained_param(extra_args) filepath = runner_utils.get_param_for_module(block_id, block_dict, 'path') if format_chained: pattern = pattern.format(chained_filepath) filepath = filepath.format(chained_filepath) else: file_content = runner_utils.get_chained_param(extra_args) if file_content: file_mode = False if format_chained and starting_string: file_content = starting_string.format(file_content) # fetch required param if file_mode: filepath = runner_utils.get_param_for_module( block_id, block_dict, 'path') if format_chained: filepath = filepath.format(file_content) # check filepath existence if file_mode: file_paths = filepath.split(' ') for file_path in file_paths: if not os.path.exists(file_path.strip()): return runner_utils.prepare_negative_result_for_module( block_id, 'file_not_found') grep_result = _grep(filepath, file_content, pattern, *flags) ret_code = grep_result.get('retcode') result = grep_result.get('stdout') log.debug("grep module output for block_id %s, is %s", block_id, result) if ret_code != 0: if ret_code == 1: return runner_utils.prepare_negative_result_for_module( block_id, "pattern_not_found") else: return runner_utils.prepare_negative_result_for_module( block_id, "non_zero_return_code") return runner_utils.prepare_positive_result_for_module(block_id, result)
def _handle_config_helper(block_id, path, pattern=None, ignore_pattern=None, dictsep=None, valsep=None, subsep=None, chained_param=None): """ This is a fairly specialized function designed to pull data from a file with formatting similar to this:: APP_ATTRIBUTES=cluster_role:control;zone:3;provider:aws APP_ATTRIBUTES=cluster_role:worker;zone:1;provider:aws APP_ATTRIBUTES=cluster_role:master;zone:0;provider:aws The arguments decide how the data is parsed and are documented below. path Required argument. The file from which data will be extracted. pattern Optional. Only lines with this pattern will be collected. Regex is supported. If ``pattern`` is not provided, the whole file will be collected. ignore_pattern Optional. Lines with this pattern will be ignored. This overrides ``pattern``. dictsep Optional. Because this is a config-like file, we assume that each line has a key and a value. This is the separator for that key and value. If no ``dictsep`` is provided, we will take the whole line and just return a list of strings instead of a dict with keys and values. Note also that if a file, like the sample data above, has duplicate keys, there will be one key in the resulting dict, with a list of values underneath that key. valsep Optional. A value could be a list, with a defined separator. If this is defined, values will be split on this character or string. subsep Optional. As in the example above, there can be key-value pairs within a value. If this argument is defined, we will split the value (or each member of the value list if ``valsep`` is defined) and turn the result into a key-value pair in a dictionary. If this is defined in conjunction with ``valsep``, the result will be a dictionary, not a list of single-key dictionaries. chained_param Chained values will be called with ``.format()`` on the ``path``. Example: Assuming we have a file ``/tmp/data`` with the lines shown in the sample data above, we could write an fdg file like this: .. code-block:: yaml main: module: readfile.config kwargs: path: /tmp/data pattern: '^APP_ATTRIBUTES' dictsep: '=' valsep: ';' subsep: ':' We would have this data (shown as json) .. code-block:: json {"APP_ATTRIBUTES": [ {"cluster_role": "control", "zone": "3", "provider": "aws"}, {"cluster_role": "worker", "zone": "1", "provider": "aws"}, {"cluster_role": "master", "zone": "0", "provider": "aws"} ] } """ if chained_param is not None: path = path.format(chained_param) if not os.path.isfile(path): log.error('Path %s not found.', path) return runner_utils.prepare_negative_result_for_module( block_id, 'file_not_found') if dictsep is None: ret = _lines_as_list(path, pattern, ignore_pattern) else: # Lines as key/value pairs in a dict ret = _lines_as_dict(path, pattern, ignore_pattern, dictsep, valsep, subsep) if ret is not None: return runner_utils.prepare_positive_result_for_module(block_id, ret) return runner_utils.prepare_negative_result_for_module(block_id, ret)
def _handle_file_helper(file_format, block_id, path, subkey=None, sep=None, chained_param=None): """ Pull data (optionally from a subkey) of a json object in a file at ``path`` path Path of file to be read in subkey Optional. Key to pull out of json dict. If ``sep`` is defined, you can use it to separate subkeys and pull a value out of the depths of a dictionary. Note that we try to detect non-dict objects and assume if we find a non-dict object that it is a list, and that the subkey at that level is an integer. sep Separator in ``subkey``. If not defined, ``subkey`` will not be split. chained_param Value passed in via chaining in fdg. Will be called with ``.format()`` on the path and subkey if defined. """ if chained_param: path = path.format(chained_param) if subkey: subkey = subkey.format(chained_param) if not os.path.isfile(path): log.error('Path %s not found.', path) return runner_utils.prepare_negative_result_for_module( block_id, 'file_not_found') ret = None try: with open(path, 'r') as file_handle: if file_format == 'json': ret = _json.load(file_handle) elif file_format == 'yaml': ret = _yaml.safe_load(file_handle) else: return runner_utils.prepare_negative_result_for_module( block_id, 'unknown_format') except Exception: log.error('Error reading file %s.', path, exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'exception while reading file') if subkey: subkey = [subkey] if not sep else subkey.split(sep) try: # Traverse dictionary for key in subkey: if not isinstance(ret, dict): # If it's not a dict, assume it's a list and that `key` is an int key = int(key) if key in ret: ret = ret[key] elif isinstance(key, int): ret = ret[key] else: log.error("key '%s' not found in dictionary '%s'", key, ret) return runner_utils.prepare_negative_result_for_module( block_id, 'KeyError') except (KeyError, TypeError, ValueError, IndexError): log.error('Error traversing dict.', exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'unknown_error') return runner_utils.prepare_positive_result_for_module(block_id, ret)
def execute(block_id, block_dict, extra_args=None): """ For getting params to log, in non-verbose logging :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) returns: tuple of result(value) and status(boolean) """ log.debug('Executing Curl module for id: {0}'.format(block_id)) chained_param = runner_utils.get_chained_param(extra_args) if chained_param: log.warn('Chained value detected in curl.request module. Chained ' 'values are unsupported in the curl module.') url = runner_utils.get_param_for_module(block_id, block_dict, 'url') kwargs = {} function_name = runner_utils.get_param_for_module(block_id, block_dict, 'function') if not function_name: function_name = 'GET' params = runner_utils.get_param_for_module(block_id, block_dict, 'params') if params: kwargs['params'] = params data = runner_utils.get_param_for_module(block_id, block_dict, 'data') if data: kwargs['data'] = data username = runner_utils.get_param_for_module(block_id, block_dict, 'username') password = runner_utils.get_param_for_module(block_id, block_dict, 'password') if username: kwargs['auth'] = (username, password) verify = runner_utils.get_param_for_module(block_id, block_dict, 'verify') if verify: kwargs['verify'] = verify headers = runner_utils.get_param_for_module(block_id, block_dict, 'headers') if headers: kwargs['headers'] = headers timeout = runner_utils.get_param_for_module(block_id, block_dict, 'timeout') if not timeout: timeout = 9 kwargs['timeout'] = int(timeout) decode_json = runner_utils.get_param_for_module(block_id, block_dict, 'decode_json') if not decode_json: decode_json = True # Make the request status, response = _make_request(function_name, url, **kwargs) if not status: return runner_utils.prepare_negative_result_for_module( block_id, response) # Pull out the pieces we want ret = _parse_response(response, decode_json) # Status in the return is based on http status try: response.raise_for_status() return runner_utils.prepare_positive_result_for_module(block_id, ret) except requests.exceptions.HTTPError: return runner_utils.prepare_negative_result_for_module(block_id, ret)
def _osquery(block_id, query, osquery_path=None, args=None, cast_to_string=None): """ Format the osquery command and run it Returns a tuple, (status, ret) where status is True if the return code is 0, False otherwise, and ``ret`` is the stdout of the osquery command """ max_file_size = 104857600 if not query: return runner_utils.prepare_negative_result_for_module( block_id, 'Empty query passed') if 'attach' in query.lower() or 'curl' in query.lower(): log.critical( 'Skipping potentially malicious osquery query \'%s\' ' 'which contains either \'attach\' or \'curl\'', query) return runner_utils.prepare_negative_result_for_module( block_id, 'Curl/Attach passed in query') # Prep the command if not osquery_path: if not os.path.isfile(__grains__['osquerybinpath']): log.error('osquery binary not found: %s', __grains__['osquerybinpath']) return runner_utils.prepare_negative_result_for_module( block_id, 'osquery binary not found') cmd = [ __grains__['osquerybinpath'], '--read_max', max_file_size, '--json', query ] else: if not os.path.isfile(osquery_path): log.error('osquery binary not found: %s', osquery_path) return runner_utils.prepare_negative_result_for_module( block_id, 'osquery binary not found') cmd = [osquery_path, '--read_max', max_file_size, '--json', query] if isinstance(args, (list, tuple)): cmd.extend(args) # Run the command res = __mods__['cmd.run_all'](cmd, timeout=10000, python_shell=False) if res['retcode'] == 0: ret = json.loads(res['stdout']) for result in ret: for key, value in result.items(): if value and isinstance( value, str) and value.startswith('__JSONIFY__'): result[key] = json.loads(value[len('__JSONIFY__'):]) if cast_to_string: try: ret = _convert_to_str(ret) except (KeyError, TypeError): log.error('Invalid data type returned by osquery call %s.', res, exc_info=True) return runner_utils.prepare_negative_result_for_module( block_id, 'Error while casting to string') return runner_utils.prepare_positive_result_for_module(block_id, ret) else: ret = {'stdout': res.get('stdout'), 'stderr': res.get('stderr')} return runner_utils.prepare_negative_result_for_module(block_id, ret)
def execute(block_id, block_dict, extra_args=None): """ Execute the module :param block_id: id of the block :param block_dict: parameter for this module :param extra_args: Extra argument dictionary, (If any) Example: {'chaining_args': {'result': "True", 'status': True}, 'extra_args': [{'check_id': 'ADOBE-01', 'check_status': 'Success'}] 'caller': 'Audit'} returns: tuple of result(value) and status(boolean) """ log.debug('Executing bexpr module for check-id: %s' % block_id) result_list = extra_args.get('extra_args') keyword_list = ['AND', 'OR', 'NOT', '(', ')'] operand_list = ['AND', 'OR', 'NOT'] expression = runner_utils.get_param_for_module(block_id, block_dict, 'expr') original_expression = expression # Separating keywords on the basis of space expression = expression.replace('(', ' ( ') expression = expression.replace(')', ' ) ') # Parse the expression and evaluate the result # Splitting the expression on the basis of spaces expr_list = expression.split(" ") # Filtering out empty spaces expr_list = list(filter(None, expr_list)) referred_checks_list = [] referred_checks_result = {} operand_present = 0 for expr in expr_list: if expr.upper() not in keyword_list: referred_checks_list.append(expr) elif expr.upper() in operand_list: operand_present += 1 # Fetch the result of check from result list and store the result of referenced checks # In case a check is not present in result list or referred check result is not Success or Failure, raise an Error error = {} if len(referred_checks_list) == 0: error[ block_id] = "No checks are referred in the boolean expression: %s" % original_expression if len(referred_checks_list) > 1 and operand_present == 0: error[ block_id] = "No operand is present for multiple referred checks in boolean expression: %s" % original_expression if error: raise HubbleCheckValidationError(error) for referred_check_id in referred_checks_list: check_found = False for result in result_list: if result.get('check_unique_id', '') == referred_check_id: check_found = True check_result = result.get('check_result', '') if check_result == "Success": referred_checks_result[referred_check_id] = "True" elif check_result == "Failure": referred_checks_result[referred_check_id] = "False" else: error[ block_id] = "Referred check: %s result is %s. Setting boolean expression check result to error." % ( referred_check_id, check_result) break if not check_found: error[ block_id] = "Referred check: %s is not available. Please verify correct check is referred." % ( referred_check_id) if error: raise HubbleCheckValidationError(error) try: check_result = _evaluate_expression(expr_list, keyword_list, referred_checks_result) except Exception as e: error[ block_id] = "Error in evaluating boolean expression: %s Please verify the expression" % original_expression raise HubbleCheckValidationError(error) if not bool(check_result): log.info("Boolean expression: '%s' evaluated to failure" % original_expression) return runner_utils.prepare_positive_result_for_module(block_id, False) return runner_utils.prepare_positive_result_for_module(block_id, True)