Last active
July 22, 2024 14:18
-
-
Save sam2332/2c1bbbc2d9a176a47b2e79efddb8360c to your computer and use it in GitHub Desktop.
Check System paths for command overwrites
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import pwd | |
import grp | |
import hashlib | |
from collections import defaultdict | |
# Trusted users | |
TRUSTED_USERS = ['root'] | |
# Base system directories | |
BASE_SYSTEM_DIRS = ['/bin', '/sbin', '/usr/bin', '/usr/sbin'] | |
def get_user_name(uid): | |
try: | |
return pwd.getpwuid(uid).pw_name | |
except KeyError: | |
return None | |
def get_group_name(gid): | |
try: | |
return grp.getgrgid(gid).gr_name | |
except KeyError: | |
return None | |
def check_directory_ownership(directory): | |
try: | |
st = os.stat(directory) | |
uid, gid = st.st_uid, st.st_gid | |
user_name = get_user_name(uid) | |
group_name = get_group_name(gid) | |
return user_name, group_name | |
except FileNotFoundError as e: | |
raise e | |
def is_trusted_user(user_name): | |
return user_name in TRUSTED_USERS | |
def get_executables_in_directory(directory): | |
try: | |
return [f for f in os.listdir(directory) if os.access(os.path.join(directory, f), os.X_OK)] | |
except FileNotFoundError: | |
return [] | |
def compute_file_hash(filepath): | |
sha256 = hashlib.sha256() | |
try: | |
with open(filepath, 'rb') as f: | |
while chunk := f.read(8192): | |
sha256.update(chunk) | |
return sha256.hexdigest() | |
except FileNotFoundError: | |
return None | |
except IsADirectoryError: | |
return None | |
def main(): | |
path_dirs = os.getenv('PATH', '').split(os.pathsep) | |
path_dirs = [d for d in path_dirs if os.path.isdir(d)] | |
path_dirs = list(set(path_dirs)) | |
# Collect all system commands | |
base_system_commands = {} | |
all_commands = defaultdict(list) | |
for base_dir in BASE_SYSTEM_DIRS: | |
for cmd in get_executables_in_directory(base_dir): | |
base_system_commands[cmd] = base_dir | |
all_commands[cmd].append(os.path.join(base_dir, cmd)) | |
# Check each directory in the PATH variable | |
for directory in path_dirs: | |
if directory in BASE_SYSTEM_DIRS: | |
continue | |
try: | |
user_name, group_name = check_directory_ownership(directory) | |
if not is_trusted_user(user_name): | |
print(f"Untrusted directory owner: {directory} (owned by {user_name}:{group_name})") | |
for cmd in get_executables_in_directory(directory): | |
all_commands[cmd].append(os.path.join(directory, cmd)) | |
if cmd in base_system_commands: | |
print(f"Command overwrite detected: {cmd} in {directory} (base: {base_system_commands[cmd]})") | |
except FileNotFoundError: | |
print(f"Directory not found: {directory}") | |
# Check for duplicate commands across directories and compare their hashes | |
for cmd, filepaths in all_commands.items(): | |
if len(filepaths) > 1: | |
hashes = {fp: compute_file_hash(fp) for fp in filepaths} | |
unique_hashes = set(hashes.values()) | |
if len(unique_hashes) > 1: | |
print(f"Duplicate but different command detected: {cmd} in directories {filepaths}") | |
else: | |
pass#print(f"Duplicate but identical command detected: {cmd} in directories {filepaths}") | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment