Removed unused code.

This commit is contained in:
Jakob Friedl
2025-10-01 15:27:06 +02:00
parent c97cb4585f
commit 0937840b77
10 changed files with 173 additions and 57 deletions

View File

@@ -3,6 +3,6 @@
-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="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
-d:MODULES="255" -d:MODULES="255"
-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

@@ -279,7 +279,6 @@ type
listeners*: Table[string, Listener] listeners*: Table[string, Listener]
threads*: Table[string, Thread[Listener]] threads*: Table[string, Thread[Listener]]
agents*: Table[string, Agent] agents*: Table[string, Agent]
interactAgent*: Agent
keyPair*: KeyPair keyPair*: KeyPair
profile*: Profile profile*: Profile
client*: UIClient client*: UIClient

View File

@@ -1,10 +1,8 @@
import terminal, strformat, strutils, tables, times, system, parsetoml import terminal, strformat, strutils, tables, system, parsetoml
import ../utils
import ../core/logger import ../core/logger
import ../db/database import ../db/database
import ../../common/types import ../../common/types
import ../websocket
# Terminate agent and remove it from the database # Terminate agent and remove it from the database
proc agentKill*(cq: Conquest, name: string) = proc agentKill*(cq: Conquest, name: string) =
@@ -17,7 +15,6 @@ proc agentKill*(cq: Conquest, name: string) =
# TODO: Stop the process of the agent on the target system # TODO: Stop the process of the agent on the target system
# TODO: Add flag to self-delete executable after killing agent # TODO: Add flag to self-delete executable after killing agent
# Remove the agent from the database # Remove the agent from the database
if not cq.dbDeleteAgentByName(name.toUpperAscii): if not cq.dbDeleteAgentByName(name.toUpperAscii):
cq.error("Failed to terminate agent: ", getCurrentExceptionMsg()) cq.error("Failed to terminate agent: ", getCurrentExceptionMsg())

View File

@@ -3,7 +3,7 @@ import terminal, strformat, strutils, sequtils, tables, system, osproc, streams,
import ../globals import ../globals
import ../core/logger import ../core/logger
import ../db/database import ../db/database
import ../../common/[types, utils, profile, serialize, crypto] import ../../common/[types, utils, serialize, crypto]
const PLACEHOLDER = "PLACEHOLDER" const PLACEHOLDER = "PLACEHOLDER"

View File

@@ -2,8 +2,6 @@ import strformat, strutils, terminal
import mummy, mummy/routers import mummy, mummy/routers
import parsetoml import parsetoml
import ../globals
import ../utils
import ../api/routes import ../api/routes
import ../db/database import ../db/database
import ../core/logger import ../core/logger

View File

@@ -11,14 +11,15 @@ proc makeAgentLogDirectory*(cq: Conquest, agentId: string): bool =
except OSError: except OSError:
return false return false
proc log*(cq: Conquest, logEntry: string) = proc log*(cq: Conquest, agentId: string = "", logEntry: string) =
# 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
let agentLogPath = fmt"{CONQUEST_ROOT}/data/logs/{cq.interactAgent.agentId}/session.log"
# Write log entry to file # Write log entry to file
let file = open(agentLogPath, fmAppend) var logFile: string
file.writeLine(fmt"{logEntry}") if agentId.isEmptyOrWhitespace():
logFile = fmt"{CONQUEST_ROOT}/data/logs/events.log"
else:
logFile = fmt"{CONQUEST_ROOT}/data/logs/{agentId}/session.log"
let file = open(logFile, fmAppend)
file.writeLine(logEntry)
file.flushFile() file.flushFile()
proc extractStrings*(args: string): string = proc extractStrings*(args: string): string =
@@ -38,8 +39,7 @@ proc getTimestamp*(): string =
# Function templates and overwrites # Function templates and overwrites
template writeLine*(cq: Conquest, args: varargs[untyped] = "") = template writeLine*(cq: Conquest, args: varargs[untyped] = "") =
stdout.styledWriteLine(args) stdout.styledWriteLine(args)
if cq.interactAgent != nil: # cq.log(extractStrings($(args)))
cq.log(extractStrings($(args)))
# Wrapper functions for logging/console output # Wrapper functions for logging/console output
template info*(cq: Conquest, args: varargs[untyped] = "") = template info*(cq: Conquest, args: varargs[untyped] = "") =
@@ -54,11 +54,5 @@ template success*(cq: Conquest, args: varargs[untyped] = "") =
template warning*(cq: Conquest, args: varargs[untyped] = "") = template warning*(cq: Conquest, args: varargs[untyped] = "") =
cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}]", fgYellow, styleDim, $LOG_WARNING, resetStyle, args) cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}]", fgYellow, styleDim, $LOG_WARNING, resetStyle, args)
template input*(cq: Conquest, args: varargs[untyped] = "") =
if cq.interactAgent != nil:
cq.writeLine(fgBlue, styleBright, fmt"[{getTimestamp()}] ", fgYellow, fmt"[{cq.interactAgent.agentId}] ", resetStyle, args)
else:
cq.writeLine(fgBlue, styleBright, fmt"[{getTimestamp()}] ", resetStyle, args)
template output*(cq: Conquest, args: varargs[untyped] = "") = template output*(cq: Conquest, args: varargs[untyped] = "") =
cq.writeLine(args) cq.writeLine(args)

132
src/server/core/server.nim Normal file
View File

@@ -0,0 +1,132 @@
import prompt, terminal, argparse, parsetoml, times, json, math
import strutils, strformat, system, tables
import ./[agent, listener, builder]
import ../globals
import ../db/database
import ../core/logger
import ../../common/[types, crypto, utils, profile, event]
import ../websocket
import mummy, mummy/routers
proc header() =
echo ""
echo "┏┏┓┏┓┏┓┓┏┏┓┏╋"
echo "┗┗┛┛┗┗┫┗┻┗ ┛┗ V0.1"
echo " ┗ @jakobfriedl"
echo "".repeat(21)
echo ""
proc init*(T: type Conquest, profile: Profile): Conquest =
var cq = new Conquest
cq.listeners = initTable[string, Listener]()
cq.threads = initTable[string, Thread[Listener]]()
cq.agents = initTable[string, Agent]()
cq.interactAgent = nil
cq.profile = profile
cq.keyPair = loadKeyPair(CONQUEST_ROOT & "/" & profile.getString("private-key-file"))
cq.dbPath = CONQUEST_ROOT & "/" & profile.getString("database-file")
cq.client = nil
return cq
#[
WebSocket
]#
proc upgradeHandler(request: Request) =
{.cast(gcsafe).}:
let ws = request.upgradeToWebSocket()
cq.client = UIClient(
ws: ws
)
proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.gcsafe.} =
{.cast(gcsafe).}:
case event:
of OpenEvent:
# New client connected to team server
# Send profile, sessions and listeners to the UI client
cq.client.sendProfile(cq.profile)
for id, listener in cq.listeners:
cq.client.sendListener(listener)
for id, agent in cq.agents:
cq.client.sendAgent(agent)
cq.client.sendEventlogItem(LOG_SUCCESS_SHORT, "CQ-V1")
of MessageEvent:
# Continuously send heartbeat messages
ws.sendHeartbeat()
let event = message.recvEvent()
case event.eventType:
of CLIENT_AGENT_TASK:
let agentId = event.data["agentId"].getStr()
let task = event.data["task"].to(Task)
cq.agents[agentId].tasks.add(task)
of CLIENT_LISTENER_START:
let listener = event.data.to(UIListener)
cq.listenerStart(listener.listenerId, listener.address, listener.port, listener.protocol)
of CLIENT_LISTENER_STOP:
let listenerId = event.data["listenerId"].getStr()
cq.listenerStop(listenerId)
of CLIENT_AGENT_BUILD:
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:
discard
of CloseEvent:
# Set the client instance to nil again to prevent debug error messages
cq.client = nil
proc startServer*(profilePath: string) =
# Ensure that the conquest root directory was passed as a compile-time define
when not defined(CONQUEST_ROOT):
quit(0)
header()
try:
# Initialize framework context
# Load and parse profile
let profile = parsetoml.parseFile(profilePath)
cq = Conquest.init(profile)
cq.info("Using profile \"", profile.getString("name"), "\" (", profilePath ,").")
except CatchableError as err:
echo err.msg
quit(0)
# Initialize database
cq.dbInit()
for agent in cq.dbGetAllAgents():
cq.agents[agent.agentId] = agent
for listener in cq.dbGetAllListeners():
cq.listeners[listener.listenerId] = listener
# Restart existing listeners
for listenerId, listener in cq.listeners:
cq.listenerStart(listenerId, listener.address, listener.port, listener.protocol)
# Start websocket server
var router: Router
router.get("/*", upgradeHandler)
# Increased websocket message length in order to support dotnet assembly execution
let server = newServer(router, websocketHandler, maxMessageLen = 1024 * 1024 * 1024)
server.serve(Port(cq.profile.getInt("team-server.port")), "0.0.0.0")

View File

@@ -1,11 +1,11 @@
import terminal, parsetoml, times, json, math import terminal, parsetoml, json, math
import strutils, strformat, system, tables import strutils, strformat, system, tables
import ./core/[agent, listener, builder] import ./core/[listener, builder]
import ./globals import ./globals
import ./db/database import ./db/database
import ./core/logger import ./core/logger
import ../common/[types, crypto, utils, profile, event] import ../common/[types, crypto, profile, event]
import ./websocket import ./websocket
import mummy, mummy/routers import mummy, mummy/routers
@@ -22,7 +22,6 @@ proc init*(T: type Conquest, profile: Profile): Conquest =
cq.listeners = initTable[string, Listener]() cq.listeners = initTable[string, Listener]()
cq.threads = initTable[string, Thread[Listener]]() cq.threads = initTable[string, Thread[Listener]]()
cq.agents = initTable[string, Agent]() cq.agents = initTable[string, Agent]()
cq.interactAgent = nil
cq.profile = profile cq.profile = profile
cq.keyPair = loadKeyPair(CONQUEST_ROOT & "/" & profile.getString("private-key-file")) cq.keyPair = loadKeyPair(CONQUEST_ROOT & "/" & profile.getString("private-key-file"))
cq.dbPath = CONQUEST_ROOT & "/" & profile.getString("database-file") cq.dbPath = CONQUEST_ROOT & "/" & profile.getString("database-file")

View File

@@ -1,25 +0,0 @@
import times, json
import ../common/types
proc `%`*(agent: Agent): JsonNode =
result = newJObject()
result["agentId"] = %agent.agentId
result["listenerId"] = %agent.listenerId
result["username"] = %agent.username
result["hostname"] = %agent.hostname
result["domain"] = %agent.domain
result["ip"] = %agent.ip
result["os"] = %agent.os
result["process"] = %agent.process
result["pid"] = %agent.pid
result["elevated"] = %agent.elevated
result["sleep"] = %agent.sleep
result["firstCheckin"] = %agent.firstCheckin.toTime().toUnix()
result["latestCheckin"] = %agent.latestCheckin.toTime().toUnix()
proc `%`*(listener: Listener): JsonNode =
result = newJObject()
result["listenerId"] = %listener.listenerId
result["address"] = %listener.address
result["port"] = %listener.port
result["protocol"] = %listener.protocol

View File

@@ -1,9 +1,31 @@
import mummy import times, json, base64, parsetoml
import times, tables, json, base64, parsetoml import ../common/[types, event]
import ./utils
import ../common/[types, utils, serialize, event]
export sendHeartbeat, recvEvent export sendHeartbeat, recvEvent
proc `%`*(agent: Agent): JsonNode =
result = newJObject()
result["agentId"] = %agent.agentId
result["listenerId"] = %agent.listenerId
result["username"] = %agent.username
result["hostname"] = %agent.hostname
result["domain"] = %agent.domain
result["ip"] = %agent.ip
result["os"] = %agent.os
result["process"] = %agent.process
result["pid"] = %agent.pid
result["elevated"] = %agent.elevated
result["sleep"] = %agent.sleep
result["firstCheckin"] = %agent.firstCheckin.toTime().toUnix()
result["latestCheckin"] = %agent.latestCheckin.toTime().toUnix()
proc `%`*(listener: Listener): JsonNode =
result = newJObject()
result["listenerId"] = %listener.listenerId
result["address"] = %listener.address
result["port"] = %listener.port
result["protocol"] = %listener.protocol
#[ #[
Server -> Client Server -> Client
]# ]#