def get_ossec_conf(section=None, field=None, filename=common.ossec_conf): """ Returns ossec.conf (manager) as dictionary. :param section: Filters by section (i.e. rules). :param field: Filters by field in section (i.e. included). :return: ossec.conf (manager) as dictionary. """ try: # Read XML xml_data = load_ossec_xml(filename) # Parse XML to JSON data = _ossecconf2json(xml_data) except Exception as e: raise OssecAPIException(1101, str(e)) if section: try: data = data[section] except KeyError as e: if section not in conf_sections.keys(): raise OssecAPIException(1102, e.args[0]) else: raise OssecAPIException(1106, e.args[0]) if section and field: try: data = data[field] # data[section][field] except: raise OssecAPIException(1103) return data
def cut_array(array, offset, limit): """ Returns a part of the array: from offset to offset + limit. :param array: Array to cut. :param offset: First element to return. :param limit: Maximum number of elements to return. 0 means no cut array. :return: cut array. """ if limit is not None: if limit > common.maximum_database_limit: raise OssecAPIException(1405, str(limit)) elif limit == 0: raise OssecAPIException(1406) elif not array or limit is None: return array offset = int(offset) limit = int(limit) if offset < 0: raise OssecAPIException(1400) elif limit < 1: raise OssecAPIException(1401) else: return array[offset:offset + limit]
def get_agent_conf(group_id=None, offset=0, limit=common.database_limit, filename=None): """ Returns agent.conf as dictionary. :return: agent.conf as dictionary. """ if group_id: if not Agent.group_exists(group_id): raise OssecAPIException(1710, group_id) agent_conf = "{0}/{1}".format(common.shared_path, group_id) if filename: agent_conf_name = filename else: agent_conf_name = 'agent.conf' agent_conf += "/{0}".format(agent_conf_name) if not os_path.exists(agent_conf): raise OssecAPIException(1006, agent_conf) try: # Read XML xml_data = load_ossec_xml(agent_conf) # Parse XML to JSON data = _agentconf2json(xml_data) except Exception as e: raise OssecAPIException(1101, str(e)) return {'totalItems': len(data), 'items': cut_array(data, offset, limit)}
def parse_internal_options(high_name, low_name): def get_config(config_path): with open(config_path) as f: str_config = StringIO('[root]\n' + f.read()) config = RawConfigParser() config.readfp(str_config) return config if not os_path.exists(common.internal_options): raise OssecAPIException(1107) # Check if the option exists at local internal options if os_path.exists(common.local_internal_options): try: return get_config(common.local_internal_options).get( 'root', '{0}.{1}'.format(high_name, low_name)) except NoOptionError: pass try: return get_config(common.internal_options).get( 'root', '{0}.{1}'.format(high_name, low_name)) except NoOptionError as e: raise OssecAPIException(1108, e.args[0])
def execute(command): """ Executes a command. It is used to execute ossec commands. """ try: output = check_output(command) except CalledProcessError as error: output = error.output except Exception as e: raise OssecAPIException(1002, "{0}: {1}".format( command, e)) # Error executing command try: output_json = json.loads(output) except Exception as e: raise OssecAPIException(1003, command) # Command output not in json keys = output_json.keys() # error and (data or message) if 'error' not in keys or ('data' not in keys and 'message' not in keys): raise OssecAPIException(1004, command) # Malformed command output if output_json['error'] != 0: raise OssecAPIException(output_json['error'], output_json['message'], True) else: return output_json['data']
def _send(self, msg): try: sent = self.socket.send(msg) if sent == 0: raise OssecAPIException(1011, self.path) except: raise OssecAPIException(1011, self.path)
def send(self, msg): try: sent = self.s.send(dumps(msg)) if sent == 0: raise OssecAPIException(1014, self.path) return sent except: raise OssecAPIException(1014, self.path)
def sort_array(array, sort_by=None, order='asc', allowed_sort_fields=None): """ Sorts an array. :param array: Array to sort. :param sort_by: Array of fields. :param order: asc or desc. :param allowed_sort_fields: Check sort_by with allowed_sort_fields (array). :return: sorted array. """ def check_sort_fields(allowed_sort_fields, sort_by): # Check if every element in sort['fields'] is in allowed_sort_fields if not sort_by.issubset(allowed_sort_fields): incorrect_fields = ', '.join(sort_by - allowed_sort_fields) raise OssecAPIException( 1403, 'Allowed sort fields: {0}. Fields: {1}'.format( ', '.join(allowed_sort_fields), incorrect_fields)) if not array: return array if order.lower() == 'desc': order_desc = True elif order.lower() == 'asc': order_desc = False else: raise OssecAPIException(1402) if allowed_sort_fields: check_sort_fields(set(allowed_sort_fields), set(sort_by)) if sort_by: # array should be a dictionary or a Class if type(array[0]) is dict: check_sort_fields(set(array[0].keys()), set(sort_by)) return sorted(array, key=lambda o: tuple( o.get(a).lower() if type(o.get(a)) in (str, unicode) else o.get(a) for a in sort_by), reverse=order_desc) else: return sorted(array, key=lambda o: tuple( getattr(o, a).lower() if type(getattr(o, a)) in (str, unicode) else getattr(o, a) for a in sort_by), reverse=order_desc) else: if type(array) is set or (type(array[0]) is not dict and 'class \'wazuh' not in str(type(array[0]))): return sorted(array, reverse=order_desc) else: raise OssecAPIException(1404)
def get_file_conf(filename, group_id=None, type_conf=None): """ Returns the configuration file as dictionary. :return: configuration file as dictionary. """ if group_id: if not Agent.group_exists(group_id): raise OssecAPIException(1710, group_id) file_path = "{0}/{1}".format(common.shared_path, filename) \ if filename == 'ar.conf' else \ "{0}/{1}/{2}".format(common.shared_path, group_id, filename) else: file_path = "{0}/{1}".format(common.shared_path, filename) if not os_path.exists(file_path): raise OssecAPIException(1006, file_path) types = { 'conf': get_agent_conf, 'rootkit_files': _rootkit_files2json, 'rootkit_trojans': _rootkit_trojans2json, 'rcl': _rcl2json } data = {} if type_conf: if type_conf in types: if type_conf == 'conf': data = types[type_conf](group_id, limit=None, filename=filename) else: data = types[type_conf](file_path) else: raise OssecAPIException( 1104, "{0}. Valid types: {1}".format(type_conf, types.keys())) else: if filename == "agent.conf": data = get_agent_conf(group_id, limit=None, filename=filename) elif filename == "rootkit_files.txt": data = _rootkit_files2json(file_path) elif filename == "rootkit_trojans.txt": data = _rootkit_trojans2json(file_path) elif filename == "ar.conf": data = _ar_conf2json(file_path) else: data = _rcl2json(file_path) return data
def get_internal_options_value(high_name, low_name, max, min): option = parse_internal_options(high_name, low_name) if not option.isdigit(): raise OssecAPIException( 1109, 'Option: {}.{}. Value: {}'.format(high_name, low_name, option)) option = int(option) if option < min or option > max: raise OssecAPIException( 1110, 'Max value: {}. Min value: {}. Found: {}.'.format( max, min, option)) return option
def run_command(agent_id=None, command=None, arguments=[], custom=False): """ Run AR command. :param agent_id: Run AR command in the agent. :return: Message. """ if not command: raise OssecAPIException(1650, "Command not specified") if not agent_id: raise OssecAPIException(1650, "Agent ID not specified") commands = get_commands() if not custom and command not in commands: raise OssecAPIException(1650, "Command not available") # Create message msg_queue = command if custom: msg_queue = "!{}".format(command) if arguments: msg_queue += " " + " ".join(shell_escape(str(x)) for x in arguments) else: msg_queue += " - -" # Send if agent_id == "000" or agent_id == "all": oq = OssecQueue(common.EXECQ) ret_msg = oq.send_msg_to_agent(msg=msg_queue, agent_id=agent_id, msg_type=OssecQueue.AR_TYPE) oq.close() if agent_id != "000" or agent_id == "all": if agent_id != "all": # Check if agent exists and it is active agent_info = Agent(agent_id).get_basic_information() if agent_info['status'].lower() != 'active': raise OssecAPIException(1651) if agent_id == "all": agent_id = None oq = OssecQueue(common.ARQUEUE) ret_msg = oq.send_msg_to_agent(msg=msg_queue, agent_id=agent_id, msg_type=OssecQueue.AR_TYPE) oq.close() return ret_msg
def receive(self): try: chunk = self.s.recv(OssecSocket.MAX_SIZE) response = loads(chunk) except: raise OssecAPIException(1014, self.path) if 'error' in response.keys(): if response['error'] != 0: raise OssecAPIException(response['error'], response['message'], cmd_error=True) else: return response['data']
def __load_decoders_from_file(decoder_file, decoder_path, decoder_status): try: decoders = [] position = 0 # root = load_ossec_xml("{}/{}".format(decoder_path, decoder_file)) root = load_ossec_xml("{}".format(decoder_path)) for xml_decoder in root.getchildren(): # New decoder if xml_decoder.tag.lower() == "decoder": decoder = Decoder() decoder.path = decoder_path decoder.file = decoder_file decoder.status = decoder_status decoder.name = xml_decoder.attrib['name'] decoder.position = position position += 1 for k in xml_decoder.attrib: if k != 'name': decoder.details[k] = xml_decoder.attrib[k] for xml_decoder_tags in xml_decoder.getchildren(): decoder.add_detail(xml_decoder_tags.tag.lower(), xml_decoder_tags.text) decoders.append(decoder) except Exception as e: raise OssecAPIException( 1501, "{0}. Error: {1}".format(decoder_file, str(e))) return decoders
def _get_requirement(offset, limit, sort, search, requirement): """ Get the requirements used in the rules :param offset: First item to return. :param limit: Maximum number of items to return. :param sort: Sorts the items. Format: {"fields":["field1","field2"],"order":"asc|desc"}. :param search: Looks for items with the specified string. :param requirement: requirement to get (pci or dgpr) :return: Dictionary: {'items': array of items, 'totalItems': Number of items (without applying the limit)} """ if requirement != 'pci' and requirement != 'gdpr': raise OssecAPIException(1205, requirement) req = list({req for rule in Rule.get_rules(limit=None)['items'] for req in rule.to_dict()[requirement]}) if search: req = search_array(req, search['value'], search['negation']) if sort: req = sort_array(req, order=sort['order']) else: req = sort_array(req) return {'items': cut_array(req, offset, limit), 'totalItems': len(req)}
def __check_status(status): if status is None: return Rule.S_ALL elif status in [Rule.S_ALL, Rule.S_ENABLED, Rule.S_DISABLED]: return status else: raise OssecAPIException(1202)
def _rootkit_trojans2json(filepath): """ Returns the rootkit trojans file as dictionary. :return: rootkit trojans file as dictionary. """ data = [] # file_name !string_to_search!Description regex_comment = re.compile(r"^\s*#") regex_check = re.compile(r"^\s*(.+)\s+!\s*(.+)\s*!\s*(.+)") try: with open(filepath) as f: for line in f: if re.search(regex_comment, line): continue match_check = re.search(regex_check, line) if match_check: new_check = { 'filename': match_check.group(1).strip(), 'name': match_check.group(2).strip(), 'description': match_check.group(3).strip() } data.append(new_check) except Exception as e: raise OssecAPIException(1101, str(e)) return data
def __check_status(status): if status is None: return Decoder.S_ALL elif status in [Decoder.S_ALL, Decoder.S_ENABLED, Decoder.S_DISABLED]: return status else: raise OssecAPIException(1202)
def check_sort_fields(allowed_sort_fields, sort_by): # Check if every element in sort['fields'] is in allowed_sort_fields if not sort_by.issubset(allowed_sort_fields): incorrect_fields = ', '.join(sort_by - allowed_sort_fields) raise OssecAPIException( 1403, 'Allowed sort fields: {0}. Fields: {1}'.format( ', '.join(allowed_sort_fields), incorrect_fields))
def delete_pid(name, pid): filename = "{0}{1}/{2}-{3}.pid".format(common.ossec_path, common.os_pidfile, name, pid) try: if os.path.exists(filename): os.unlink(filename) except OSError as e: raise OssecAPIException(3003, str(e))
def clear(agent_id=None, all_agents=False): """ Clears the database. :param agent_id: For an agent. :param all_agents: For all agents. :return: Message. """ # Clear DB conn = Connection(common.database_path) regex = re.compile(r'^\d{,3}-\S+$') db_agents_list = [] if not int(all_agents): raw_str = r'^' + "{}".format(int(agent_id)).zfill(3) + r'-\S+$' regex = re.compile(raw_str) for db_agent in conn.getDbsName(): if (regex.search(db_agent) != None): db_agents_list.append(db_agent) if (db_agents_list.count() <= 0): raise OssecAPIException(1600) for db_agent in db_agents_list: conn.connect(db_agent) if conn.getDb() != None: doc = conn.getDb()['pm_event'] if doc != None: doc.drop() conn.vacuum() doc = conn.getDb()['pmCounterInfo'] if doc != None: doc.drop() conn.vacuum() # Clear OSSEC info if int(all_agents): rootcheck_files = glob('{0}/queue/rootcheck/*'.format( common.ossec_path)) else: if agent_id == "000": rootcheck_files = [ '{0}/queue/rootcheck/rootcheck'.format(common.ossec_path) ] else: agent_info = Agent(agent_id).get_basic_information() rootcheck_files = glob( '{0}/queue/rootcheck/({1}) {2}->rootcheck'.format( common.ossec_path, agent_info['name'], agent_info['ip'])) for rootcheck_file in rootcheck_files: if path.exists(rootcheck_file): remove(rootcheck_file) return "Rootcheck database deleted"
def create_pid(name, pid): filename = "{0}{1}/{2}-{3}.pid".format(common.ossec_path, common.os_pidfile, name, pid) with open(filename, 'a') as fp: try: fp.write("{0}\n".format(pid)) os.chmod(filename, 0o640) except OSError as e: raise OssecAPIException(3002, str(e))
def run(agent_id=None, all_agents=False): """ Runs rootcheck and syscheck. :param agent_id: Run rootcheck/syscheck in the agent. :param all_agents: Run rootcheck/syscheck in all agents. :return: Message. """ if agent_id == "000" or all_agents: try: SYSCHECK_RESTART = "{0}/var/run/.syscheck_run".format( common.ossec_path) fp = open(SYSCHECK_RESTART, 'w') fp.write('{0}\n'.format(SYSCHECK_RESTART)) fp.close() ret_msg = "Restarting Syscheck/Rootcheck locally" except: raise OssecAPIException(1601, "locally") if all_agents: oq = OssecQueue(common.ARQUEUE) ret_msg = oq.send_msg_to_agent(OssecQueue.HC_SK_RESTART) oq.close() else: # Check if agent exists and it is active agent_info = Agent(agent_id).get_basic_information() if 'status' in agent_info: agent_status = agent_info['status'] else: agent_status = "N/A" if agent_status.lower() != 'active': raise OssecAPIException(1602, '{0} - {1}'.format(agent_id, agent_status)) oq = OssecQueue(common.ARQUEUE) ret_msg = oq.send_msg_to_agent(OssecQueue.HC_SK_RESTART, agent_id) oq.close() return ret_msg
def _connect(self): try: self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) self.socket.connect(self.path) length_send_buffer = self.socket.getsockopt( socket.SOL_SOCKET, socket.SO_SNDBUF) if length_send_buffer < OssecQueue.MAX_MSG_SIZE: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, OssecQueue.MAX_MSG_SIZE) except: raise OssecAPIException(1010, self.path)
def last_scan(agent_id): """ Gets the last scan of the agent. :param agent_id: Agent ID. :return: Dictionary: end, start. """ # Connection db_url = common.database_path conn = Connection(db_url) conn.connect(conn.getDbById(str(agent_id).zfill(3))) if (conn.getDb() == None): raise OssecAPIException(1600) data = {} lastSyscheckEndTime = None lastSyscheckEndTimeObj = list((conn.getDb()['pm_event'].find({ "log": 'Ending syscheck scan.' }).sort([('date_last', -1)]).limit(1)))[0] if (lastSyscheckEndTimeObj != None): lastSyscheckEndTime = lastSyscheckEndTimeObj.get('date_last') if lastSyscheckEndTime != None: data['end'] = (lastSyscheckEndTime + timedelta(seconds=timeoffset)).__str__() else: data['end'] = lastSyscheckEndTime.__str__() lastSyscheckStartTime = None lastSyscheckStartTimeObj = list((conn.getDb()['pm_event'].find({ "log": 'Starting syscheck scan.' }).sort([('date_last', -1)]).limit(1)))[0] if (lastSyscheckStartTimeObj != None): lastSyscheckStartTime = lastSyscheckStartTimeObj.get('date_last') if lastSyscheckStartTime != None: data['start'] = (lastSyscheckStartTime + timedelta(seconds=timeoffset)).__str__() else: data['start'] = lastSyscheckStartTime.__str__() return data
def _rcl2json(filepath): """ Returns the RCL file as dictionary. :return: rcl file (system_audit, windows_audit) as dictionary. """ data = {'vars': {}, 'controls': []} # [Application name] [any or all] [reference] # type:<entry name>; regex_comment = re.compile(r"^\s*#") regex_title = re.compile(r"^\s*\[(.*)\]\s*\[(.*)\]\s*\[(.*)\]\s*") regex_name_groups = re.compile(r"(\{\w+:\s+\S+\s*\S*\})") regex_check = re.compile(r"^\s*(\w:.+)") regex_var = re.compile(r"^\s*\$(\w+)=(.+)") try: item = {} with open(filepath) as f: for line in f: if re.search(regex_comment, line): continue match_title = re.search(regex_title, line) if match_title: # Previous if item: data['controls'].append(item) # New name = match_title.group(1) condition = match_title.group(2) reference = match_title.group(3) item = {} # Name end_name = name.find('{') item['name'] = name[:end_name].strip() # Extract PCI and CIS from name name_groups = re.findall(regex_name_groups, name) cis = [] pci = [] if name_groups: for group in name_groups: # {CIS: 1.1.2 RHEL7} g_value = group.split(':')[-1][:-1].strip() if 'CIS' in group: cis.append(g_value) elif 'PCI' in group: pci.append(g_value) if cis: item['cis'] = cis if pci: item['pci'] = pci # Conditions if condition: item['condition'] = condition if reference: item['reference'] = reference item['checks'] = [] continue match_checks = re.search(regex_check, line) if match_checks: item['checks'].append(match_checks.group(1)) continue match_var = re.search(regex_var, line) if match_var: data['vars'][match_var.group(1)] = match_var.group(2) continue # Last item data['controls'].append(item) except Exception as e: raise OssecAPIException(1101, str(e)) return data
def print_db(agent_id=None, status='all', pci=None, offset=0, limit=common.database_limit, sort=None, search=None): """ Returns a list of events from the database. :param agent_id: Agent ID. :param status: Filters by status: outstanding, solved, all. :param pci: Filters by PCI DSS requirement. :param cis: Filters by CIS. :param offset: First item to return. :param limit: Maximum number of items to return. :param sort: Sorts the items. Format: {"fields":["field1","field2"],"order":"asc|desc"}. :param search: Looks for items with the specified string. :return: Dictionary: {'items': array of items, 'totalItems': Number of items (without applying the limit)} """ # Connection db_url = common.database_path conn = Connection(db_url) conn.connect(conn.getDbById(str(agent_id).zfill(3))) if (conn.getDb() == None): raise OssecAPIException(1600) request = {"$and": []} lastRootcheckEndTime = None lastRootcheckEndTimeObj = list((conn.getDb()['pm_event'].find({ "log": 'Ending rootcheck scan.' }).sort([('date_last', -1)]).limit(1)))[0] if (lastRootcheckEndTimeObj != None): lastRootcheckEndTime = lastRootcheckEndTimeObj.get( 'date_last', datetime.now()) fields = { 'status': 'status', 'event': 'log', 'oldDay': 'date_first', 'readDay': 'date_last' } request['$and'].append({ 'log': { '$nin': [ 'Starting rootcheck scan.', 'Ending rootcheck scan.', 'Starting syscheck scan.', 'Ending syscheck scan.' ] } }) if status == 'outstanding': if lastRootcheckEndTime != None: request['$and'].append({ 'date_last': { '$gt': (lastRootcheckEndTime - timedelta(seconds=86400)) } }) elif status == 'solved': if lastRootcheckEndTime != None: request['$and'].append({ 'date_last': { '$lte': (lastRootcheckEndTime - timedelta(seconds=86400)) } }) if pci: request["$and"].append({"pci_dss": pci}) # search if search: regex = re.compile(".*{0}.*".format(int(search['value']) if search['value'].isdigit() \ else search['value']), re.IGNORECASE) search_con = {"$or": []} for x in fields.values(): search_con["$or"].append({x: regex}) if bool(search['negation']): if search_con["$or"]: request["$and"].append({"$not": search_con}) else: if search_con["$or"]: request["$and"].append(search_con) # Sorting sort_con = [] if sort: if sort['fields']: allowed_sort_fields = set(fields.keys()) # Check if every element in sort['fields'] is in allowed_sort_fields if not set(sort['fields']).issubset(allowed_sort_fields): uncorrect_fields = list( map(lambda x: str(x), set(sort['fields']) - set(allowed_sort_fields))) raise OssecAPIException( 1403, 'Allowed sort fields: {0}. Fields: {1}'.format( allowed_sort_fields, uncorrect_fields)) for i in sort['fields']: str_order = 1 if sort['order'] == 'asc' else -1 sort_con.append((Agent.fields[i], str_order)) else: sort_con.append( (fields["readDay"], 1 if sort['order'] == 'asc' else -1)) else: sort_con.append((fields["readDay"], -1)) if limit: if limit > common.maximum_database_limit: raise OssecAPIException(1405, str(limit)) elif limit == 0: raise OssecAPIException(1406) select = ["status", "date_first", "date_last", "log", "pci_dss"] select_fields = {} for x in set(select): select_fields[x] = 1 if not request["$and"]: request = {} data = {} db_data = conn.getDb()['pm_event'].find(request, select_fields) data['totalItems'] = db_data.count() db_data = db_data.sort(sort_con).skip(offset).limit(limit) # process get data data['items'] = [] for pmEvent in db_data: pmEvent.pop('_id') if pmEvent.get("date_last") != None: if (pmEvent['date_last'] > lastRootcheckEndTime): pmEvent['status'] = 'outstanding' elif (pmEvent['date_last'] <= lastRootcheckEndTime): pmEvent['status'] = 'solved' if pmEvent.get("date_first") != None: pmEvent['date_first'] = (pmEvent.get("date_first") + timedelta(seconds=timeoffset)).__str__() else: pmEvent['date_first'] = pmEvent.get("date_first").__str__() if pmEvent.get("date_last") != None: pmEvent['date_last'] = (pmEvent.get("date_last") + timedelta(seconds=timeoffset)).__str__() else: pmEvent['date_last'] = pmEvent.get("date_last").__str__() data['items'].append(pmEvent) return data
def files(agent_id=None, event=None, filename=None, filetype='file', md5=None, sha1=None, hash=None, summary=False, offset=0, limit=common.database_limit, sort=None, search=None): """ Return a list of files from the database that match the filters :param agent_id: Agent ID. :param event: Filters by event: added, readded, modified, deleted. :param filename: Filters by filename. :param filetype: Filters by filetype: file or registry. :param md5: Filters by md5 hash. :param sha1: Filters by sha1 hash. :param hash: Filters by md5 or sha1 hash. :param summary: Returns a summary grouping by filename. :param offset: First item to return. :param limit: Maximum number of items to return. :param sort: Sorts the items. Format: {"fields":["field1","field2"],"order":"asc|desc"}. :param search: Looks for items with the specified string. :return: Dictionary: {'items': array of items, 'totalItems': Number of items (without applying the limit)} """ # Connection db_url = common.database_path conn = Connection(db_url) conn.connect(conn.getDbById(str(agent_id).zfill(3))) if (conn.getDb() == None): raise OssecAPIException(1600) agent_info = Agent(agent_id).get_basic_information() if 'os' in agent_info: if 'windows' in agent_info['os']['name'].lower(): windows_agent = True else: windows_agent = False else: windows_agent = False # if 'os' in agent_info and 'platform' in agent_info['os']: # if agent_info['os']['platform'].lower() == 'windows': # windows_agent = True # else: # windows_agent = False # else: # # We do not know if it is a windows or linux agent. # # It is set to windows agent in order to avoid wrong data (uid, gid, ...) # windows_agent = True eventRequest = {"$and": []} fileRequest = {"$and": []} eventFields = { 'scanDate': 'date', 'modificationDate': 'mtime', 'size': 'size', 'user': '******', 'group': 'gname' } fileFields = {'file': 'path', 'filetype': 'type'} # Query # query = "SELECT {0} FROM fim_event, fim_file WHERE fim_event.id_file = fim_file.id AND fim_file.type = :filetype" # fileRequest['$and'].append({ # 'type': filetype # }) # if event: # # query += ' AND fim_event.type = :event' # # request['event'] = event # eventRequest['$and'].append({ # 'event': event # }) # if filename: # # query += ' AND path = :filename' # # request['filename'] = filename # fileRequest['$and'].append({ # 'path': filename # }) # if md5: # # query += ' AND md5 = :md5' # # request['md5'] = md5 # eventRequest['$and'].append({ # 'md5': md5 # }) # if sha1: # # query += ' AND sha1 = :sha1' # # request['sha1'] = sha1 # eventRequest['$and'].append({ # 'sha1': sha1 # }) # if hash: # # query += ' AND (md5 = :hash OR sha1 = :hash)' # # request['hash'] = hash # eventRequest['$and'].append({ # '$or': [ # { # 'md5': hash # }, # { # 'sha1': hash # } # ] # }) # if search: # query += " AND NOT" if bool(search['negation']) else ' AND' # query += " (" + " OR ".join(x + ' LIKE :search' for x in ('path', "date", 'size', 'md5', 'sha1', 'uname', 'gname', 'inode', 'perm')) + " )" # request['search'] = '%{0}%'.format(search['value']) if search: regex = re.compile(".*{0}.*".format(int(search['value']) if search['value'].isdigit() \ else search['value']), re.IGNORECASE) event_search_con = {"$or": []} file_search_con = {"$or": []} for x in [ 'path', "date", 'size', 'md5', 'sha1', 'uname', 'gname', 'perm' ]: if x == 'path': file_search_con["$or"].append({x: regex}) else: event_search_con["$or"].append({x: regex}) if bool(search['negation']): if event_search_con["$or"]: eventRequest["$and"].append({"$not": event_search_con}) if file_search_con["$or"]: fileRequest["$and"].append({"$not": file_search_con}) else: if event_search_con["$or"]: eventRequest["$and"].append(event_search_con) if file_search_con["$or"]: fileRequest["$and"].append(file_search_con) # Total items db_data = None events = [] if summary: db_data = conn.getDb()['fim_file'].aggregate([ { '$lookup': { 'from': 'fim_event', 'localField': '_id', 'foreignField': 'file_id', 'as': 'fim_events' } }, ], cursor={}) for sysFile in db_data: item = sysFile for fEvent in item['fim_events']: if not item.get('fim_event'): item['fim_event'] = fEvent else: if fEvent['date'] > item['fim_event']['date']: item['fim_event'] = fEvent item.pop('fim_events') if item['type'] != filetype: continue if event: if item['fim_event']['type'] != event: continue if filename: if item['path'] != filename: continue if md5: if item['fim_event']['md5'] != md5: continue if sha1: if item['fim_event']['sha1'] != md5: continue if hash: if (item['fim_event']['sha1'] != hash) and (item['fim_event']['md5'] != hash): continue if search: search_value = int(search['value']) if search['value'].isdigit( ) else search['value'] if (search_value not in item['path']) and (search_value not in item['fim_event']['date']) \ and (search_value not in item['fim_event']['size']) and (search_value not in item['fim_event']['md5']) \ and (search_value not in item['fim_event']['sha1']) and (search_value not in item['fim_event']['uname']) \ and (search_value not in item['fim_event']['gname']) and (search_value not in item['fim_event']['perm']): continue item['sha1'] = item['fim_event']['sha1'] item['uid'] = item['fim_event']['uid'] item['date'] = item['fim_event']['date'] item['gid'] = item['fim_event']['gid'] # item['mtime'] = item['fim_event']['mtime'] item['perm'] = item['fim_event']['perm'] item['md5'] = item['fim_event']['md5'] item.pop('fim_event') item['fim_event.type'] = item['type'] item.pop('type') events.append(item) # rFileRequest = fileRequest.copy() # if not rFileRequest['$and']: # rFileRequest = {} # db_data = conn.getDb()['fim_file'].find_one(rFileRequest) # # list_db_data = list(db_data) # for eFile in db_data: # rEventRequest = eventRequest.copy() # rEventRequest['$and'].append({ # 'file_id': eFile.get('_id') # }) # event_data = conn.getDb()['fim_event'].find(rEventRequest).sort(('date', -1)).limit(1) # if event_data.count() == 1: # item = list(event_data)[0] # item['type'] = eFile.get('type') # item['type'] = eFile.get('path') # print(item) # events.append(item) # query += ' group by path' # conn.execute("SELECT COUNT(*) FROM ({0}) AS TEMP".format(query.format("max(date)")), request) else: db_data = conn.getDb()['fim_event'].aggregate([ { '$lookup': { 'from': 'fim_file', 'localField': 'file_id', 'foreignField': '_id', 'as': 'fim_file' } }, ], cursor={}) for sysEvent in db_data: if sysEvent['fim_file'][0]['type'] != filetype: continue if event: if sysEvent['type'] != event: continue if filename: if sysEvent['fim_file'][0]['path'] != filename: continue if md5: if sysEvent['md5'] != md5: continue if sha1: if sysEvent['sha1'] != md5: continue if hash: if (sysEvent['sha1'] != hash) and (sysEvent['md5'] != hash): continue if search: search_value = int(search['value']) if search['value'].isdigit( ) else search['value'] if (search_value not in sysEvent['fim_file'][0]['path']) and (search_value not in sysEvent['date']) \ and (search_value not in sysEvent['size']) and (search_value not in sysEvent['md5']) \ and (search_value not in sysEvent['sha1']) and (search_value not in sysEvent['uname']) \ and (search_value not in sysEvent['gname']) and (search_value not in sysEvent['perm']): continue item = sysEvent item['fim_event.type'] = item['fim_file'][0]['type'] item['path'] = item['fim_file'][0]['path'] # print(item) item.pop('_id') item.pop('fim_file') events.append(item) data = {'totalItems': len(events)} # Sorting event_sort_con = [] file_sort_con = [] if sort: if sort['fields']: allowed_sort_fields = set(eventFields.keys() + fileFields.keys()) # Check if every element in sort['fields'] is in allowed_sort_fields if not set(sort['fields']).issubset(allowed_sort_fields): uncorrect_fields = list( map(lambda x: str(x), set(sort['fields']) - set(allowed_sort_fields))) raise OssecAPIException( 1403, 'Allowed sort fields: {0}. Fields: {1}'.format( allowed_sort_fields, uncorrect_fields)) for i in sort['fields']: # str_order = 1 if sort['order'] == 'asc' else -1 sort_order = False if sort['order'] == 'asc' else True events.sort(key=lambda e: e[i], reverse=sort_order) # if i in eventFields.keys(): # event_sort_con.append((eventFields[i], str_order)) # events.sort(key=lambda e: e[i], reverse=sort_order) # elif i in fileFields.keys(): # file_sort_con.append((fileFields[i], str_order)) else: # event_sort_con.append((eventFields["date"], 1 if sort['order'] == 'asc' else -1)) sort_order = False if sort['order'] == 'asc' else True events.sort(key=lambda e: e['date'], reverse=sort_order) else: # event_sort_con.append((eventFields["date"], -1)) events.sort(key=lambda e: e['date'], reverse=True) # if sort: # if sort['fields']: # allowed_sort_fields = fields.keys() # # Check if every element in sort['fields'] is in allowed_sort_fields # if not set(sort['fields']).issubset(allowed_sort_fields): # uncorrect_fields = list(map(lambda x: str(x), set(sort['fields']) - set(allowed_sort_fields))) # raise OssecAPIException(1403, 'Allowed sort fields: {0}. Fields: {1}'.format(allowed_sort_fields, uncorrect_fields)) # query += ' ORDER BY ' + ','.join(['{0} {1}'.format(fields[i], sort['order']) for i in sort['fields']]) # else: # query += ' ORDER BY date {0}'.format(sort['order']) # else: # query += ' ORDER BY date DESC' if limit: if limit > common.maximum_database_limit: raise OssecAPIException(1405, str(limit)) # query += ' LIMIT :offset,:limit' # request['offset'] = offset # request['limit'] = limit if offset >= 0: events = events[int(offset):(int(offset) + int(limit))] elif limit == 0: raise OssecAPIException(1406) # if summary: # select = ["max(date)", "mtime", "fim_event.type", "path"] # else: # select = ["date", "mtime", "fim_event.type", "path", "size", "perm", "uid", "gid", "md5", "sha1"] data['items'] = [] for fEvent in events: data_tuple = {} if fEvent.get('date') != None: data_tuple['scanDate'] = (fEvent.get('date') + timedelta(seconds=timeoffset)).__str__() else: data_tuple['scanDate'] = fEvent.get('date').__str__() # if fEvent.get('mtime') != None: # data_tuple['modificationDate'] = (fEvent.get('mtime') + timedelta(seconds=timeoffset)).__str__() # modificationDate # else: # data_tuple['modificationDate'] = data_tuple['scanDate'] # scanDate if fEvent.get('fim_event.type') != None: data_tuple['event'] = fEvent.get('fim_event.type') if fEvent.get('path') != None: data_tuple['file'] = fEvent.get('path') if not summary: try: permissions = filemode(int(fEvent.get('perm'), 8)) except TypeError: permissions = None if fEvent.get('size') != None: data_tuple['size'] = fEvent.get('size') if fEvent.get('md5') != None: data_tuple['md5'] = fEvent.get('md5') if fEvent.get('sha1') != None: data_tuple['sha1'] = fEvent.get('sha1') if not windows_agent: if fEvent.get('uid') != None: data_tuple['uid'] = fEvent.get('uid') if fEvent.get('gid') != None: data_tuple['gid'] = fEvent.get('gid') if fEvent.get('perm') != None: data_tuple['octalMode'] = fEvent.get('perm') if permissions: data_tuple['permissions'] = permissions data['items'].append(data_tuple) return data
def get_decoders_files(status=None, path=None, file=None, offset=0, limit=common.database_limit, sort=None, search=None): """ Gets a list of the available decoder files. :param status: Filters by status: enabled, disabled, all. :param path: Filters by path. :param file: Filters by filename. :param offset: First item to return. :param limit: Maximum number of items to return. :param sort: Sorts the items. Format: {"fields":["field1","field2"],"order":"asc|desc"}. :param search: Looks for items with the specified string. :return: Dictionary: {'items': array of items, 'totalItems': Number of items (without applying the limit)} """ status = Decoder.__check_status(status) ruleset_conf = configuration.get_ossec_conf(section='rules') if not ruleset_conf: raise OssecAPIException(1500) tmp_data = [] tags = ['decoder'] exclude_filenames = [] for tag in tags: if tag in ruleset_conf: item_status = Decoder.S_ENABLED if type(ruleset_conf[tag]) is list: items = ruleset_conf[tag] else: items = [ruleset_conf[tag]] for item in items: if '/' in item: item_split = item.split('/') item_name = item_split[-1] item_dir = "{0}/{1}".format(common.ossec_path, "/".join(item_split[:-1])) else: item_name = item item_dir = "{0}/{1}".format(common.ruleset_rules_path, item) tmp_data.append({ 'file': item_name, 'path': item_dir, 'status': item_status }) tag = 'decoder_dir' if tag in ruleset_conf: if type(ruleset_conf[tag]) is list: items = ruleset_conf[tag] else: items = [ruleset_conf[tag]] for item_dir in items: all_decoders = "{0}/{1}/*.xml".format(common.ossec_path, item_dir) for item in glob(all_decoders): item_split = item.split('/') item_name = item_split[-1] item_dir = "/".join(item_split[:-1]) if item_name in exclude_filenames: item_status = Decoder.S_DISABLED else: item_status = Decoder.S_ENABLED tmp_data.append({ 'file': item_name, 'path': item_dir, 'status': item_status }) data = list(tmp_data) for d in tmp_data: if status and status != 'all' and status != d['status']: data.remove(d) continue if path and path != d['path']: data.remove(d) continue if file and file != d['file']: data.remove(d) continue if search: data = search_array(data, search['value'], search['negation']) if sort: data = sort_array(data, sort['fields'], sort['order']) else: data = sort_array(data, ['file'], 'asc') return { 'items': cut_array(data, offset, limit), 'totalItems': len(data) }
def get_pci(agent_id=None, offset=0, limit=common.database_limit, sort=None, search=None): """ Get all the PCI requirements used in the rootchecks of the agent. :param agent_id: Agent ID. :param offset: First item to return. :param limit: Maximum number of items to return. :param sort: Sorts the items. Format: {"fields":["field1","field2"],"order":"asc|desc"}. :param search: Looks for items with the specified string. :return: Dictionary: {'items': array of items, 'totalItems': Number of items (without applying the limit)} """ fields = {} request = {"$and": [{'pci_dss': {'$ne': None}}]} # Connection db_url = common.database_path conn = Connection(db_url) conn.connect(conn.getDbById(str(agent_id).zfill(3))) if (conn.getDb() == None): raise OssecAPIException(1600) # Search if search: regex = re.compile(".*{0}.*".format(int(search['value']) if search['value'].isdigit() \ else search['value']), re.IGNORECASE) search_con = {"$or": []} search_con["$or"].append({'pci_dss': regex}) if bool(search['negation']): if search_con["$or"]: request["$and"].append({"$not": search_con}) else: if search_con["$or"]: request["$and"].append(search_con) # Total items # conn.execute(query.format('COUNT(DISTINCT pci_dss)'), request) # data = {'totalItems': conn.fetch()[0]} # Sorting sort_con = [] if sort: if sort['fields']: allowed_sort_fields = set(fields.keys()) # Check if every element in sort['fields'] is in allowed_sort_fields if not set(sort['fields']).issubset(allowed_sort_fields): uncorrect_fields = list( map(lambda x: str(x), set(sort['fields']) - set(allowed_sort_fields))) raise OssecAPIException( 1403, 'Allowed sort fields: {0}. Fields: {1}'.format( allowed_sort_fields, uncorrect_fields)) for i in sort['fields']: str_order = 1 if sort['order'] == 'asc' else -1 sort_con.append((fields[i], str_order)) else: sort_con.append(('pci_dss', 1 if sort['order'] == 'asc' else -1)) else: sort_con.append(('pci_dss', 1)) if limit: if limit > common.maximum_database_limit: raise OssecAPIException(1405, str(limit)) elif limit == 0: raise OssecAPIException(1406) if not request["$and"]: request = {} db_data = conn.getDb()['pm_event'].find(request).sort(sort_con).skip( offset).limit(limit).distinct('pci_dss') data = {} data['items'] = [] for pmEvent in db_data: data['items'].append(pmEvent) return data
def send_msg_to_agent(self, msg, agent_id=None, msg_type=None): # Active-response # Agents: /var/ossec/queue/alerts/ar # - Existing command: # - (msg_to_agent) [] NNS 001 restart-ossec0 arg1 arg2 arg3 # - (msg_to_agent) [] ANN (null) restart-ossec0 arg1 arg2 arg3 # - Custom command: # - (msg_to_agent) [] NNS 001 !test.sh arg1 arg2 arg3 # - (msg_to_agent) [] ANN (null) !test.sh arg1 arg2 arg3 # Manager: /var/ossec/queue/alerts/execq # - Existing command: # - restart-ossec0 arg1 arg2 arg3 # - Custom command: # - !test.sh Hello World # Build message ALL_AGENTS_C = 'A' NONE_C = 'N' SPECIFIC_AGENT_C = 'S' NO_AR_C = '!' if agent_id: str_all_agents = NONE_C str_agent = SPECIFIC_AGENT_C str_agent_id = agent_id else: str_all_agents = ALL_AGENTS_C str_agent = NONE_C str_agent_id = "(null)" # AR if msg_type == OssecQueue.AR_TYPE: if not agent_id: raise OssecAPIException(1653) if agent_id != "000": # Example restart 'msg': restart-ossec0 - null (from_the_server) (no_rule_id) socket_msg = "{0} {1}{2}{3} {4} {5}".format( "(msg_to_agent) []", str_all_agents, NONE_C, str_agent, str_agent_id, msg) elif agent_id == "000": socket_msg = msg # Send message try: self._send(socket_msg.encode()) except: raise OssecAPIException(1652) return "Command sent." # Legacy: Restart syscheck, restart agents else: if msg == OssecQueue.HC_SK_RESTART: socket_msg = "{0} {1}{2}{3} {4} {5}".format( "(msg_to_agent) []", str_all_agents, NO_AR_C, str_agent, str_agent_id, OssecQueue.HC_SK_RESTART) elif msg == OssecQueue.RESTART_AGENTS: socket_msg = "{0} {1}{2}{3} {4} {5} - {6} (from_the_server) (no_rule_id)".format( "(msg_to_agent) []", str_all_agents, NONE_C, str_agent, str_agent_id, OssecQueue.RESTART_AGENTS, "null") else: raise OssecAPIException(1012, msg) # Send message try: self._send(socket_msg.encode()) except: if msg == OssecQueue.HC_SK_RESTART: if agent_id: raise OssecAPIException(1601, "on agent") else: raise OssecAPIException(1601, "on all agents") elif msg == OssecQueue.RESTART_AGENTS: raise OssecAPIException(1702) # Return message if msg == OssecQueue.HC_SK_RESTART: return "Restarting Syscheck/Rootcheck on agent" if agent_id else "Restarting Syscheck/Rootcheck on all agents" elif msg == OssecQueue.RESTART_AGENTS: return "Restarting agent" if agent_id else "Restarting all agents"