PEASS-ng/linPEAS/builder/src/linpeasModule.py
2024-08-27 23:56:21 +02:00

229 lines
10 KiB
Python

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())