335 lines
13 KiB
Python
335 lines
13 KiB
Python
import os
|
|
from typing import List
|
|
|
|
from .linpeasModule import LinpeasModule, LinpeasModuleList
|
|
from .yamlGlobals import (
|
|
LINPEAS_PARTS,
|
|
TEMPORARY_LINPEAS_BASE_PATH,
|
|
PEAS_CHECKS_MARKUP
|
|
)
|
|
|
|
class LinpeasBaseBuilder:
|
|
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 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"
|
|
|
|
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 as 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"
|
|
|
|
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(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"""
|
|
|
|
module_funcs = list(set(module.initial_functions + module.functions_used))
|
|
for func in module_funcs:
|
|
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
|
|
|
|
|
|
|