2021-12-27 13:52:26 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
# written by sqall
|
|
|
|
# twitter: https://twitter.com/sqall01
|
|
|
|
# blog: https://h4des.org
|
|
|
|
# github: https://github.com/sqall01
|
|
|
|
#
|
2021-12-30 20:48:17 +01:00
|
|
|
# Licensed under the MIT License.
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
"""
|
|
|
|
Short summary:
|
|
|
|
Searches for immutable files in the filesystem.
|
|
|
|
|
|
|
|
Requirements:
|
|
|
|
None
|
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
2022-01-05 20:16:57 +01:00
|
|
|
from typing import List, cast
|
2021-12-27 13:52:26 +01:00
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
from lib.step_state import StepLocation, load_step_state, store_step_state
|
|
|
|
from lib.util import output_error, output_finding
|
|
|
|
from lib.util_file import FileLocation, apply_directory_whitelist, apply_file_whitelist
|
2021-12-27 13:52:26 +01:00
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
# Read configuration.
|
2021-12-27 13:52:26 +01:00
|
|
|
try:
|
|
|
|
from config.config import ALERTR_FIFO, FROM_ADDR, TO_ADDR, STATE_DIR
|
|
|
|
from config.search_immutable_files import ACTIVATED, SEARCH_IN_STEPS, SEARCH_LOCATIONS, \
|
|
|
|
IMMUTABLE_DIRECTORY_WHITELIST, IMMUTABLE_FILE_WHITELIST
|
|
|
|
|
|
|
|
STATE_DIR = os.path.join(os.path.dirname(__file__), STATE_DIR, os.path.basename(__file__))
|
|
|
|
except:
|
|
|
|
ALERTR_FIFO = None
|
|
|
|
FROM_ADDR = None
|
|
|
|
TO_ADDR = None
|
|
|
|
ACTIVATED = True
|
|
|
|
SEARCH_IN_STEPS = False
|
|
|
|
SEARCH_LOCATIONS = ["/"]
|
|
|
|
IMMUTABLE_DIRECTORY_WHITELIST = []
|
|
|
|
IMMUTABLE_FILE_WHITELIST = []
|
|
|
|
STATE_DIR = os.path.join("/tmp", os.path.basename(__file__))
|
|
|
|
|
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
class ImmutableFile(FileLocation):
|
|
|
|
def __init__(self, location: str, attribute: str):
|
|
|
|
super().__init__(location)
|
|
|
|
self._attribute = attribute
|
2021-12-27 13:52:26 +01:00
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
@property
|
|
|
|
def attribute(self) -> str:
|
|
|
|
return self._attribute
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
|
|
|
|
def search_immutable_files():
|
|
|
|
# Decide where to output results.
|
|
|
|
print_output = False
|
|
|
|
if ALERTR_FIFO is None and FROM_ADDR is None and TO_ADDR is None:
|
|
|
|
print_output = True
|
|
|
|
|
|
|
|
if not ACTIVATED:
|
|
|
|
if print_output:
|
|
|
|
print("Module deactivated.")
|
|
|
|
return
|
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
step_state_data = {}
|
2021-12-27 13:52:26 +01:00
|
|
|
try:
|
2022-01-05 20:16:57 +01:00
|
|
|
step_state_data = load_step_state(STATE_DIR)
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
except Exception as e:
|
2022-01-05 20:16:57 +01:00
|
|
|
output_error(__file__, str(e))
|
2021-12-27 13:52:26 +01:00
|
|
|
return
|
|
|
|
|
|
|
|
# Reset step if we do not search in steps but everything.
|
|
|
|
if not SEARCH_IN_STEPS:
|
2022-01-05 20:16:57 +01:00
|
|
|
step_state_data["next_step"] = 0
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
if not SEARCH_LOCATIONS:
|
|
|
|
SEARCH_LOCATIONS.append("/")
|
|
|
|
|
|
|
|
# Gather all search locations.
|
2022-01-05 20:16:57 +01:00
|
|
|
search_locations = [] # type: List[StepLocation]
|
2021-12-27 13:52:26 +01:00
|
|
|
# If SEARCH_IN_STEPS is active, build a list of directories to search in
|
|
|
|
if SEARCH_IN_STEPS:
|
|
|
|
for search_location in SEARCH_LOCATIONS:
|
|
|
|
|
|
|
|
# Add parent directory as non-recursive search location in order to search in it without going deeper.
|
2022-01-05 20:16:57 +01:00
|
|
|
search_locations.append(StepLocation(search_location, False))
|
2021-12-27 13:52:26 +01:00
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
# Add all containing subdirectories as recursive search locations.
|
2021-12-27 13:52:26 +01:00
|
|
|
elements = os.listdir(search_location)
|
|
|
|
elements.sort()
|
|
|
|
for element in elements:
|
|
|
|
path = os.path.join(search_location, element)
|
|
|
|
if os.path.isdir(path):
|
2022-01-05 20:16:57 +01:00
|
|
|
search_locations.append(StepLocation(path, True))
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
# If we do not search in separated steps, just add each directory as a recursive search location.
|
|
|
|
else:
|
|
|
|
for search_location in SEARCH_LOCATIONS:
|
2022-01-05 20:16:57 +01:00
|
|
|
search_locations.append(StepLocation(search_location, True))
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
# Reset index if it is outside the search locations.
|
2022-01-05 20:16:57 +01:00
|
|
|
if step_state_data["next_step"] >= len(search_locations):
|
|
|
|
step_state_data["next_step"] = 0
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
while True:
|
2022-01-05 20:16:57 +01:00
|
|
|
search_location_obj = search_locations[step_state_data["next_step"]]
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
# Get all immutable files.
|
2022-01-05 20:16:57 +01:00
|
|
|
if search_location_obj.search_recursive:
|
2021-12-27 13:52:26 +01:00
|
|
|
fd = os.popen("lsattr -R -a %s 2> /dev/null | sed -rn '/^[aAcCdDeijPsStTu\\-]{4}i/p'"
|
2022-01-05 20:16:57 +01:00
|
|
|
% search_location_obj.location)
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
else:
|
|
|
|
fd = os.popen("lsattr -a %s 2> /dev/null | sed -rn '/^[aAcCdDeijPsStTu\\-]{4}i/p'"
|
2022-01-05 20:16:57 +01:00
|
|
|
% search_location_obj.location)
|
2021-12-27 13:52:26 +01:00
|
|
|
output_raw = fd.read().strip()
|
|
|
|
fd.close()
|
|
|
|
|
|
|
|
if output_raw != "":
|
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
immutable_files = [] # type: List[ImmutableFile]
|
2021-12-27 13:52:26 +01:00
|
|
|
output_list = output_raw.split("\n")
|
|
|
|
for output_entry in output_list:
|
|
|
|
output_entry_list = output_entry.split(" ")
|
|
|
|
|
|
|
|
# Notify and skip line if sanity check fails.
|
|
|
|
if len(output_entry_list) != 2:
|
2022-01-05 20:16:57 +01:00
|
|
|
output_error(__file__, "Unable to process line '%s'" % output_entry)
|
2021-12-27 13:52:26 +01:00
|
|
|
continue
|
|
|
|
|
|
|
|
attributes = output_entry_list[0]
|
|
|
|
file_location = output_entry_list[1]
|
2022-01-05 20:16:57 +01:00
|
|
|
immutable_files.append(ImmutableFile(file_location, attributes))
|
2021-12-27 13:52:26 +01:00
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
dir_whitelist = [FileLocation(x) for x in IMMUTABLE_DIRECTORY_WHITELIST]
|
|
|
|
file_whitelist = [FileLocation(x) for x in IMMUTABLE_FILE_WHITELIST]
|
2021-12-27 13:52:26 +01:00
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
immutable_files = cast(List[ImmutableFile], apply_directory_whitelist(dir_whitelist, immutable_files))
|
|
|
|
immutable_files = cast(List[ImmutableFile], apply_file_whitelist(file_whitelist, immutable_files))
|
2021-12-27 13:52:26 +01:00
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
if immutable_files:
|
|
|
|
message = "Immutable file(s) found:\n\n"
|
|
|
|
message += "\n".join(["File: %s; Attributes: %s" % (x.location, x.attribute) for x in immutable_files])
|
2021-12-27 13:52:26 +01:00
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
output_finding(__file__, message)
|
2021-12-27 13:52:26 +01:00
|
|
|
|
2022-01-05 20:16:57 +01:00
|
|
|
step_state_data["next_step"] += 1
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
# Stop search if we are finished.
|
2022-01-05 20:16:57 +01:00
|
|
|
if SEARCH_IN_STEPS or step_state_data["next_step"] >= len(search_locations):
|
2021-12-27 13:52:26 +01:00
|
|
|
break
|
|
|
|
|
|
|
|
try:
|
2022-01-05 20:16:57 +01:00
|
|
|
store_step_state(STATE_DIR, step_state_data)
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
except Exception as e:
|
2022-01-05 20:16:57 +01:00
|
|
|
output_error(__file__, str(e))
|
2021-12-27 13:52:26 +01:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
search_immutable_files()
|