Exemple #1
0
def test_skip_sys(get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start):
    """Check if syscheckd skips /sys when setting 'skip_sys="yes"'."""
    check_apply_test({'skip_sys'}, get_configuration['tags'])
    trigger = get_configuration['metadata']['skip'] == 'no'

    if trigger:
        # If /sys/module/isofs does not exist, use 'modprobe isofs'
        assert os.path.exists('/sys/module/isofs'), f'/sys/module/isofs does not exist'

        # Do not expect any 'Sending event'
        with pytest.raises(TimeoutError):
            event = wazuh_log_monitor.start(timeout=5, callback=callback_detect_event)
            raise AttributeError(f'Unexpected event {event}')

        # Remove module isofs and travel to future to check alerts
        subprocess.Popen(["modprobe", "-r", "isofs"])
        TimeMachine.travel_to_future(timedelta(hours=13))

        # Detect at least one 'delete' event in /sys/module/isofs path
        event = wazuh_log_monitor.start(timeout=5, callback=callback_detect_event,
                                        error_message='Did not receive expected '
                                                      '"Sending FIM event: ..." event').result()
        assert event['data'].get('type') == 'deleted' and '/sys/module/isofs' in event['data'].get('path'), \
            f'Sys event not detected'

        # Restore module isofs
        subprocess.Popen(["modprobe", "isofs"])
    else:
        with pytest.raises(TimeoutError):
            event = wazuh_log_monitor.start(timeout=3, callback=callback_detect_integrity_state)
            raise AttributeError(f'Unexpected event {event}')
Exemple #2
0
def test_sync_interval(get_configuration, configure_environment,
                       restart_syscheckd, wait_for_initial_scan):
    """
    Verify that synchronization checks take place at the expected time given SYNC_INTERVAL variable.
    """
    # Check if the test should be skipped
    check_apply_test({'sync_interval'}, get_configuration['tags'])
    interval = time_to_timedelta(get_configuration['metadata']['interval'])

    wazuh_log_monitor.start(
        timeout=global_parameters.default_timeout,
        callback=callback_detect_synchronization,
        error_message='Did not receive expected '
        '"Initializing FIM Integrity Synchronization check" event')

    TimeMachine.travel_to_future(interval)
    wazuh_log_monitor.start(
        timeout=global_parameters.default_timeout,
        callback=callback_detect_synchronization,
        error_message='Did not receive expected '
        '"Initializing FIM Integrity Synchronization check" event')

    # This should fail as we are only advancing half the time needed for synchronization to occur
    TimeMachine.travel_to_future(interval / 2)
    try:
        result = wazuh_log_monitor.start(
            timeout=1 if interval.total_seconds() == 10.0 else 3,
            callback=callback_detect_synchronization,
            accum_results=1,
            error_message='Did not receive expected "Initializing FIM Integrity '
            'Synchronization check" event').result()
        if result is not None:
            pytest.fail("Synchronization shouldn't happen at this point")
    except TimeoutError:
        return
Exemple #3
0
def test_day_wday(tags_to_apply, get_configuration, configure_environment,
                  restart_wazuh, wait_for_gcp_start):
    """
    These tests verify the module starts to pull according to the day of the week 
    or month and time.
    """
    check_apply_test(tags_to_apply, get_configuration['tags'])

    with pytest.raises(TimeoutError):
        event = wazuh_log_monitor.start(
            timeout=3, callback=callback_detect_start_fetching_logs).result()
        raise AttributeError(f'Unexpected event {event}')

    wazuh_log_monitor.start(timeout=global_parameters.default_timeout + 120,
                            callback=callback_detect_start_fetching_logs,
                            accum_results=1,
                            error_message='Did not receive expected '
                            '"Starting fetching of logs" event').result()
    next_scan_time_log = wazuh_log_monitor.start(
        timeout=global_parameters.default_timeout + 60,
        callback=callback_detect_start_gcp_sleep,
        accum_results=1,
        error_message='Did not receive expected '
        '"Sleeping until ..." event').result()
    next_scan_time_spl = next_scan_time_log.split(" ")
    date = next_scan_time_spl[0].split("/")
    hour = next_scan_time_spl[1].split(":")

    test_now = datetime.datetime.now()
    next_scan_time = datetime.datetime(int(date[0]), int(date[1]),
                                       int(date[2]), int(hour[0]),
                                       int(hour[1]), int(hour[2]))
    diff_time = (next_scan_time - test_now).total_seconds()
    seconds = (int(diff_time - 20))

    TimeMachine.travel_to_future(timedelta(seconds=seconds))

    test_today = datetime.date.today()
    if tags_to_apply == {'ossec_day_conf'}:
        if today.month < 12:
            assert test_today.month == today.month + 1
        else:
            assert test_today.month == 1
    if tags_to_apply == {'ossec_wday_conf'}:
        assert weekDays[test_today.weekday()] == wday
        assert test_today.day == (today + datetime.timedelta(weeks=1)).day
    if tags_to_apply == {'ossec_time_conf'}:
        assert test_today.day == (today + datetime.timedelta(days=1)).day

    wazuh_log_monitor.start(timeout=global_parameters.default_timeout + 60,
                            callback=callback_detect_start_fetching_logs,
                            accum_results=1,
                            error_message='Did not receive expected '
                            '"Starting fetching of logs" event').result()
    TimeMachine.travel_to_future(timedelta(seconds=seconds + 20 - 172800),
                                 back_in_time=True)
def test_command_execution_freq(get_local_internal_options,
                                configure_local_internal_options,
                                get_configuration, configure_environment,
                                restart_logcollector):
    """Check if the Wazuh run correctly with the specified command monitoring option "frequency".

    For this purpose, it is verified that the command has not been executed
    before the period established in this option.

    Args:
        get_local_internal_options (fixture): Get internal configuration.
        configure_local_internal_options (fixture): Set internal configuration.
        get_configuration (fixture): Get configurations from the module.
        configure_environment (fixture): Configure a custom environment for testing.
        restart_logcollector (fixture): Reset log file and start a new monitor.

    Raises:
        TimeoutError: If the command monitoring callback is not generated.
    """
    config = get_configuration['metadata']
    log_callback = logcollector.callback_running_command(
        log_format=config['log_format'],
        command=config['command'],
        prefix=LOG_COLLECTOR_DETECTOR_PREFIX)
    seconds_to_travel = config[
        'frequency'] / 2  # Middle of the command execution cycle.

    wazuh_log_monitor.start(
        timeout=global_parameters.default_timeout,
        callback=log_callback,
        error_message=logcollector.GENERIC_CALLBACK_ERROR_COMMAND_MONITORING)

    before = str(datetime.now())
    TimeMachine.travel_to_future(timedelta(seconds=seconds_to_travel))
    logger.debug(
        f"Changing the system clock from {before} to {datetime.now()}")

    # The command should not be executed in the middle of the command execution cycle.
    with pytest.raises(TimeoutError):
        wazuh_log_monitor.start(timeout=global_parameters.default_timeout,
                                callback=log_callback,
                                error_message=logcollector.
                                GENERIC_CALLBACK_ERROR_COMMAND_MONITORING)

    before = str(datetime.now())
    TimeMachine.travel_to_future(timedelta(seconds=seconds_to_travel))
    logger.debug(
        f"Changing the system clock from {before} to {datetime.now()}")

    wazuh_log_monitor.start(
        timeout=global_parameters.default_timeout,
        callback=log_callback,
        error_message=logcollector.GENERIC_CALLBACK_ERROR_COMMAND_MONITORING)

    # Restore the system clock.
    TimeMachine.time_rollback()
def test_events_while_integrity_scan(tags_to_apply, get_configuration,
                                     configure_environment, restart_syscheckd):
    """Check that events are being generated while a synchronization is being performed simultaneously.
    """
    check_apply_test(tags_to_apply, get_configuration['tags'])

    folder = testdir1 if get_configuration['metadata'][
        'fim_mode'] == 'realtime' else testdir2
    key_h = create_registry(registry_parser[key], subkey, KEY_WOW64_64KEY)

    # Wait for whodata to start and the synchronization check. Since they are different threads, we cannot expect
    # them to come in order every time
    if get_configuration['metadata']['fim_mode'] == 'whodata':
        value_1 = wazuh_log_monitor.start(
            timeout=global_parameters.default_timeout * 2,
            callback=callback_integrity_or_whodata,
            error_message='Did not receive expected "File integrity monitoring '
            'real-time Whodata engine started" or "Initializing '
            'FIM Integrity Synchronization check"').result()

        value_2 = wazuh_log_monitor.start(
            timeout=global_parameters.default_timeout * 2,
            callback=callback_integrity_or_whodata,
            error_message='Did not receive expected "File integrity monitoring '
            'real-time Whodata engine started" or "Initializing FIM '
            'Integrity Synchronization check"').result()
        assert value_1 != value_2, "callback_integrity_or_whodata detected the same message twice"

    else:
        # Check the integrity scan has begun
        wazuh_log_monitor.start(
            timeout=global_parameters.default_timeout * 3,
            callback=callback_integrity_synchronization_check,
            error_message='Did not receive expected '
            '"Initializing FIM Integrity Synchronization check" event')

    # Create a file and a registry value. Assert syscheckd detects it while doing the integrity scan
    file_name = 'file'
    create_file(REGULAR, folder, file_name, content='')
    modify_registry_value(key_h, "test_value", REG_SZ, 'added')

    sending_event = wazuh_log_monitor.start(
        timeout=global_parameters.default_timeout,
        callback=callback_detect_event,
        error_message='Did not receive expected '
        '"Sending FIM event: ..." event').result()
    assert sending_event['data']['path'] == os.path.join(folder, file_name)

    TimeMachine.travel_to_future(timedelta(hours=13))
    sending_event = wazuh_log_monitor.start(
        timeout=global_parameters.default_timeout,
        callback=callback_detect_event,
        error_message='Did not receive expected '
        '"Sending FIM event: ..." event').result()
    assert sending_event['data']['path'] == os.path.join(key, subkey)
    assert sending_event['data']['arch'] == '[x64]'
def test_configuration_age_datetime(new_datetime, get_files_list,
                                    get_configuration,
                                    create_file_structure_function,
                                    configure_environment):
    """Check if logcollector age option works correctly when date time of the system changes.

    Ensure that when date of the system change logcollector use properly age value, ignoring files that have not been
    modified for a time greater than age value using current date.

    Raises:
        TimeoutError: If the expected callbacks are not generated.
    """
    cfg = get_configuration['metadata']
    age_seconds = time_to_seconds(cfg['age'])

    control_service('stop', daemon=DAEMON_NAME)
    truncate_file(LOG_FILE_PATH)
    wazuh_log_monitor = FileMonitor(LOG_FILE_PATH)
    control_service('start', daemon=DAEMON_NAME)

    TimeMachine.travel_to_future(time_to_timedelta(new_datetime))

    for file in file_structure:
        for name in file['filename']:
            absolute_file_path = os.path.join(file['folder_path'], name)

            log_callback = logcollector.callback_match_pattern_file(
                cfg['location'], absolute_file_path)
            wazuh_log_monitor.start(timeout=5,
                                    callback=log_callback,
                                    error_message=f"{name} was not detected")

            fileinfo = os.stat(absolute_file_path)
            current_time = time.time()
            mfile_time = current_time - fileinfo.st_mtime

            if age_seconds <= int(mfile_time):
                log_callback = logcollector.callback_ignoring_file(
                    absolute_file_path)
                wazuh_log_monitor.start(
                    timeout=5,
                    callback=log_callback,
                    error_message=f"{name} was not ignored")
            else:
                with pytest.raises(TimeoutError):
                    log_callback = logcollector.callback_ignoring_file(
                        absolute_file_path)
                    wazuh_log_monitor.start(
                        timeout=5,
                        callback=log_callback,
                        error_message=f"{name} was not ignored")

        TimeMachine.time_rollback()
Exemple #7
0
def check_time_travel(time_travel: bool,
                      interval: timedelta = timedelta(hours=13),
                      monitor: FileMonitor = None):
    """
    Change date and time of the system depending on a boolean condition. Optionally, a monitor may be used to check
    if a scheduled scan has been performed.

    This function is specially useful to deal with scheduled scans that are triggered on a time interval basis.

    Parameters
    ----------
    time_travel : boolean
        True if we need to update time. False otherwise.
    interval : timedelta, optional
        Time interval that will be added to system clock. Default: 13 hours.
    monitor : FileMonitor, optional
        If passed, after changing system clock it will check for the end of the scheduled scan. The `monitor` will not
        consume any log line. Default `None`.

    Raises
    ------
    TimeoutError
        If `monitor` is not `None` and the scan has not ended in the default timeout specified in `global_parameters`.
    """
    if time_travel:
        before = str(datetime.now())
        TimeMachine.travel_to_future(interval)
        logger.info(
            f"Changing the system clock from {before} to {str(datetime.now())}"
        )

        if monitor:
            monitor.start(
                timeout=global_parameters.default_timeout,
                callback=callback_detect_end_scan,
                update_position=False,
                error_message=f'End of scheduled scan not detected after '
                f'{global_parameters.default_timeout} seconds')
def configure_syscheck_environment(time_sleep):
    # Create every needed directory
    for n in range(n_windows_registry):
        t_dir = f'{testreg}{n}'
        create_registry(registry_parser[KEY], f'{testreg}{n}', KEY_WOW64_64KEY)
        reg_list.append(t_dir)

    control_service('restart')
    logger.debug('Waiting 15 seconds for syscheckd to start.')
    time.sleep(15)

    reg_key = 'reg_key'
    reg_value = 'value_name'

    logger.debug(
        f'Waiting {str(time_sleep)} seconds. Execute `generate_windows_yaml.py` now.'
    )
    time.sleep(time_sleep)

    logger.debug(f'Waiting {SCAN_WAIT} seconds for baseline scan to finish.')
    time.sleep(120)

    logger.debug('Creating registries...')
    for registry in reg_list:
        key_h = create_registry(registry_parser[KEY],
                                os.path.join(registry, reg_key),
                                KEY_WOW64_64KEY)
        modify_registry_value(key_h, reg_value, REG_SZ, 'added')

    TimeMachine.travel_to_future(timedelta(hours=13))

    logger.debug(f'Waiting {SCAN_WAIT} seconds for scan to finish.')
    time.sleep(SCAN_WAIT)

    logger.debug('Modifying registries...')
    for registry in reg_list:
        modify_key_perms(
            registry_parser[KEY], os.path.join(registry, reg_key),
            KEY_WOW64_64KEY,
            LookupAccountName(None, f"{platform.node()}\\{os.getlogin()}")[0])
        modify_registry_owner(
            registry_parser[KEY], os.path.join(registry, reg_key),
            KEY_WOW64_64KEY,
            LookupAccountName(None, f"{platform.node()}\\{os.getlogin()}")[0])
        key_h = RegOpenKeyEx(registry_parser[KEY],
                             os.path.join(registry, reg_key), 0,
                             KEY_ALL_ACCESS | KEY_WOW64_64KEY)
        modify_registry_value(key_h, reg_value, REG_SZ, 'modified')

    TimeMachine.travel_to_future(timedelta(hours=13))

    logger.debug(f'Waiting {SCAN_WAIT} seconds for scan to finish.')
    time.sleep(SCAN_WAIT)

    logger.debug('Deleting registries...')
    for registry in reg_list:
        delete_registry(registry_parser[KEY], os.path.join(registry, reg_key),
                        KEY_WOW64_64KEY)

    TimeMachine.travel_to_future(timedelta(hours=13))

    logger.debug(f'Waiting {SCAN_WAIT} seconds for scan to finish.')
    time.sleep(SCAN_WAIT)
Exemple #9
0
def test_response_timeout(num_files, get_configuration, configure_environment,
                          restart_syscheckd):
    """Verify that synchronization checks take place at the expected time given INTERVAL and RESPONSE_TIMEOUT
    parameters, being INTERVAL greater than RESPONSE_TIMEOUT.

    To accomplish this a connection with a Wazuh Agent (Linux based) must be established via SSH using
    Paramiko. All operations will take place on the Agent side.

    Parameters
    ----------
    num_files : int
        Number of files to create within the test
    """
    def overwrite_agent_conf_file():
        cmd = "sudo sed -i ':a;N;$!ba;s|<synchronization>.*</synchronization>|<synchronization>\
            <enabled>yes</enabled>\
                <interval>" + str(sync_interval) + "</interval>\
                    <response_timeout>" + str(
            response_timeout) + "</response_timeout>\
                        </synchronization>|g' /var/ossec/etc/ossec.conf"

        ssh.exec_command(cmd)

    def wait_agent_initial_scan(time_out=60):
        truncate_agent_log()
        start_time = datetime.now()
        while datetime.now() < start_time + timedelta(seconds=time_out):
            ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(
                "sudo cat /var/ossec/logs/ossec.log")
            for line in ssh_stdout.read().decode('ascii').splitlines():
                if callback_detect_end_scan(line):
                    return
        pytest.fail(
            "No 'File integrity monitoring scan ended.' was found on ossec.log."
        )

    def create_files_in_agent():
        ssh.exec_command("sudo systemctl stop wazuh-agent")
        ssh.exec_command("sudo mkdir " + DIR_NAME)
        ssh.exec_command("sudo touch " + DIR_NAME + "/testfile{0.." +
                         str(num_files) + "}")

        purge_manager_db()

        ssh.exec_command("sudo systemctl start wazuh-agent")

    def purge_manager_db():
        for proc in psutil.process_iter(attrs=['name']):
            if proc.name() == "wazuh-db":
                proc.terminate()

        os.system("rm -f /var/ossec/queue/db/00{1..9}.db*")
        os.system("/var/ossec/bin/wazuh-db")
        time.sleep(5)

    def detect_synchronization_start(time_out=1):
        start_time = datetime.now()
        while datetime.now() < start_time + timedelta(seconds=time_out):
            ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(
                "sudo cat /var/ossec/logs/ossec.log")
            for line in ssh_stdout.read().decode('ascii').splitlines():
                if callback_detect_synchronization(line):
                    return extract_datetime(str(line))
        return None

    def wait_agent_dbsync_finish():
        previous_time = datetime.now()
        while datetime.now() - previous_time < timedelta(seconds=3):
            ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(
                "sudo cat /var/ossec/logs/ossec.log")
            for line in ssh_stdout.read().decode('ascii').splitlines():
                if "syscheck dbsync" in line:
                    previous_time = datetime.now()
            truncate_agent_log()
        return datetime.now()

    def wait_agent_integrity_control():
        previous_time = datetime.now()
        while datetime.now() - previous_time < timedelta(seconds=1):
            ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(
                "sudo cat /var/ossec/logs/ossec.log")
            for line in ssh_stdout.read().decode('ascii').splitlines():
                if "Sending integrity control message" in line:
                    previous_time = datetime.now()
                elif callback_detect_synchronization(line):
                    pytest.fail(
                        "No new synchronization process should start until `integrity control message` ends."
                    )
            truncate_agent_log()

    def truncate_agent_log():
        ssh.exec_command("sudo truncate -s 0 " + LOG_PATH)

    def extract_datetime(line):
        return datetime.strptime(line[0:19], '%Y/%m/%d %H:%M:%S')

    def update_agent_datetime():
        now = datetime.now()
        year = str(now.year)
        month = str(now.month)
        day = str(now.day)
        hour = str(now.hour)
        minute = str(now.minute)
        second = str(now.second)
        timedatectl_cmd = "sudo timedatectl set-time '" + year + "-" + month + "-" + day + " " + hour + ":" + minute + \
                          ":" + second + "'"
        ssh.exec_command(timedatectl_cmd)

    # Check if the test should be skipped
    check_apply_test({'response_timeout'}, get_configuration['tags'])

    # Variables
    LOG_PATH = "/var/ossec/logs/ossec.log"
    DIR_NAME = "/testdir1"
    AGENT_IP = "172.19.0.201"
    USERNAME = "******"
    PASSWORD = "******"
    response_timeout = get_configuration['metadata']['response_timeout']
    sync_interval = get_configuration['metadata']['interval']

    # Connect to the agent
    ssh = paramiko.SSHClient()
    ssh.load_system_host_keys()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname=AGENT_IP, username=USERNAME, password=PASSWORD)

    # Setup agent
    overwrite_agent_conf_file()
    update_agent_datetime()
    create_files_in_agent()
    wait_agent_initial_scan()

    # Check if first synchronization has started
    time_first_synchronization = detect_synchronization_start()
    if time_first_synchronization is None:
        pytest.fail("No synchronization was detected.")

    # Wait until synchronization ends
    time_after_dbsync = wait_agent_dbsync_finish()
    wait_agent_integrity_control()
    truncate_agent_log()

    # Determines when the next synchronization should occur
    max_next_synchronization = max(
        time_first_synchronization + time_to_timedelta(sync_interval),
        time_after_dbsync + time_to_timedelta(response_timeout))

    # Calculate a datetime when the next synchronization should NOT happen
    min_next_synchronization = min(
        time_first_synchronization + time_to_timedelta(sync_interval),
        time_after_dbsync + time_to_timedelta(response_timeout))

    # Travels in time to a datetime when synchronization should NOT happen, if needed
    if min_next_synchronization > datetime.now(
    ) and sync_interval != response_timeout:
        TimeMachine.travel_to_future(min_next_synchronization - datetime.now())
        update_agent_datetime()

    # Ensure there is no synchronization at this time.
    if detect_synchronization_start() is not None:
        pytest.fail("No synchronization should happen at this time.")

    # Travels in time to a datetime when synchronization MUST ocurr
    TimeMachine.travel_to_future(max_next_synchronization - datetime.now())
    update_agent_datetime()

    # Wait until next synchronization is detected
    if detect_synchronization_start(time_out=10) is None:
        pytest.fail("No synchronization was detected.")
def test_reconnect_time(get_local_internal_options,
                        configure_local_internal_options, get_configuration,
                        configure_environment, restart_logcollector):
    """Check if reconnect_time value works properly

    Ensure correspond debug logs are generated when Windows event log service stop. Also, when event log service is
    restarted, `wazuh-agent` should reconnect to it using reconnect_time value.
    """

    config = get_configuration['metadata']

    if time_to_seconds(
            config['reconnect_time']) >= timeout_callback_reconnect_time:
        pytest.xfail(
            "Expected fail: https://github.com/wazuh/wazuh/issues/8580")

    log_callback = logcollector.callback_eventchannel_analyzing(
        config['location'])
    wazuh_log_monitor.start(timeout=global_parameters.default_timeout,
                            callback=log_callback,
                            error_message=logcollector.
                            GENERIC_CALLBACK_ERROR_ANALYZING_EVENTCHANNEL)

    services.control_event_log_service('stop')

    log_callback = logcollector.callback_event_log_service_down(
        config['location'])
    wazuh_log_monitor.start(timeout=30,
                            callback=log_callback,
                            error_message=logcollector.
                            GENERIC_CALLBACK_ERROR_ANALYZING_EVENTCHANNEL)

    log_callback = logcollector.callback_trying_to_reconnect(
        config['location'], time_to_seconds(config['reconnect_time']))
    wazuh_log_monitor.start(timeout=30,
                            callback=log_callback,
                            error_message=logcollector.
                            GENERIC_CALLBACK_ERROR_ANALYZING_EVENTCHANNEL)

    services.control_event_log_service('start')

    time.sleep(1)

    if time_to_seconds(
            config['reconnect_time']) >= timeout_callback_reconnect_time:
        before = str(datetime.now())
        seconds_to_travel = time_to_seconds(config['reconnect_time']) / 2
        TimeMachine.travel_to_future(timedelta(seconds=seconds_to_travel))
        logger.debug(
            f"Changing the system clock from {before} to {datetime.now()}")

    log_callback = logcollector.callback_reconnect_eventchannel(
        config['location'])

    before = str(datetime.now())

    if time_to_seconds(
            config['reconnect_time']) >= timeout_callback_reconnect_time:
        TimeMachine.travel_to_future(timedelta(seconds=(seconds_to_travel)))
        logger.debug(
            f"Changing the system clock from {before} to {datetime.now()}")

    wazuh_log_monitor.start(
        timeout=30,
        callback=log_callback,
        error_message=logcollector.GENERIC_CALLBACK_ERROR_COMMAND_MONITORING)

    TimeMachine.time_rollback()