feature: rework guardrails

This commit is contained in:
Dobin Rutishauser
2025-06-09 22:09:32 +02:00
parent f55596f4a8
commit ca1f497074
5 changed files with 84 additions and 33 deletions
+2 -1
View File
@@ -211,7 +211,8 @@ def add_project():
settings.decoder_style = request.form['decoder_style'] settings.decoder_style = request.form['decoder_style']
payload_location = request.form['payload_location'] payload_location = request.form['payload_location']
settings.payload_location = PayloadLocation[payload_location] settings.payload_location = PayloadLocation[payload_location]
settings.plugin_guardrail_data = request.form.get('guardrail_data', settings.plugin_guardrail_data) settings.plugin_guardrail_data_key = request.form.get('guardrail_data_key', settings.plugin_guardrail_data_key)
settings.plugin_guardrail_data_value = request.form.get('guardrail_data_value', settings.plugin_guardrail_data_value)
settings.plugin_virtualprotect = request.form.get('virtualprotect', "standard") settings.plugin_virtualprotect = request.form.get('virtualprotect', "standard")
# overwrite project # overwrite project
+37 -20
View File
@@ -1,29 +1,46 @@
char my_tolower(char c) {
if (c >= 'A' && c <= 'Z') {
return c + ('a' - 'A'); // or return c + 32;
}
return c;
}
int mystrcmp(wchar_t* str1, wchar_t* str2) { // Returns 1 if 'needle' is found in 'haystack' (case-insensitive), 0 otherwise
int i = 0; int contains_case_insensitive(const char* haystack, const char* needle) {
while (str1[i] != L'\0' && str2[i] != L'\0') { if (!haystack || !needle)
if (str1[i] != str2[i]) { return 0;
return 1;
} for (; *haystack; haystack++) {
i++; const char* h = haystack;
} const char* n = needle;
return 0;
while (*h && *n && my_tolower((unsigned char)*h) == my_tolower((unsigned char)*n)) {
h++;
n++;
}
if (*n == '\0') {
return 1; // Match found
}
}
return 0; // No match
} }
int executionguardrail() { int executionguardrail() {
// Execution Guardrail: Env Check // Execution Guardrail: Env Check
wchar_t envVarName[] = L"USERPROFILE"; LPCSTR envVarName = "{{guardrail_data_key}}";
wchar_t tocheck[] = L"{{guardrail_data}}"; LPCSTR tocheck = "{{guardrail_data_value}}";
WCHAR buffer[1024]; // NOTE: Do not make it bigger, or we have a __chkstack() dependency! char buffer[1024]; // NOTE: Do not make it bigger, or we have a __chkstack() dependency!
DWORD result = GetEnvironmentVariableW(envVarName, buffer, 1024); DWORD result = GetEnvironmentVariableA(envVarName, buffer, 1024);
if (result == 0) { if (result == 0) {
return 6; return 6;
} }
if (mystrcmp(buffer, tocheck) != 0) { if (! contains_case_insensitive(buffer, tocheck)) {
return 6; return 6;
} }
return 0; return 0;
} }
+2 -1
View File
@@ -17,7 +17,8 @@ class Settings():
self.plugin_antiemulation: str = "none" self.plugin_antiemulation: str = "none"
self.plugin_decoy: str = "none" self.plugin_decoy: str = "none"
self.plugin_guardrail: str = "none" self.plugin_guardrail: str = "none"
self.plugin_guardrail_data: str = "C:\\\\Users\\\\hacker" self.plugin_guardrail_data_key: str = ""
self.plugin_guardrail_data_value: str = ""
self.plugin_virtualprotect: str = "standard" self.plugin_virtualprotect: str = "standard"
self.plugin_virtualprotect_data: str = "" self.plugin_virtualprotect_data: str = ""
+10 -4
View File
@@ -53,9 +53,14 @@ def create_c_from_template(settings: Settings, payload_len: int):
logger.info(" Carrier AntiEmulation: {}".format( logger.info(" Carrier AntiEmulation: {}".format(
settings.plugin_antiemulation) settings.plugin_antiemulation)
) )
logger.info(" Carrier Guardrail: {}".format( if settings.plugin_guardrail != "none":
settings.plugin_guardrail) logger.info(" Carrier Guardrail: {} (key: {} value: {})".format(
) settings.plugin_guardrail,
settings.plugin_guardrail_data_key,
settings.plugin_guardrail_data_value)
)
else:
logger.info(" Carrier Guardrail: none")
logger.info(" Carrier Decoy: {}".format( logger.info(" Carrier Decoy: {}".format(
settings.plugin_decoy) settings.plugin_decoy)
) )
@@ -75,7 +80,8 @@ def create_c_from_template(settings: Settings, payload_len: int):
with open(filepath_guardrails, "r", encoding='utf-8') as file: with open(filepath_guardrails, "r", encoding='utf-8') as file:
plugin_guardrails = file.read() plugin_guardrails = file.read()
plugin_guardrails = Template(plugin_guardrails).render({ plugin_guardrails = Template(plugin_guardrails).render({
'guardrail_data': settings.plugin_guardrail_data, 'guardrail_data_key': settings.plugin_guardrail_data_key,
'guardrail_data_value': settings.plugin_guardrail_data_value,
}) })
# Plugin: Decoder # Plugin: Decoder
+33 -7
View File
@@ -34,7 +34,10 @@ def main():
parser.add_argument('--carrier', type=str, help='carrier: data/source/carrier/* (alloc_rw_rx, peb_walk, ...)', default="alloc_rw_rx") parser.add_argument('--carrier', type=str, help='carrier: data/source/carrier/* (alloc_rw_rx, peb_walk, ...)', default="alloc_rw_rx")
parser.add_argument('--decoder', type=str, help='decoder: data/source/decoders/* (xor_1, xor_2, plain, ...)', default="xor_2") parser.add_argument('--decoder', type=str, help='decoder: data/source/decoders/* (xor_1, xor_2, plain, ...)', default="xor_2")
parser.add_argument('--antiemulation', type=str, help='anti-emulation: data/source/antiemulation/* (sirallocalot, timeraw, none, ...)', default="sirallocalot") parser.add_argument('--antiemulation', type=str, help='anti-emulation: data/source/antiemulation/* (sirallocalot, timeraw, none, ...)', default="sirallocalot")
parser.add_argument('--fix-iat', action='store_true', help='Fix missing IAT entries in the infectable executable', default=True) parser.add_argument('--guardrail', type=str, help='guardrails: Enable execution guardrails', default="none")
parser.add_argument('--guardrail-key', type=str, help='guardrails: key', default="")
parser.add_argument('--guardrail-value', type=str, help='guardrails: value', default="")
parser.add_argument('--no-fix-iat', action='store_true', help='Fix missing IAT entries in the infectable executable', default=False)
parser.add_argument('--carrier_invoke', type=str, help='how carrier is started: \"backdoor\" to rewrite call instruction, \"eop\" for entry point', choices=["eop", "backdoor"], default="backdoor") parser.add_argument('--carrier_invoke', type=str, help='how carrier is started: \"backdoor\" to rewrite call instruction, \"eop\" for entry point', choices=["eop", "backdoor"], default="backdoor")
parser.add_argument('--start', action='store_true', help='Start the infected executable at the end for testing') parser.add_argument('--start', action='store_true', help='Start the infected executable at the end for testing')
parser.add_argument('--short-call-patching', action='store_true', help='Debug: Make short calls long. You will know when you need it.') parser.add_argument('--short-call-patching', action='store_true', help='Debug: Make short calls long. You will know when you need it.')
@@ -51,10 +54,23 @@ def main():
else: else:
setup_logging(logging.INFO) setup_logging(logging.INFO)
settings.try_start_final_infected_exe = args.start_injected settings.try_start_final_infected_exe = args.start
settings.cleanup_files_on_start = not args.no_clean_at_start settings.cleanup_files_on_start = not args.no_clean_at_start
settings.cleanup_files_on_exit =not args.no_clean_at_exit settings.cleanup_files_on_exit =not args.no_clean_at_exit
settings.fix_missing_iat = not args.no_fix_iat
if args.guardrail:
settings.plugin_guardrail = args.guardrail
settings.plugin_guardrail_data_key = args.guardrail_key
settings.plugin_guardrail_data_value = args.guardrail_value
logger.info("-( Config: Implant IAT fixup if necessary: {}".format(settings.fix_missing_iat))
if settings.plugin_guardrail != "none":
logger.info("-( Config: Guardrails Plugin: {} {}/{}".format(
settings.plugin_guardrail,
settings.plugin_guardrail_data_key,
settings.plugin_guardrail_data_value))
# Shellcode: filename # Shellcode: filename
# Inject: filename # Inject: filename
settings.init_payload_injectable( settings.init_payload_injectable(
@@ -130,14 +146,14 @@ def sanity_checks(settings):
def start_real(settings: Settings): def start_real(settings: Settings) -> bool:
"""Main entry point for the application. This is where the magic happens (based on settings)""" """Main entry point for the application. This is where the magic happens (based on settings)"""
# Load our input # Load our input
project = Project(settings) project = Project(settings)
if not project.init(): if not project.init():
logger.error("Error initializing project") logger.error("Error initializing project")
return 1 return False
# CHECK if 64 bit # CHECK if 64 bit
if not project.injectable.superpe.is_64(): if not project.injectable.superpe.is_64():
@@ -158,7 +174,7 @@ def start_real(settings: Settings):
phases.templater.create_c_from_template(settings, len(project.payload.payload_data)) phases.templater.create_c_from_template(settings, len(project.payload.payload_data))
except FileNotFoundError as e: except FileNotFoundError as e:
logger.error("Error creating C from template: {}".format(e)) logger.error("Error creating C from template: {}".format(e))
return 1 return False
# PREPARE DataReuseEntry for usage in Compiler/AsmTextParser # PREPARE DataReuseEntry for usage in Compiler/AsmTextParser
# So the carrier is able to find the payload # So the carrier is able to find the payload
@@ -181,7 +197,7 @@ def start_real(settings: Settings):
settings = project.settings) settings = project.settings)
except ChildProcessError as e: except ChildProcessError as e:
logger.error("Error compiling C to ASM: {}".format(e)) logger.error("Error compiling C to ASM: {}".format(e))
return return False
# we have the carrier-required IAT entries in carrier.iat_requests # we have the carrier-required IAT entries in carrier.iat_requests
# CHECK if all are available in infectable, or abort (early check) # CHECK if all are available in infectable, or abort (early check)
@@ -190,7 +206,8 @@ def start_real(settings: Settings):
logging.error("IAT entries not found in infectable: {}".format(", ".join(functions))) logging.error("IAT entries not found in infectable: {}".format(", ".join(functions)))
logging.error("The carrier depends on these functions, but they are not available in the infectable exe.") logging.error("The carrier depends on these functions, but they are not available in the infectable exe.")
logging.error("Use another infectable exe, or update the carrier to not depend on these functions.") logging.error("Use another infectable exe, or update the carrier to not depend on these functions.")
raise Exception("Required carrier import not found in infectable: {}".format(", ".join(functions))) logging.error(" or dont use --no-fix-iat")
return False
# ASSEMBLE: Assemble .asm to .shc (ASM -> SHC) # ASSEMBLE: Assemble .asm to .shc (ASM -> SHC)
carrier_shellcode: bytes = phases.assembler.asm_to_shellcode( carrier_shellcode: bytes = phases.assembler.asm_to_shellcode(
@@ -227,6 +244,15 @@ def start_real(settings: Settings):
elif settings.try_start_final_infected_exe: elif settings.try_start_final_infected_exe:
run_exe(settings.inject_exe_out, dllfunc=settings.dllfunc, check=False) run_exe(settings.inject_exe_out, dllfunc=settings.dllfunc, check=False)
if settings.plugin_guardrail != "none":
logger.warning("! Remember your guardrails settings when testing")
logger.warning("! {}: {} / {}".format(
settings.plugin_guardrail,
settings.plugin_guardrail_data_key,
settings.plugin_guardrail_data_value))
return True
def obfuscate_shc_loader(file_shc_in, file_shc_out): def obfuscate_shc_loader(file_shc_in, file_shc_out):
logger.info(" Obfuscate shellcode with SGN") logger.info(" Obfuscate shellcode with SGN")