def test_report_changes_after_restart(get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check if diff directories are removed after disabling report_changes and Wazuh is restarted. """ check_apply_test({'test_delete_after_restart'}, get_configuration['tags']) value_name = 'random_value' folder_path_key1, diff_file_key_1 = calculate_registry_diff_paths( key, sub_key_1, KEY_WOW64_64KEY, value_name) folder_path_key2, diff_file_key_2 = calculate_registry_diff_paths( key, sub_key_1, KEY_WOW64_64KEY, value_name) # Open key key1_h = create_registry(registry_parser[key], sub_key_1, KEY_WOW64_64KEY) key2_h = create_registry(registry_parser[key], sub_key_2, KEY_WOW64_64KEY) # Modify the registry modify_registry_value(key1_h, value_name, REG_SZ, "some_content") modify_registry_value(key2_h, value_name, REG_SZ, "some_content") # Travel to future check_time_travel(True, monitor=wazuh_log_monitor) assert os.path.exists( diff_file_key_1), f'{diff_file_key_1} does not exists' assert os.path.exists( diff_file_key_2), f'{diff_file_key_2} does not exists' reload_new_conf('no', test_regs[0], test_regs[1]) assert not os.path.exists( folder_path_key1), f'{folder_path_key1} does exists' assert not os.path.exists( folder_path_key2), f'{folder_path_key2} does exists'
def extra_configuration_before_yield(): """Generate registry entries to fill database""" reg1_handle = create_registry(registry_parser[KEY], sub_key_1, KEY_WOW64_64KEY) reg1_handle = RegOpenKeyEx(registry_parser[KEY], sub_key_1, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY) for i in range(0, NUM_REGS): modify_registry_value(reg1_handle, f'value_{i}', REG_SZ, 'added') RegCloseKey(reg1_handle)
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_file_size_default(key, subkey, arch, value_name, tags_to_apply, get_configuration, configure_environment, restart_syscheckd_each_time): """ Check that no events are sent when the disk_quota exceeded Parameters ---------- key : str Root key (HKEY_*) subkey : str path of the registry. arch : str Architecture of the registry. value_name : str Name of the value that will be created tags_to_apply : set Run test if match with a configuration identifier, skip otherwise. size : int Size of the content to write in value """ check_apply_test(tags_to_apply, get_configuration['tags']) mode = get_configuration['metadata']['fim_mode'] file_size_values = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_diff_size_limit_value, accum_results=3, error_message='Did not receive expected ' '"Maximum file size limit to generate diff information ' 'configured to \'... KB\'..." event').result() for value in file_size_values: if value: assert value == str(DEFAULT_SIZE), 'Wrong value for file_size' else: raise AssertionError('Wrong value for file_size') key_h = create_registry(registry_parser[key], subkey, arch) modify_registry_value(key_h, "some_value", REG_SZ, "some content") check_time_travel(True, monitor=wazuh_log_monitor) events = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, accum_results=2, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() for ev in events: validate_registry_value_event(ev, mode=mode)
def test_wait_until_baseline(key, subkey, arch, value_type, content, get_configuration, configure_environment, restart_syscheckd_each_time): """ Check if events are appearing after the baseline The message 'File integrity monitoring scan ended' informs about the end of the first scan, which generates the baseline """ key_handle = create_registry(registry_parser[key], subkey, arch) modify_registry_value(key_handle, "value_name", value_type, content) wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event_before_end_scan, error_message='Did not receive expected event before end the scan')
def test_file_limit_full(tags_to_apply, get_configuration, configure_environment, restart_syscheckd): """ Check that the full database alerts are being sent. Parameters ---------- tags_to_apply : set Run test if matches with a configuration identifier, skip otherwise. """ check_apply_test(tags_to_apply, get_configuration['tags']) database_state = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_file_limit_capacity, error_message='Did not receive expected ' '"DEBUG: ...: Sending DB 100% full alert." event').result() if database_state: assert database_state == '100', 'Wrong value for full database alert.' else: pytest.fail('Did not receive the value of the database state,') reg1_handle = RegOpenKeyEx(registry_parser[KEY], sub_key_1, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY) modify_registry_value(reg1_handle, 'value_full', REG_SZ, 'added') RegCloseKey(reg1_handle) wazuh_log_monitor.start(timeout=40, callback=callback_file_limit_full_database, error_message='Did not receive expected ' '"DEBUG: ...: Couldn\'t insert \'...\' entry into DB. The DB is full, ..." event') entries = wazuh_log_monitor.start(timeout=global_parameters.default_timeout, callback=callback_registry_count_entries, error_message='Did not receive expected ' '"Fim inode entries: ..., path count: ..." event' ).result() if entries: assert entries == str(get_configuration['metadata']['file_limit']), 'Wrong number of entries count.' else: pytest.fail('Wrong number of entries count')
def extra_configuration_before_yield(): key_h = create_registry(registry_parser[key], sub_key_1, arch) modify_registry_value(key_h, "value1", REG_SZ, "some content") modify_registry_value(key_h, "value2", REG_MULTI_SZ, "some content\0second string\0") modify_registry_value(key_h, "value3", REG_DWORD, 1234)
def test_registry_sync_after_restart(key_name, value_name, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Test to check that FIM synchronizes the registry DB when a modification is performed while the agent is down. Params: key_name (str): Name of the subkey that will be created in the test. value_name (str): Name of the value that will be created in the test. If tags_to_apply (set): Run test if matches with a configuration identifier, skip otherwise. get_configuration (fixture): Gets the current configuration of the test. configure_environment (fixture): Configure the environment for the execution of the test. restart_syscheckd (fixture): Restarts syscheck. wait_for_fim_start (fixture): Waits until the first FIM scan is completed. Raises: TimeoutError: If an expected event couldn't be captured. ValueError: If a path or value are not in the sync event. """ check_apply_test(tags_to_apply, get_configuration['tags']) key_path = os.path.join(subkey, key_name) value_path = os.path.join(key, key_path, value_name) # wait until the sync is done. get_sync_msgs(sync_interval) # stops syscheckd control_service('stop') key_handle = fim.create_registry(fim.registry_parser[key], key_path, fim.KEY_WOW64_64KEY) fim.modify_registry_value(key_handle, value_name, fim.REG_SZ, 'This is a test with syscheckd down.') control_service('start') events = get_sync_msgs(sync_interval + 15) assert find_value_in_event_list( os.path.join(key, key_path), value_name, events) is not None, f"No sync event was found for {value_path}"
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)
def test_file_limit_capacity_alert(percentage, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Checks that the corresponding alerts appear in schedule mode for different capacity thresholds. Parameters ---------- percentage : int Percentage of full database. tags_to_apply : set Run test if matches with a configuration identifier, skip otherwise. """ # This import must be here in order to avoid problems in Linux. import pywintypes check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' limit = int(get_configuration['metadata']['file_limit']) NUM_REGS = int(limit * (percentage / 100)) + 1 if percentage == 0: NUM_REGS = 0 reg1_handle = RegOpenKeyEx(registry_parser[KEY], sub_key_1, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY) if percentage >= 80: # Percentages 80 and 90 for i in range(NUM_REGS): modify_registry_value(reg1_handle, f'value_{i}', REG_SZ, 'added') else: # Database back to normal for i in range(limit - 10): modify_registry_value(reg1_handle, f'value_{i}', REG_SZ, 'added') check_time_travel(scheduled, monitor=wazuh_log_monitor) wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_end_scan, error_message='Did not receive expected ' '"Fim inode entries: ..., path count: ..." event') for i in range(limit): try: delete_registry_value(reg1_handle, f'value_{i}') except OSError: break # Break out of the loop when all values have been deleted except pywintypes.error: break RegCloseKey(reg1_handle) check_time_travel(scheduled, monitor=wazuh_log_monitor) if percentage >= 80: # Percentages 80 and 90 file_limit_capacity = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_file_limit_capacity, error_message='Did not receive expected ' '"DEBUG: ...: Sending DB ...% full alert." event').result() if file_limit_capacity: assert file_limit_capacity == str( percentage), 'Wrong capacity log for DB file_limit' else: pytest.fail('Wrong capacity log for DB file_limit') else: # Database back to normal event_found = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_file_limit_back_to_normal, error_message='Did not receive expected ' '"DEBUG: ...: Sending DB back to normal alert." event').result() assert event_found, 'Event "Sending DB back to normal alert." not found' entries = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_registry_count_entries, error_message='Did not receive expected ' '"Fim inode entries: ..., path count: ..." event').result() if entries: # We add 1 because of the key created to hold the values assert entries == str(NUM_REGS + 1), 'Wrong number of entries count.' else: pytest.fail('Wrong number of entries count')
def test_restrict_value(key, subkey, arch, value_name, triggers_event, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check the only registry values detected are those matching the restrict regex Parameters ---------- key : str Root key (HKEY_*) subkey : str path of the registry. arch : str Architecture of the registry. value_name : str Name of the value that will be created triggers_event : bool True if an event must be generated, False otherwise. tags_to_apply : set Run test if match with a configuration identifier, skip otherwise. """ check_apply_test(tags_to_apply, get_configuration['tags']) # This shouldn't create an alert because the key is already created key_h = create_registry(registry_parser[key], subkey, arch) # Create values modify_registry_value(key_h, value_name, REG_SZ, "added") # Go ahead in time to let syscheck perform a new scan check_time_travel(get_configuration['metadata']['fim_mode'] == 'scheduled', monitor=wazuh_log_monitor) event = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, accum_results=2 if triggers_event else 1).result() if triggers_event: assert event[0]['data']['type'] == 'modified', 'Key event not modified' assert event[0]['data']['path'] == os.path.join( key, subkey), 'Key event wrong path' assert event[0]['data'][ 'arch'] == '[x32]' if arch == KEY_WOW64_32KEY else '[x64]', 'Key event arch not equal' assert event[1]['data']['type'] == 'added', 'Event type not equal' assert event[1]['data']['path'] == os.path.join( key, subkey), 'Event path not equal' assert event[1]['data'][ 'value_name'] == value_name, 'Value name not equal' assert event[1]['data'][ 'arch'] == '[x32]' if arch == KEY_WOW64_32KEY else '[x64]', 'Value event arch not equal' else: assert event['data']['type'] == 'modified', 'Key event not modified' assert event['data']['path'] == os.path.join( key, subkey), 'Key event wrong path' assert event['data'][ 'arch'] == '[x32]' if arch == KEY_WOW64_32KEY else '[x64]', 'Key event arch not equal' while True: ignored_value = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_restricted, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() if ignored_value == value_name: break delete_registry_value(key_h, value_name) # Go ahead in time to let syscheck perform a new scan check_time_travel(get_configuration['metadata']['fim_mode'] == 'scheduled', monitor=wazuh_log_monitor) event = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, accum_results=2 if triggers_event else 1).result() if triggers_event: assert event[0]['data']['type'] == 'modified', 'Key event not modified' assert event[0]['data']['path'] == os.path.join( key, subkey), 'Key event wrong path' assert event[0]['data'][ 'arch'] == '[x32]' if arch == KEY_WOW64_32KEY else '[x64]', 'Key event arch not equal' assert event[1]['data']['type'] == 'deleted', 'Event type not equal' assert event[1]['data']['path'] == os.path.join( key, subkey), 'Event path not equal' assert event[1]['data'][ 'value_name'] == value_name, 'Value name not equal' assert event[1]['data'][ 'arch'] == '[x32]' if arch == KEY_WOW64_32KEY else '[x64]', 'Value event arch not equal' else: # After deleting the value, we don't expect any message of the value because it's not in the DB assert event['data']['type'] == 'modified', 'Key event not modified' assert event['data']['path'] == os.path.join( key, subkey), 'Key event wrong path' assert event['data'][ 'arch'] == '[x32]' if arch == KEY_WOW64_32KEY else '[x64]', 'Key event arch not equal'
def test_delete_registry(key, subkey, arch, value_list, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check if syscheckd detects 'deleted' events from the values contained in a registry key that is being deleted. Parameters ---------- key : str Root key where the sub key will be created (HKEY_LOCAL_MACHINE, etc) subkey : str Path of the registry subkey arch : int Arch of the registry subkey value_list : list Names of the values. """ mode = get_configuration['metadata']['fim_mode'] scheduled = mode == 'scheduled' key_h = create_registry(registry_parser[key], subkey, arch) # Create values inside subkey modify_registry_value(key_h, value_list[0], REG_SZ, "some content") modify_registry_value(key_h, value_list[1], REG_MULTI_SZ, "some content\0second string\0") modify_registry_value(key_h, value_list[2], REG_DWORD, 1234) check_time_travel(scheduled, monitor=wazuh_log_monitor) events = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, accum_results=len(value_list) + 1, error_message='Did not receive expected ' '"Sending FIM event: ..." event').result() for ev in events: validate_registry_value_event(ev, mode=mode) # Remove registry delete_registry(registry_parser[key], subkey, arch) check_time_travel(scheduled, monitor=wazuh_log_monitor) # Expect deleted events event_list = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, error_message='Did not receive expected ' '"Sending FIM event: ..." event', accum_results=len(value_list) + 1).result() counter_type = Counter([event['data']['type'] for event in event_list]) for ev in events: validate_registry_value_event(ev, mode=mode) assert counter_type['deleted'] == len( value_list ) + 1, f'Number of "deleted" events should be {len(value_list) + 1}' name_list = set([event['data']['value_name'] for event in event_list[1:]]) for value in value_list: assert value in name_list, f'Value {value} not found within the events'
def test_registry_responses(key_name, value_name, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """Test to check that the fields in synchronization events are decoded properly by the agent. Params: key_name (str): Name of the subkey that will be created in the test. value_name (str): Name of the value that will be created in the test. If tags_to_apply (set): Run test if matches with a configuration identifier, skip otherwise. get_configuration (fixture): Gets the current configuration of the test. configure_environment (fixture): Configure the environment for the execution of the test. restart_syscheckd (fixture): Restarts syscheck. wait_for_fim_start (fixture): Waits until the first FIM scan is completed. Raises: TimeoutError: If an expected event couldn't be captured. ValueError: If a path or value are not in the sync event. """ if key_name is None and value_name is None: pytest.skip('key_name and value_name are None. Skipping') check_apply_test(tags_to_apply, get_configuration['tags']) # Wait for subkey sync messages only when it's expected if key_name is not None: fim.create_registry(fim.registry_parser[key], os.path.join(subkey, key_name), fim.KEY_WOW64_64KEY) fim.check_time_travel(True, monitor=wazuh_log_monitor) wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=fim.callback_detect_end_scan, error_message='Did not receive expected "end_scan" event').result( ) events = get_sync_msgs(sync_interval + 15) # Parent key sync event assert find_path_in_event_list( test_regs[0], events) is not None, f"No sync event was found for {test_regs[0]}" # Subkey sync event subkey_path = os.path.join(test_regs[0], key_name) assert find_path_in_event_list( subkey_path, events) is not None, f'No sync event was found for {subkey_path}' if value_name is not None: if key_name is not None: key_path = os.path.join(subkey, key_name) else: key_path = subkey value_path = os.path.join(key, key_path, value_name) key_handle = fim.RegOpenKeyEx(fim.registry_parser[key], key_path, 0, fim.KEY_ALL_ACCESS | fim.KEY_WOW64_64KEY) fim.modify_registry_value(key_handle, value_name, fim.REG_SZ, 'This is a test') fim.check_time_travel(True, monitor=wazuh_log_monitor) wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=fim.callback_detect_end_scan, error_message='Did not receive expected "end_scan" event').result( ) events = get_sync_msgs(sync_interval + 15) assert find_value_in_event_list( os.path.join(key, key_path), value_name, events) is not None, f"No sync event was found for {value_path}"
def test_ignore_registry_value(root_key, registry, arch, value, triggers_event, tags_to_apply, get_configuration, configure_environment, restart_syscheckd, wait_for_fim_start): """ Check registry values are ignored according to configuration. Parameters ---------- root_key : str Root key (HKEY_*) registry : str path of the registry where the test will be executed. arch : str Architecture of the registry. value : str Name of the value that will be created. triggers_event : bool True if an event must be generated, False otherwise. tags_to_apply : set Run test if match with a configuration identifier, skip otherwise. """ check_apply_test(tags_to_apply, get_configuration['tags']) scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled' # Open the key (this shouldn't create an alert) key_h = create_registry(registry_parser[root_key], registry, arch) # Create values modify_registry_value(key_h, value, REG_SZ, "test_value") # Go ahead in time to let syscheck perform a new scan check_time_travel(scheduled, monitor=wazuh_log_monitor) event = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_detect_event, error_message='Did not receive expected ' '"Sending FIM event: ..." event', accum_results=2 if triggers_event else 1).result() if triggers_event: assert event[0]['data'][ 'type'] == 'modified', 'Parent key event type not equal' assert event[0]['data']['path'] == os.path.join( root_key, registry), 'Wrong parent key path.' assert event[0]['data'][ 'arch'] == '[x32]' if arch == KEY_WOW64_32KEY else '[x64]', 'Parent key arch not equal.' assert event[1]['data']['type'] == 'added', 'Wrong event type.' assert event[1]['data']['path'] == os.path.join( root_key, registry), 'Wrong value path.' assert event[1]['data'][ 'arch'] == '[x32]' if arch == KEY_WOW64_32KEY else '[x64]', 'wrong key arch.' assert event[1]['data']['value_name'] == value, 'Wrong value name' else: assert event['data'][ 'type'] == 'modified', 'Parent key event type not equal' assert event['data']['path'] == os.path.join( root_key, registry), 'Wrong parent key path.' assert event['data'][ 'arch'] == '[x32]' if arch == KEY_WOW64_32KEY else '[x64]', 'Parent key arch not equal.' while True: # Look for the ignore event of the created value ignored_value = wazuh_log_monitor.start( timeout=global_parameters.default_timeout, callback=callback_ignore).result() if ignored_value == "{} {}".format( '[x64]' if arch == KEY_WOW64_64KEY else '[x32]', os.path.join(root_key, registry, value)): break