mirror of
https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite.git
synced 2025-12-10 18:59:02 +00:00
Big linpeas update
This commit is contained in:
@@ -1,37 +1,333 @@
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
from .linpeasModule import LinpeasModule, LinpeasModuleList
|
||||
from .yamlGlobals import (
|
||||
LINPEAS_PARTS,
|
||||
LINPEAS_BASE_PATH,
|
||||
TEMPORARY_LINPEAS_BASE_PATH,
|
||||
PEAS_CHECKS_MARKUP
|
||||
)
|
||||
|
||||
class LinpeasBaseBuilder:
|
||||
def __init__(self):
|
||||
with open(LINPEAS_BASE_PATH, 'r') as file:
|
||||
self.linpeas_base = file.read()
|
||||
def __init__(self, all_modules, all_no_fat_modules, no_network_scanning, small, include_modules, exclude_modules):
|
||||
# Everything relevant
|
||||
self.all_modules = self.get_modules(all_modules, all_no_fat_modules, no_network_scanning, small, include_modules, exclude_modules)
|
||||
# Only base
|
||||
self.base = self.get_base()
|
||||
# Only checks
|
||||
self.checks = self.get_checks()
|
||||
print(f"[+] {len(self.checks)} checks located")
|
||||
# Only functions sorted
|
||||
self.functions = self.get_functions()
|
||||
# Only variables sorted
|
||||
self.variables = self.get_variables()
|
||||
|
||||
self.linpeas_base = ""
|
||||
|
||||
|
||||
|
||||
def build(self):
|
||||
print("[+] Building temporary linpeas_base.sh...")
|
||||
checks = []
|
||||
for part in LINPEAS_PARTS:
|
||||
name = part["name"]
|
||||
assert name, f"Name not found in {part}"
|
||||
name_check = part["name_check"]
|
||||
assert name_check, f"Name not found in {name_check}"
|
||||
file_path = part["file_path"]
|
||||
assert file_path, f"Name not found in {file_path}"
|
||||
print("[+] Building temporary linpeas_base.sh with the indicated modules...")
|
||||
|
||||
# Add base code
|
||||
for base in self.base:
|
||||
self.linpeas_base += base.sh_code.strip() + "\n\n"
|
||||
|
||||
# Add variables
|
||||
self.linpeas_base += "\n\n\n# Variables\n\n"
|
||||
for variable in self.variables:
|
||||
if "Checks pre-everything" in variable.sh_code:
|
||||
a=1
|
||||
self.linpeas_base += variable.sh_code.strip() + "\n\n"
|
||||
|
||||
self.linpeas_base += "\n\n\n# Functions\n\n"
|
||||
# Add functions
|
||||
for function in self.functions:
|
||||
self.linpeas_base += function.sh_code.strip() + "\n\n"
|
||||
|
||||
with open(file_path, 'r') as file:
|
||||
linpeas_part = file.read()
|
||||
self.linpeas_base += "\n\n\n# Checks\n\n"
|
||||
|
||||
section_checks = {}
|
||||
check_names = []
|
||||
for check in self.checks:
|
||||
# Get the section of the check
|
||||
for part_mod in LINPEAS_PARTS["modules"]:
|
||||
if part_mod["folder_path"] in check.path:
|
||||
if part_mod["name"] not in section_checks:
|
||||
section_checks[part_mod["name"]] = part_mod
|
||||
section_checks[part_mod["name"]]["checks"] = []
|
||||
section_checks[part_mod["name"]]["checks"].append(check)
|
||||
break
|
||||
|
||||
initial_functions = set()
|
||||
for section_name, section_info in section_checks.items():
|
||||
# Add 1 time the big section name to check_names to then put it inside linpeas in PEAS_CHECKS_MARKUP
|
||||
if not section_info['name_check'] in check_names: check_names.append(section_info['name_check'])
|
||||
self.linpeas_base += f"\nif echo $CHECKS | grep -q {section_info['name_check']}; then\n"
|
||||
self.linpeas_base += f'print_title "{section_name}"\n'
|
||||
|
||||
# Sort checks alphabetically to get them in the same order of they are in the folder
|
||||
section_info["checks"] = sorted(section_info["checks"], key=lambda x: int(os.path.basename(x.path).split('_')[0]) if os.path.basename(x.path).split('_')[0].isdigit() else 99)
|
||||
for check in section_info["checks"]:
|
||||
for func in check.initial_functions:
|
||||
if not func in initial_functions:
|
||||
self.linpeas_base += func + "\n"
|
||||
initial_functions.add(func)
|
||||
|
||||
self.linpeas_base += check.sh_code.strip() + "\n\n"
|
||||
|
||||
checks.append(name_check)
|
||||
self.linpeas_base += f"\nif echo $CHECKS | grep -q {name_check}; then\n"
|
||||
self.linpeas_base += f'print_title "{name}"\n'
|
||||
self.linpeas_base += linpeas_part
|
||||
self.linpeas_base += f"\nfi\necho ''\necho ''\n"
|
||||
self.linpeas_base += 'if [ "$WAIT" ]; then echo "Press enter to continue"; read "asd"; fi\n'
|
||||
|
||||
self.linpeas_base = self.linpeas_base.replace(PEAS_CHECKS_MARKUP, ",".join(checks))
|
||||
self.linpeas_base = self.linpeas_base.replace(PEAS_CHECKS_MARKUP, ",".join(check_names))
|
||||
|
||||
with open(TEMPORARY_LINPEAS_BASE_PATH, "w") as f:
|
||||
f.write(self.linpeas_base)
|
||||
|
||||
def find_func_module(self, func_name:str):
|
||||
"""Given a function name and the list of modules return the module that contains the function"""
|
||||
|
||||
modules = []
|
||||
for module in self.all_modules:
|
||||
if func_name in module.defined_funcs:
|
||||
modules.append(module)
|
||||
|
||||
if len(modules) == 0:
|
||||
raise Exception(f"Function {func_name} not found in any module")
|
||||
elif len(modules) > 1:
|
||||
raise Exception(f"Function {func_name} found in more than 1 module: {modules}")
|
||||
|
||||
return modules[0]
|
||||
|
||||
def find_variable_module(self, var_name:str, orig_module:LinpeasModule):
|
||||
"""Given a variable name and the list of modules return the module that contains the variable"""
|
||||
|
||||
modules = []
|
||||
for module in self.all_modules:
|
||||
if var_name in module.generated_global_variables:
|
||||
modules.append(module)
|
||||
|
||||
if len(modules) == 0:
|
||||
raise Exception(f"Variable '{var_name}' from {orig_module.path} not found in any module")
|
||||
elif len(modules) > 1:
|
||||
raise Exception(f"Variable {var_name} found in more than 1 module: {', '.join([m.path for m in modules])}")
|
||||
|
||||
return modules[0]
|
||||
|
||||
def sort_funcs(self, functions:List[LinpeasModule]):
|
||||
"""Given a list of functions, return the list sorted by dependencies"""
|
||||
|
||||
sorted_funcs = functions.copy()
|
||||
retry = False
|
||||
|
||||
for i,func in enumerate(functions):
|
||||
for d_func in func.functions_used:
|
||||
is_base = False
|
||||
# If the dependant variable is defined in a module that is in the base, remove it from the list
|
||||
if any (d_func in m.defined_funcs for m in self.base):
|
||||
try:
|
||||
sorted_funcs.index(d_func) # Check if it's there
|
||||
sorted_funcs.remove(d_func) # Remove if it's
|
||||
retry = True # After a failure, start again
|
||||
except:
|
||||
pass
|
||||
|
||||
is_base = True
|
||||
|
||||
if is_base:
|
||||
continue
|
||||
|
||||
# If a dependant variable is after the current one, move it to the current position
|
||||
try:
|
||||
dp_index = functions.index(d_func)
|
||||
except:
|
||||
raise Exception(f"Variable {d_func} not found in {func.path}")
|
||||
|
||||
if dp_index > i:
|
||||
sorted_funcs.remove(d_func)
|
||||
sorted_funcs.insert(i, functions[dp_index])
|
||||
retry = True
|
||||
|
||||
if retry:
|
||||
return self.sort_funcs(sorted_funcs)
|
||||
return sorted_funcs
|
||||
|
||||
|
||||
def sort_variables(self, variables:List[LinpeasModule]):
|
||||
"""Given a list of variables, return the list sorted by dependencies"""
|
||||
|
||||
sorted_vars = variables.copy()
|
||||
retry = False
|
||||
|
||||
for i,var in enumerate(variables):
|
||||
for d_var in var.global_variables:
|
||||
is_base = False
|
||||
# If the dependant variable is defined in a module that is in the base, remove it from the list
|
||||
if any (d_var in m.generated_global_variables for m in self.base):
|
||||
try:
|
||||
sorted_vars.index(d_var) # Check if it's there
|
||||
sorted_vars.remove(d_var) # Remove if it's
|
||||
retry = True # After a failure, start again
|
||||
except:
|
||||
pass
|
||||
|
||||
is_base = True
|
||||
|
||||
if is_base:
|
||||
continue
|
||||
|
||||
# If a dependant variable is after the current one, move it to the current position
|
||||
try:
|
||||
dp_index = variables.index(d_var)
|
||||
except:
|
||||
raise Exception(f"Variable {d_var} not found in {var.path}")
|
||||
|
||||
if dp_index > i:
|
||||
sorted_vars.remove(d_var)
|
||||
sorted_vars.insert(i, variables[dp_index])
|
||||
retry = True
|
||||
|
||||
if retry:
|
||||
return self.sort_variables(sorted_vars)
|
||||
return sorted_vars
|
||||
|
||||
def get_funcs_deps(self, module, all_funcs):
|
||||
"""Given 1 module and the list of modules return the functions recursively it depends on"""
|
||||
|
||||
for func in module.functions_used:
|
||||
func_module = self.find_func_module(func)
|
||||
#print(f"{module.id} has found {func} in {func_module.id}") #To find circular dependencies
|
||||
if not func_module.is_function:
|
||||
continue
|
||||
if func_module in all_funcs:
|
||||
all_funcs.remove(func_module)
|
||||
all_funcs.append(func_module)
|
||||
all_funcs = self.get_funcs_deps(func_module, all_funcs)
|
||||
|
||||
return all_funcs
|
||||
|
||||
|
||||
def get_vars_deps(self, module, all_vars):
|
||||
"""Given 1 module and the list of modules return the variables recursively it depends on"""
|
||||
|
||||
for var in module.global_variables:
|
||||
var_module = self.find_variable_module(var, module)
|
||||
#print(f"{module.id} has found {var} in {var_module.id}") #To find circular dependencies
|
||||
if not var_module.is_variable:
|
||||
continue
|
||||
if var_module in all_vars:
|
||||
all_vars.remove(var_module)
|
||||
all_vars.append(var_module)
|
||||
all_vars = self.get_vars_deps(var_module, all_vars)
|
||||
|
||||
return all_vars
|
||||
|
||||
|
||||
def get_functions(self):
|
||||
"""Get all the functions used sorted, first the ones that don't depend on any other, then the ones that depend on the previous ones, etc."""
|
||||
|
||||
all_funcs = LinpeasModuleList()
|
||||
|
||||
for module in self.checks:
|
||||
all_funcs = self.get_funcs_deps(module, all_funcs)
|
||||
|
||||
return self.sort_funcs(all_funcs)
|
||||
|
||||
|
||||
def get_variables(self):
|
||||
"""Get all the variables used sorted, first the ones that don't depend on any other, then the ones that depend on the previous ones, etc."""
|
||||
|
||||
all_variables = LinpeasModuleList()
|
||||
|
||||
for module in self.checks + self.functions:
|
||||
all_variables = self.get_vars_deps(module, all_variables)
|
||||
|
||||
return self.sort_variables(all_variables)
|
||||
|
||||
|
||||
def get_checks(self):
|
||||
"""Given all the modules get only the checks"""
|
||||
|
||||
checks = LinpeasModuleList()
|
||||
for module in self.all_modules:
|
||||
if not module.is_check:
|
||||
continue
|
||||
|
||||
checks.append(module)
|
||||
|
||||
return checks
|
||||
|
||||
|
||||
def get_base(self):
|
||||
"""Given all the modules get only the base"""
|
||||
|
||||
checks = LinpeasModuleList()
|
||||
for module in self.all_modules:
|
||||
if not module.is_base:
|
||||
continue
|
||||
|
||||
checks.append(module)
|
||||
|
||||
return checks
|
||||
|
||||
|
||||
def enumerate_directory(self, path):
|
||||
"""Given a directory get the paths to all the files inside it"""
|
||||
return sorted([os.path.join(path, f) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))])
|
||||
|
||||
def get_modules(self, all_modules, all_no_fat_modules, no_network_scanning, small, include_modules, exclude_modules) -> LinpeasModuleList:
|
||||
"""Get all the base, variable, function and specified modules to create the new linpeas"""
|
||||
|
||||
print("[+] Checking the syntax of the modules...")
|
||||
parsed_modules = LinpeasModuleList()
|
||||
all_module_paths = []
|
||||
# Base modules
|
||||
all_module_paths += self.enumerate_directory(LINPEAS_PARTS["base"])
|
||||
|
||||
# Function modules
|
||||
all_module_paths += self.enumerate_directory(LINPEAS_PARTS["functions"])
|
||||
|
||||
# Variable modules
|
||||
all_module_paths += self.enumerate_directory(LINPEAS_PARTS["variables"])
|
||||
|
||||
for module in LINPEAS_PARTS["modules"]:
|
||||
for ex_module in exclude_modules:
|
||||
if ex_module in module["folder_path"] or ex_module in [module["name"], module["name_check"]]:
|
||||
continue
|
||||
all_module_paths += self.enumerate_directory(module["folder_path"])
|
||||
|
||||
for module in all_module_paths:
|
||||
m = LinpeasModule(module)
|
||||
|
||||
# If base, function or variable, add it as it will only be used if needed
|
||||
if m.is_function or m.is_variable:
|
||||
parsed_modules.append(m)
|
||||
continue
|
||||
|
||||
# If base but no interested in network scanning, skip, else, add
|
||||
if m.is_base:
|
||||
if "check_network_jobs" in m.path and no_network_scanning:
|
||||
continue
|
||||
parsed_modules.append(m)
|
||||
continue
|
||||
|
||||
# If explicitely excluded, skip
|
||||
if m.id in exclude_modules:
|
||||
continue
|
||||
if all_no_fat_modules and m.is_fat:
|
||||
continue
|
||||
if small and not m.is_small:
|
||||
continue
|
||||
|
||||
# If implicitly included, add
|
||||
if all_modules or all_no_fat_modules or m.id in include_modules:
|
||||
parsed_modules.append(m)
|
||||
for in_module in include_modules:
|
||||
if in_module.lower() in os.path.basename(m.path).lower() or in_module.lower() == m.id.lower() or in_module in [m.section_info["name"], m.section_info["name_check"]]:
|
||||
parsed_modules.append(m)
|
||||
break
|
||||
|
||||
return parsed_modules
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user