예제 #1
0
 def __init__(self, server_address='127.0.0.1', enrollment_port=1515, key_path='/etc/manager.key',
              cert_path='/etc/manager.cert', initial_mode='ACCEPT'):
     self.mitm_enrollment = ManInTheMiddle(address=(server_address, enrollment_port), family='AF_INET',
                                           connection_protocol='SSL', func=self._process_enrollment_message)
     self.key_path = key_path
     self.cert_path = cert_path
     self.id_count = 1
     self.secret = 'TopSecret'
     self.controller = CertificateController()
     self.mode = initial_mode
예제 #2
0
def configure_mitm_environment_wazuhdb(request):
    """Use MITM to replace analysisd and wazuh-db sockets."""
    wdb_path = getattr(request.module, 'wdb_path')

    # Stop wazuh-service and ensure all daemons are stopped
    control_service('stop')
    check_daemon_status(running=False)
    remove_logs()

    control_service('start', daemon='wazuh-db', debug_mode=True)
    check_daemon_status(running=True, daemon='wazuh-db')

    mitm_wdb = ManInTheMiddle(socket_path=wdb_path)
    wdb_queue = mitm_wdb.queue
    mitm_wdb.start()

    wdb_monitor = QueueMonitor(queue_item=wdb_queue)

    setattr(request.module, 'wdb_monitor', wdb_monitor)

    yield

    mitm_wdb.shutdown()

    for daemon in ['wazuh-db']:
        control_service('stop', daemon=daemon)
        check_daemon_status(running=False, daemon=daemon)

    # Delete all db
    delete_dbs()

    control_service('start')
예제 #3
0
configurations = load_wazuh_configurations(configurations_path,
                                           __name__,
                                           params=params,
                                           metadata=metadata)

# Variables

log_monitor_paths = [CLUSTER_LOGS_PATH]
modulesd_socket_path = os.path.join(WAZUH_PATH, 'queue', 'ossec', 'krequest')
cluster_socket_address = ('localhost', 1516)

receiver_sockets_params = [(cluster_socket_address, 'AF_INET', 'TCP')
                           ]  # SocketController items

mitm_modules = ManInTheMiddle(address=modulesd_socket_path,
                              family='AF_UNIX',
                              connection_protocol='UDP')
# monitored_sockets_params is a List of daemons to start with optional ManInTheMiddle to monitor
# List items -> (wazuh_daemon: str,(
#                mitm: ManInTheMiddle
#                daemon_first: bool))
# Example1 -> ('wazuh-clusterd', None)              Only start wazuh-clusterd with no MITM
# Example2 -> ('wazuh-clusterd', (my_mitm, True))   Start MITM and then wazuh-clusterd
monitored_sockets_params = [('wazuh-clusterd', None, None),
                            ('wazuh-modulesd', mitm_modules, True)]

receiver_sockets, monitored_sockets, log_monitors = None, None, None  # Set in the fixtures

# Functions

예제 #4
0
def generate_analysisd_yaml(n_events, modify_events):
    def parse_events_into_yaml(requests, yaml_file):
        yaml_result = []
        with open(yaml_file, 'a') as y_f:
            id_ev = 0
            for req, event in requests:
                type_ev = event['data']['type']
                stage_ev = type_ev.title()
                mode = None
                agent_id = callback_analysisd_agent_id(req) or '000'

                del event['data']['mode']
                del event['data']['type']
                if 'tags' in event['data']:
                    del event['data']['tags']
                if type_ev == 'added':
                    mode = 'save2'
                    output_ev = json.dumps(event['data'])

                elif type_ev == 'deleted':
                    mode = 'delete'
                    output_ev = json.dumps(event['data']['path']).replace(
                        '"', '')

                elif type_ev == 'modified':
                    mode = 'save2'
                    for field in [
                            'old_attributes', 'changed_attributes',
                            'content_changes'
                    ]:
                        if field in event['data']:
                            del event['data'][field]
                    output_ev = json.dumps(event['data'])

                yaml_result.append({
                    'name':
                    f"{stage_ev}{id_ev}",
                    'test_case': [{
                        'input': f"{req}",
                        'output':
                        f"agent {agent_id} syscheck {mode} {output_ev}",
                        'stage': f"{stage_ev}"
                    }]
                })
                id_ev += 1
            y_f.write(yaml.safe_dump(yaml_result))

    def remove_logs():
        for root, dirs, files in os.walk(WAZUH_LOGS_PATH):
            for file in files:
                os.remove(os.path.join(root, file))

    # Restart syscheckd with the new configuration
    truncate_file(LOG_FILE_PATH)
    control_service('stop')
    check_daemon_status(running=False)

    remove_logs()

    control_service('start', daemon='ossec-analysisd', debug_mode=True)
    check_daemon_status(running=True, daemon='ossec-analysisd')

    mitm_analysisd = ManInTheMiddle(address=analysis_path,
                                    family='AF_UNIX',
                                    connection_protocol='UDP')
    analysis_queue = mitm_analysisd.queue
    mitm_analysisd.start()

    control_service('start', daemon='ossec-remoted', debug_mode=True)
    check_daemon_status(running=True, daemon='ossec-remoted')

    analysis_monitor = QueueMonitor(analysis_queue)

    while True:
        try:
            grep = subprocess.Popen(['grep', 'deleted', alerts_json],
                                    stdout=subprocess.PIPE)
            wc = int(
                subprocess.check_output([
                    'wc',
                    '-l',
                ], stdin=grep.stdout).decode())
        except subprocess.CalledProcessError:
            wc = 0
        if wc >= n_events:
            logging.debug('All alerts received. Collecting by alert type...')
            break
        logger.debug(f'{wc} deleted events so far.')
        logger.debug('Waiting for alerts. Sleeping 5 seconds.')
        time.sleep(5)

    added = analysis_monitor.start(timeout=max(0.01 * n_events, 10),
                                   callback=callback_analysisd_event,
                                   accum_results=n_events).result()
    logger.debug('"added" alerts collected.')

    modified = analysis_monitor.start(timeout=max(0.01 * n_events, 10),
                                      callback=callback_analysisd_event,
                                      accum_results=modify_events).result()
    logger.debug('"modified" alerts collected.')

    deleted = analysis_monitor.start(timeout=max(0.01 * n_events, 10),
                                     callback=callback_analysisd_event,
                                     accum_results=n_events).result()
    logger.debug('"deleted" alerts collected.')

    # Truncate file
    with open(yaml_file, 'w') as y_f:
        y_f.write(f'---\n')

    for ev_list in [added, modified, deleted]:
        parse_events_into_yaml(ev_list, yaml_file)
    logger.debug(f'YAML done: "{yaml_file}"')

    return mitm_analysisd
예제 #5
0
                              'data')
messages_path = os.path.join(test_data_path, 'integrity_messages.yaml')
with open(messages_path) as f:
    test_cases = yaml.safe_load(f)

# Variables

log_monitor_paths = [LOG_FILE_PATH]
wdb_path = os.path.join(os.path.join(WAZUH_PATH, 'queue', 'db', 'wdb'))
analysis_path = os.path.join(
    os.path.join(WAZUH_PATH, 'queue', 'sockets', 'queue'))

receiver_sockets_params = [(analysis_path, 'AF_UNIX', 'UDP')]

mitm_wdb = ManInTheMiddle(address=wdb_path,
                          family='AF_UNIX',
                          connection_protocol='TCP')
mitm_analysisd = ManInTheMiddle(address=analysis_path,
                                family='AF_UNIX',
                                connection_protocol='UDP')
# monitored_sockets_params is a List of daemons to start with optional ManInTheMiddle to monitor
# List items -> (wazuh_daemon: str,(
#                mitm: ManInTheMiddle
#                daemon_first: bool))
# Example1 -> ('wazuh-clusterd', None)              Only start wazuh-clusterd with no MITM
# Example2 -> ('wazuh-clusterd', (my_mitm, True))   Start MITM and then wazuh-clusterd
monitored_sockets_params = [('wazuh-db', mitm_wdb, True),
                            ('wazuh-analysisd', mitm_analysisd, True)]

receiver_sockets, monitored_sockets, log_monitors = None, None, None  # Set in the fixtures
예제 #6
0
messages_files = os.listdir(test_data_path)
module_tests = list()
for file in messages_files:
    with open(os.path.join(test_data_path, file)) as f:
        module_tests.append((yaml.safe_load(f), file.split("_")[0]))

# Variables

log_monitor_paths = []

wdb_path = os.path.join(os.path.join(WAZUH_PATH, 'queue', 'db', 'wdb'))

receiver_sockets_params = [(wdb_path, 'AF_UNIX', 'TCP')]

mitm_wdb = ManInTheMiddle(address=wdb_path,
                          family='AF_UNIX',
                          connection_protocol='TCP')
# mitm_analysisd = ManInTheMiddle(address=analysis_path, family='AF_UNIX', connection_protocol='UDP')
# monitored_sockets_params is a List of daemons to start with optional ManInTheMiddle to monitor
# List items -> (wazuh_daemon: str,(
#                mitm: ManInTheMiddle
#                daemon_first: bool))
# Example1 -> ('wazuh-clusterd', None)              Only start wazuh-clusterd with no MITM
# Example2 -> ('wazuh-clusterd', (my_mitm, True))   Start MITM and then wazuh-clusterd
monitored_sockets_params = [('wazuh-db', mitm_wdb, True)]

receiver_sockets, monitored_sockets, log_monitors = None, None, None  # Set in the fixtures

# Tests

예제 #7
0
test_data_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data')
configurations_path = os.path.join(test_data_path, 'cluster_conf.yaml')
params = [{'FERNET_KEY': FERNET_KEY}]
metadata = [{'fernet_key': FERNET_KEY}]
configurations = load_wazuh_configurations(configurations_path, __name__, params=params, metadata=metadata)

# Variables

log_monitor_paths = [CLUSTER_LOGS_PATH]
cluster_socket_path = os.path.join(os.path.join(WAZUH_PATH, 'queue', 'cluster', 'c-internal.sock'))
cluster_socket_address = ('localhost', 1516)

receiver_sockets_params = [(cluster_socket_path, 'AF_UNIX', 'TCP')]  # SocketController items

mitm_master = ManInTheMiddle(address=cluster_socket_address, family='AF_INET', connection_protocol='TCP',
                             func=master_simulator)

# monitored_sockets_params is a list of daemons to start with optional ManInTheMiddle to monitor
# List items -> (wazuh_daemon: str,(
#                mitm: ManInTheMiddle
#                daemon_first: bool))
# Example1 -> ('wazuh-clusterd', None)              Only start wazuh-clusterd with no MITM
# Example2 -> ('wazuh-clusterd', (my_mitm, True))   Start MITM and then wazuh-clusterd
monitored_sockets_params = [('wazuh-clusterd', mitm_master, False)]

receiver_sockets, monitored_sockets, log_monitors = None, None, None  # Set in the fixtures


# Fixtures

예제 #8
0
class AuthdSimulator:
    """
    Create an SSL server socket for simulating authd connection
    """

    def __init__(self, server_address='127.0.0.1', enrollment_port=1515, key_path='/etc/manager.key',
                 cert_path='/etc/manager.cert', initial_mode='ACCEPT'):
        self.mitm_enrollment = ManInTheMiddle(address=(server_address, enrollment_port), family='AF_INET',
                                              connection_protocol='SSL', func=self._process_enrollment_message)
        self.key_path = key_path
        self.cert_path = cert_path
        self.id_count = 1
        self.secret = 'TopSecret'
        self.controller = CertificateController()
        self.mode = initial_mode

    def start(self):
        """
        Generates certificate for the SSL server and starts server sockets
        """
        self._generate_certificates()
        self.mitm_enrollment.start()
        self.mitm_enrollment.listener.set_ssl_configuration(connection_protocol=ssl.PROTOCOL_TLSv1_2,
                                                            certificate=self.cert_path, keyfile=self.key_path)

    def shutdown(self):
        """
        Shutdown sockets
        """
        self.mitm_enrollment.shutdown()

    def clear(self):
        """
        Clear sockets after each response. By default, they stop handling connections
        after one successful connection, and they need to be cleared afterwards
        """
        while not self.mitm_enrollment.queue.empty():
            self.mitm_enrollment.queue.get_nowait()
        self.mitm_enrollment.event.clear()

    @property
    def queue(self):
        return self.mitm_enrollment.queue

    @property
    def cert_controller(self):
        return self.controller

    @property
    def agent_id(self):
        return self.id_count

    @agent_id.setter
    def agent_id(self, value):
        self.id_count = value

    def set_mode(self, mode):
        """
        Sets a mode:

            ACCEPT: Accepts connection and produces enrollment
            REJECT: Waits 2 seconds and anwsers with an empty message
        """
        self.mode = mode

    def _process_enrollment_message(self, received):
        """ 
        Reads a message received at the SSL socket, and parses to emulate a authd response
        
        Expected message:
            OSSEC A:'{name}' G:'{groups}' IP:'{ip}'\n

        Key response:
            OSSEC K: {id} {name} {ip} {key:64}
        """
        if self.mode == 'REJECT':
            time.sleep(2)
            self.mitm_enrollment.event.set()
            return b'ERROR'

        agent_info = {
            'id': self.id_count,
            'name': None,
            'ip': None
        }
        if len(received) == 0:
            # Empty message
            raise
        parts = received.decode().split(' ')
        for part in parts:
            if part.startswith('A:'):
                agent_info['name'] = part.split("'")[1]
            if part.startswith('IP:'):
                agent_info['ip'] = part.split("'")[1]
        if agent_info['ip'] is None:
            agent_info['ip'] = 'any'
        if agent_info['ip'] == 'src':
            agent_info['ip'] = self.mitm_enrollment.listener.last_address[0]
        self.id_count += 1
        self.mitm_enrollment.event.set()
        return f'OSSEC K:\'{agent_info.get("id"):03d} {agent_info.get("name")} {agent_info["ip"]} {self.secret}\'\n'.encode()

    def _generate_certificates(self):
        # Generate root key and certificate
        self.controller.get_root_ca_cert().sign(self.controller.get_root_key(), self.controller.digest)
        self.controller.store_private_key(self.controller.get_root_key(), self.key_path)
        self.controller.store_ca_certificate(self.controller.get_root_ca_cert(), self.cert_path)
예제 #9
0
def generate_analysisd_yaml(n_events, modify_events):
    def parse_events_into_yaml(requests, yaml_file):
        yaml_result = []
        with open(yaml_file, 'a') as y_f:
            id_ev = 0
            for req, event in requests:
                type_ev = event['data']['type']
                stage_ev = type_ev.title()
                mode = None
                agent_id = callback_analysisd_agent_id(req) or '000'

                del event['data']['mode']
                del event['data']['type']
                if 'tags' in event['data']:
                    del event['data']['tags']
                if type_ev == 'added':
                    mode = 'save2'
                    output_ev = json.dumps(event['data'])

                elif type_ev == 'deleted':
                    mode = 'delete'
                    output_ev = json.dumps(event['data']['path']).replace(
                        '"', '')

                elif type_ev == 'modified':
                    mode = 'save2'
                    for field in [
                            'old_attributes', 'changed_attributes',
                            'content_changes'
                    ]:
                        if field in event['data']:
                            del event['data'][field]
                    output_ev = json.dumps(event['data'])

                yaml_result.append({
                    'name':
                    f"{stage_ev}{id_ev}",
                    'test_case': [{
                        'input': f"{req}",
                        'output':
                        f"agent {agent_id} syscheck {mode} {output_ev}",
                        'stage': f"{stage_ev}"
                    }]
                })
                id_ev += 1
            y_f.write(yaml.safe_dump(yaml_result))

    def remove_logs():
        for root, dirs, files in os.walk(WAZUH_LOGS_PATH):
            for file in files:
                os.remove(os.path.join(root, file))

    file = 'regular'

    # Restart syscheckd with the new configuration
    truncate_file(LOG_FILE_PATH)
    file_monitor = FileMonitor(LOG_FILE_PATH)
    control_service('stop')
    check_daemon_status(running=False)
    remove_logs()

    control_service('start', daemon='wazuh-db', debug_mode=True)
    check_daemon_status(running=True, daemon='wazuh-db')

    control_service('start', daemon='wazuh-analysisd', debug_mode=True)
    check_daemon_status(running=True, daemon='wazuh-analysisd')

    mitm_analysisd = ManInTheMiddle(address=analysis_path,
                                    family='AF_UNIX',
                                    connection_protocol='UDP')
    analysis_queue = mitm_analysisd.queue
    mitm_analysisd.start()

    control_service('start', daemon='wazuh-syscheckd', debug_mode=True)
    check_daemon_status(running=True, daemon='wazuh-syscheckd')

    # Wait for initial scan
    detect_initial_scan(file_monitor)

    analysis_monitor = QueueMonitor(analysis_queue)

    for directory in directories_list:
        create_file(REGULAR, directory, file, content='')
        time.sleep(0.01)
    added = analysis_monitor.start(
        timeout=max(0.01 * n_events, 10),
        callback=callback_analysisd_event,
        accum_results=len(directories_list)).result()
    logger.debug('"added" alerts collected.')

    for directory in directories_list:
        modify_file(directory, file, new_content='Modified')
        time.sleep(0.01)
    modified = analysis_monitor.start(timeout=max(0.01 * n_events, 10),
                                      callback=callback_analysisd_event,
                                      accum_results=modify_events).result()
    logger.debug('"modified" alerts collected.')

    for directory in directories_list:
        delete_file(directory, file)
        time.sleep(0.01)
    deleted = analysis_monitor.start(
        timeout=max(0.01 * len(directories_list), 10),
        callback=callback_analysisd_event,
        accum_results=len(directories_list)).result()
    logger.debug('"deleted" alerts collected.')

    # Truncate file
    with open(yaml_file, 'w') as y_f:
        y_f.write(f'---\n')

    for ev_list in [added, modified, deleted]:
        parse_events_into_yaml(ev_list, yaml_file)
    logger.debug(f'YAML done: "{yaml_file}"')

    return mitm_analysisd
예제 #10
0
def configure_mitm_environment_analysisd(request):
    """Use MITM to replace analysisd and wazuh-db sockets."""
    def remove_logs():
        for root, dirs, files in os.walk(WAZUH_LOGS_PATH):
            for file in files:
                os.remove(os.path.join(root, file))

    analysis_path = getattr(request.module, 'analysis_path')
    wdb_path = getattr(request.module, 'wdb_path')

    # Stop wazuh-service and ensure all daemons are stopped
    control_service('stop')
    check_daemon_status(running=False)
    remove_logs()

    control_service('start', daemon='wazuh-db', debug_mode=True)
    check_daemon_status(running=True, daemon='wazuh-db')

    mitm_wdb = ManInTheMiddle(socket_path=wdb_path)
    wdb_queue = mitm_wdb.queue
    mitm_wdb.start()

    control_service('start', daemon='ossec-analysisd', debug_mode=True)
    check_daemon_status(running=True, daemon='ossec-analysisd')

    mitm_analysisd = ManInTheMiddle(socket_path=analysis_path, mode='UDP')
    analysisd_queue = mitm_analysisd.queue
    mitm_analysisd.start()

    analysis_monitor = QueueMonitor(queue_item=analysisd_queue)
    wdb_monitor = QueueMonitor(queue_item=wdb_queue)

    setattr(request.module, 'analysis_monitor', analysis_monitor)
    setattr(request.module, 'wdb_monitor', wdb_monitor)

    yield

    mitm_analysisd.shutdown()
    mitm_wdb.shutdown()

    for daemon in ['wazuh-db', 'ossec-analysisd']:
        control_service('stop', daemon=daemon)
        check_daemon_status(running=False, daemon=daemon)

    control_service('start')