Added profile system to agent communication. Randomized URL endpoints/request methods and dynamic data transformation based on C2 profile. Profile is defined as compile-time string for now.

This commit is contained in:
Jakob Friedl
2025-08-15 15:42:57 +02:00
parent 5a73c0f2f4
commit c7980d219d
19 changed files with 273 additions and 184 deletions

View File

@@ -1,4 +1,4 @@
import terminal, strformat, strutils, tables, times, system, osproc, streams, base64
import terminal, strformat, strutils, tables, times, system, osproc, streams, base64, parsetoml
import ./task
import ../utils
@@ -135,13 +135,14 @@ proc agentBuild*(cq: Conquest, listener, sleep, payload: string) =
let listener = cq.listeners[listener.toUpperAscii]
# Create/overwrite nim.cfg file to set agent configuration
let agentConfigFile = fmt"../src/agent/nim.cfg"
let AgentCtxFile = fmt"../src/agent/nim.cfg"
# Parse IP Address and store as compile-time integer to hide hardcoded-strings in binary from `strings` command
let (first, second, third, fourth) = parseOctets(listener.address)
# Covert the servers's public X25519 key to as base64 string
let publicKey = encode(cq.keyPair.publicKey)
let profileString = encode(cq.profile.toTomlString())
# The following shows the format of the agent configuration file that defines compile-time variables
let config = fmt"""
@@ -154,8 +155,9 @@ proc agentBuild*(cq: Conquest, listener, sleep, payload: string) =
-d:ListenerPort={listener.port}
-d:SleepDelay={sleep}
-d:ServerPublicKey="{publicKey}"
-d:ProfileString="{profileString}"
""".replace(" ", "")
writeFile(agentConfigFile, config)
writeFile(AgentCtxFile, config)
cq.writeLine(fgBlack, styleBright, "[*] ", resetStyle, "Configuration file created.")

View File

@@ -6,7 +6,7 @@ import sugar
import ../utils
import ../api/routes
import ../db/database
import ../../common/[types, utils]
import ../../common/[types, utils, profile]
# Utility functions
proc delListener(cq: Conquest, listenerName: string) =
@@ -60,13 +60,21 @@ proc listenerStart*(cq: Conquest, host: string, portStr: string) =
# Define API endpoints based on C2 profile
# GET requests
for endpoint in cq.profile["http-get"]["endpoints"].getElems():
listener.get(endpoint.getStr(), routes.httpGet)
for endpoint in cq.profile.getArray("http-get.endpoints"):
listener.addRoute(endpoint.getStr(), routes.httpGet)
# POST requests
for endpoint in cq.profile["http-post"]["endpoints"].getElems():
listener.post(endpoint.getStr(), routes.httpPost)
var postMethods: seq[HttpMethod]
for reqMethod in cq.profile.getArray("http-post.request-methods"):
postMethods.add(parseEnum[HttpMethod](reqMethod.getStr()))
# Default method is POST
if postMethods.len == 0:
postMethods = @[HttpPost]
for endpoint in cq.profile.getArray("http-post.endpoints"):
listener.addRoute(endpoint.getStr(), routes.httpPost, postMethods)
listener.registerErrorHandler(Http404, routes.error404)
# Store listener in database
@@ -104,12 +112,20 @@ proc restartListeners*(cq: Conquest) =
# Define API endpoints based on C2 profile
# TODO: Store endpoints for already running listeners is DB (comma-separated) and use those values for restarts
# GET requests
for endpoint in cq.profile["http-get"]["endpoints"].getElems():
for endpoint in cq.profile.getArray("http-get.endpoints"):
listener.get(endpoint.getStr(), routes.httpGet)
# POST requests
for endpoint in cq.profile["http-post"]["endpoints"].getElems():
listener.post(endpoint.getStr(), routes.httpPost)
var postMethods: seq[HttpMethod]
for reqMethod in cq.profile.getArray("http-post.request-methods"):
postMethods.add(parseEnum[HttpMethod](reqMethod.getStr()))
# Default method is POST
if postMethods.len == 0:
postMethods = @[HttpPost]
for endpoint in cq.profile.getArray("http-post.endpoints"):
listener.addRoute(endpoint.getStr(), routes.httpPost, postMethods)
listener.registerErrorHandler(Http404, routes.error404)

View File

@@ -4,7 +4,7 @@ import strutils, strformat, times, system, tables
import ./[agent, listener]
import ../[globals, utils]
import ../db/database
import ../../common/[types, utils, crypto]
import ../../common/[types, utils, crypto, profile]
#[
Argument parsing
@@ -135,8 +135,8 @@ proc init*(T: type Conquest, profile: Profile): Conquest =
cq.agents = initTable[string, Agent]()
cq.interactAgent = nil
cq.keyPair = loadKeyPair(profile["private_key_file"].getStr())
cq.dbPath = profile["database_file"].getStr()
cq.keyPair = loadKeyPair(profile.getString("private_key_file"))
cq.dbPath = profile.getString("database_file")
cq.profile = profile
return cq