import os import re from .yamlGlobals import ( LINPEAS_PARTS ) class LinpeasModule: def __init__(self, path): self.path = path with open(path, 'r') as file: self.module_text = file.read() self.sh_code = "" self.is_check = False self.is_function = False self.is_variable = False self.is_base = False if "/functions/" in path: self.is_function = True elif "/variables/" in path: self.is_variable = True elif "/linpeas_base/" in path: self.is_base = True self.section_info = {} if not (self.is_base or self.is_function or self.is_variable): for module in LINPEAS_PARTS["modules"]: if module["folder_path"] in path: self.section_info = module self.is_check = True break if not (self.is_base or self.is_function or self.is_variable or self.is_check): raise Exception(f"Module {path} doesn't belong to any section") for i,line in enumerate(self.module_text.splitlines()): if i == 0: if not line.startswith("# Title:"): raise Exception(f"Wrong title in module {path}. It should start with '# Title: '") self.title = line[8:].strip() elif i == 1: if not line.startswith("# ID:"): raise Exception(f"Wrong ID in module {path}. It should start with '# ID: '") self.id = line[5:].strip() if re.sub('^[0-9]+_', '', os.path.basename(path).replace(".sh", "")) not in [self.id, self.id[3:]]: raise Exception(f"Wrong ID in module {path}. It should be the same as the filename") elif i == 2: if not line.startswith("# Author:"): raise Exception(f"Wrong author in module {path}. It should start with '# Author: '") self.author = line[10:].strip() elif i == 3: if not line.startswith("# Last Update:"): raise Exception(f"Wrong last update in module {path}. It should start with '# Last Update: '") self.last_update = line[15:].strip() elif i == 4: if not line.startswith("# Description:"): raise Exception(f"Wrong description in module {path}. It should start with '# Description: '") self.description = line[15:].strip() elif i == 5: if not line.startswith("# License:"): raise Exception(f"Wrong license in module {path}. It should start with '# License: '") elif i == 6: if not line.startswith("# Version:"): raise Exception(f"Wrong version in module {path}. It should start with '# Version: '") self.version = line[11:].strip() elif i == 7: if not line.startswith("# Functions Used:"): raise Exception(f"Wrong functions used in module {path}. It should start with '# Functions Used: '") self.functions_used = line[17:].split(",") self.functions_used = [f.strip() for f in self.functions_used if f.strip()] if "/variables/" in path and self.functions_used: raise Exception(f"Variables shouldn't user functions, so functions in module {path} should be empty") elif i == 8: if not line.startswith("# Global Variables:"): raise Exception(f"Wrong global variables in module {path}. It should start with '# Global Variables: '") self.global_variables = line[19:].split(",") self.global_variables = [f.strip().replace("$", "") for f in self.global_variables if f.strip()] elif i == 9: if not line.startswith("# Initial Functions:"): raise Exception(f"Wrong initial functions in module {path}. It should start with '# Initial Functions: '") self.initial_functions = line[20:].split(",") self.initial_functions = [f.strip() for f in self.initial_functions if f.strip()] elif i == 10: if not line.startswith("# Generated Global Variables:"): raise Exception(f"Wrong generated global variables in module {path}. It should start with '# Generated Global Variables: '") self.generated_global_variables = line[29:].split(",") self.generated_global_variables = [f.strip().replace("$", "") for f in self.generated_global_variables if f.strip()] elif i == 11: if not line.startswith("# Fat linpeas:"): raise Exception(f"Wrong generated global variables in module {path}. It should start with '# Generated Global Variables: '") self.is_fat = bool(int(line[15])) elif i == 12: if not line.startswith("# Small linpeas:"): raise Exception(f"Wrong generated global variables in module {path}. It should start with '# Generated Global Variables: '") self.is_small = bool(int(line[17])) elif i == 13: if line != "": raise Exception(f"Wrong module {path}. Line 12 should be a new line") else: self.sh_code += line + "\n" self.sh_code = self.sh_code.strip() self.defined_funcs = self.extract_function_names() # Check if the indicated dependencies are actually being used for func in self.functions_used: if func not in self.sh_code and func not in self.initial_functions and not "peass{" in self.sh_code: raise Exception(f"Used function '{func}' in module {path} doesn't exist in the final code") for var in self.global_variables: if var not in self.sh_code and not "peass{" in self.sh_code: raise Exception(f"Used variable '{var}' in module {path} doesn't exist in the final code") for var in self.generated_global_variables: if var not in self.sh_code: raise Exception(f"Generated variable '{var}' in module {path} doesn't exist in the final code") # Check for funcs and vars imported from itself for func in self.defined_funcs: if func in self.functions_used: raise Exception(f"Function '{func}' in module {path} is imported from itself") for var in self.global_variables: if var in self.generated_global_variables: raise Exception(f"Variable '{var}' in module {path} is imported from itself") # Check if all variables are correctly defined linux_global_vars = [ "OPTARG", "PID", "PPID", "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "AWS_LAMBDA_RUNTIME_API", "ECS_CONTAINER_METADATA_URI", "ECS_CONTAINER_METADATA_URI_v4", "IDENTITY_ENDPOINT", "IDENTITY_HEADER", "KUBERNETES_SERVICE_PORT_HTTPS", "KUBERNETES_SERVICE_HOST" ] main_base = None # Base global variables don't need to be defined if self.id != "BS_variables_base": main_base = LinpeasModule(os.path.join(os.path.dirname(__file__), "..", "linpeas_parts", "linpeas_base", "0_variables_base.sh")) for var in self.extract_variables(self.sh_code): if len(var) > 2 and not var in linux_global_vars and var not in self.global_variables and var not in self.generated_global_variables: if not var.startswith("PSTORAGE_"): if not main_base or var not in main_base.generated_global_variables: raise Exception(f"Variable '{var}' in module {path} is not defined") def __eq__(self, other): # Check if other object is an instance of LinpeasModule if isinstance(other, LinpeasModule): return self.id == other.id return NotImplemented # Return NotImplemented for unsupported comparisons def extract_function_names(self): # This regular expression pattern matches function definitions in sh code pattern = r'\b(\w+)\s*\(\s*\)\s*{' return re.findall(pattern, self.sh_code) def extract_variables(self, sh_code): # This regex pattern matches variables in the form $VAR_NAME or ${VAR_NAME} pattern = r'\$({?([a-zA-Z_][a-zA-Z0-9_]*)}?)' matches = re.findall(pattern, sh_code) # Extract the variable name from each match variables = [match[1] for match in matches] return list(set(variables)) # Using set to remove duplicates class LinpeasModuleList(list): def __contains__(self, item): # Check if item is already a LinpeasModule object. if isinstance(item, LinpeasModule): return super().__contains__(item) # Otherwise, treat the item as the id of a LinpeasModule. for module in self: if module.id == item: return True return False def index(self, item_id): for index, module in enumerate(self): if module.id == item_id: return index raise ValueError(f"{item_id} is not in the list") def remove(self, item): # If item is an id, find the corresponding object first. if not isinstance(item, LinpeasModule): index = self.index(item) super().pop(index) else: super().remove(item) def insert(self, index, item): # Ensure that item is a LinpeasModule object before inserting. if not isinstance(item, LinpeasModule): raise ValueError("Item should be an instance of LinpeasModule") super().insert(index, item) def copy(self): return LinpeasModuleList(super().copy())