diff --git a/src/agent/core/context.nim b/src/agent/core/context.nim index f20320b..f23be7d 100644 --- a/src/agent/core/context.nim +++ b/src/agent/core/context.nim @@ -6,9 +6,24 @@ const CONFIGURATION {.strdefine.}: string = "" proc deserializeConfiguration(config: string): AgentCtx = var unpacker = Unpacker.init(config) + + var aesKey = unpacker.getByteArray(Key) + let + iv = unpacker.getByteArray(Iv) + authTag = unpacker.getByteArray(AuthenticationTag) + length = int(unpacker.getUint32()) + + # Decrypt profile configuration + let (decData, gmac) = decrypt(aesKey, iv, unpacker.getBytes(length)) + wipeKey(aesKey) + + if gmac != authTag: + raise newException(CatchableError, "Invalid authentication tag.") + + # Parse decrypted profile configuration + unpacker = Unpacker.init(Bytes.toString(decData)) var agentKeyPair = generateKeyPair() - var ctx = AgentCtx( agentId: generateUUID(), listenerId: Uuid.toString(unpacker.getUint32()), @@ -19,9 +34,9 @@ proc deserializeConfiguration(config: string): AgentCtx = agentPublicKey: agentKeyPair.publicKey, profile: parseString(unpacker.getDataWithLengthPrefix()) ) - - wipeKey(agentKeyPair.privateKey) + wipeKey(agentKeyPair.privateKey) + echo "[+] Profile configuration deserialized." return ctx diff --git a/src/agent/nim.cfg b/src/agent/nim.cfg index 95432bf..8bfbd28 100644 --- a/src/agent/nim.cfg +++ b/src/agent/nim.cfg @@ -1,3 +1,3 @@ # Agent configuration --d:CONFIGURATION=PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER +-d:CONFIGURATION=PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" diff --git a/src/common/crypto.nim b/src/common/crypto.nim index 71d2a74..c5aa80d 100644 --- a/src/common/crypto.nim +++ b/src/common/crypto.nim @@ -10,11 +10,17 @@ import ./[utils, types] proc generateIV*(): Iv = # Generate a random 98-bit (12-byte) initialization vector for AES-256 GCM mode var iv: Iv - if randomBytes(iv) != 12: + if randomBytes(iv) != sizeof(Iv): raise newException(CatchableError, "Failed to generate IV.") return iv -proc encrypt*(key: Key, iv: Iv, data: seq[byte], sequenceNumber: uint32): (seq[byte], AuthenticationTag) = +proc generateKey*(): Key = + var key: Key + if randomBytes(key) != sizeof(Key): + raise newException(CatchableError, "Failed to generate IV.") + return key + +proc encrypt*(key: Key, iv: Iv, data: seq[byte], sequenceNumber: uint32 = 0): (seq[byte], AuthenticationTag) = # Encrypt data using AES-256 GCM var encData = newSeq[byte](data.len) @@ -29,7 +35,7 @@ proc encrypt*(key: Key, iv: Iv, data: seq[byte], sequenceNumber: uint32): (seq[b return (encData, tag) -proc decrypt*(key: Key, iv: Iv, encData: seq[byte], sequenceNumber: uint32): (seq[byte], AuthenticationTag) = +proc decrypt*(key: Key, iv: Iv, encData: seq[byte], sequenceNumber: uint32 = 0): (seq[byte], AuthenticationTag) = # Decrypt data using AES-256 GCM var data = newSeq[byte](encData.len) @@ -91,10 +97,7 @@ proc wipeKey*(data: var openArray[byte]) = # Key pair generation proc generateKeyPair*(): KeyPair = - var privateKey: Key - if randomBytes(privateKey) != sizeof(Key): - raise newException(ValueError, "Failed to generate key.") - + let privateKey = generateKey() return KeyPair( privateKey: privateKey, publicKey: getPublicKey(privateKey) diff --git a/src/common/profile.nim b/src/common/profile.nim index b55a02e..99de27c 100644 --- a/src/common/profile.nim +++ b/src/common/profile.nim @@ -1,5 +1,6 @@ import parsetoml, strutils, sequtils, random -import ./[types, utils] + +import ./types proc findKey(profile: Profile, path: string): TomlValueRef = let keys = path.split(".") diff --git a/src/common/sequence.nim b/src/common/sequence.nim index 4d1bb3a..2db3ba2 100644 --- a/src/common/sequence.nim +++ b/src/common/sequence.nim @@ -1,5 +1,5 @@ import tables -import ./[types, utils] +import ./types var sequenceTable {.global.}: Table[uint32, uint32] diff --git a/src/common/serialize.nim b/src/common/serialize.nim index a77e312..1b6a6cc 100644 --- a/src/common/serialize.nim +++ b/src/common/serialize.nim @@ -1,5 +1,5 @@ -import streams, strutils, tables -import ./[types, utils, crypto, sequence] +import streams, tables +import ./[types, utils, crypto] #[ Packer @@ -129,8 +129,6 @@ proc getArgument*(unpacker: Unpacker): TaskArg = result.data = unpacker.getBytes(8) of BOOL: result.data = unpacker.getBytes(1) - else: - discard proc getDataWithLengthPrefix*(unpacker: Unpacker): string = # Read length of variable-length field diff --git a/src/common/types.nim b/src/common/types.nim index a4e37b5..e8cc7b6 100644 --- a/src/common/types.nim +++ b/src/common/types.nim @@ -1,7 +1,6 @@ import prompt import tables import times -import streams import parsetoml # Custom Binary Task structure diff --git a/src/common/utils.nim b/src/common/utils.nim index 3d0be6f..6569403 100644 --- a/src/common/utils.nim +++ b/src/common/utils.nim @@ -1,5 +1,4 @@ -import strutils, sequtils, strformat -import nimcrypto +import strutils, nimcrypto import ./types diff --git a/src/server/api/handlers.nim b/src/server/api/handlers.nim index 2336dd3..0b71f76 100644 --- a/src/server/api/handlers.nim +++ b/src/server/api/handlers.nim @@ -1,4 +1,4 @@ -import terminal, strformat, strutils, sequtils, tables, json, times, base64, system +import terminal, strformat, strutils, sequtils, tables, times, system import ../[utils, globals] import ../db/database @@ -44,7 +44,7 @@ proc getTasks*(heartbeat: seq[byte]): seq[seq[byte]] = listenerId = Uuid.toString(request.listenerId) timestamp = request.timestamp - var result: seq[seq[byte]] + var tasks: seq[seq[byte]] # Check if listener exists if not cq.dbListenerExists(listenerId): @@ -62,9 +62,9 @@ proc getTasks*(heartbeat: seq[byte]): seq[seq[byte]] = # Return tasks for task in cq.agents[agentId].tasks.mitems: # Iterate over agents as mutable items in order to modify GMAC tag let taskData = cq.serializeTask(task) - result.add(taskData) + tasks.add(taskData) - return result + return tasks proc handleResult*(resultData: seq[byte]) = diff --git a/src/server/api/routes.nim b/src/server/api/routes.nim index da5ebf6..642d66f 100644 --- a/src/server/api/routes.nim +++ b/src/server/api/routes.nim @@ -1,6 +1,6 @@ -import prologue, json, terminal, strformat, parsetoml, tables -import sequtils, strutils, times, base64 -import sugar +import prologue, terminal, strformat, parsetoml, tables +import strutils, times, base64 + import ./handlers import ../[utils, globals] import ../../common/[types, utils, serialize, profile] diff --git a/src/server/core/agent.nim b/src/server/core/agent.nim index 73360fd..59c3e6d 100644 --- a/src/server/core/agent.nim +++ b/src/server/core/agent.nim @@ -1,9 +1,9 @@ -import terminal, strformat, strutils, tables, times, system, osproc, streams, base64, parsetoml +import terminal, strformat, strutils, tables, times, system, parsetoml import ./task import ../utils import ../db/database -import ../../common/[types, utils] +import ../../common/types # Utility functions proc addMultiple*(cq: Conquest, agents: seq[Agent]) = diff --git a/src/server/core/builder.nim b/src/server/core/builder.nim index 65e7cf0..64d8e8c 100644 --- a/src/server/core/builder.nim +++ b/src/server/core/builder.nim @@ -1,7 +1,7 @@ import terminal, strformat, strutils, tables, system, osproc, streams, parsetoml import ../utils -import ../../common/[types, utils, profile, serialize] +import ../../common/[types, utils, profile, serialize, crypto] import ../db/database const PLACEHOLDER = "PLACEHOLDER" @@ -20,9 +20,25 @@ proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int): seq[b packer.addDataWithLengthPrefix(string.toBytes(cq.profile.toTomlString())) let data = packer.pack() - cq.writeLine(fgBlack, styleBright, "[*] ", resetStyle, "Profile configuration serialized.") + packer.reset() - return data + # Encrypt profile configuration data with a newly generated encryption key + var aesKey = generateKey() + let iv = generateIV() + + let (encData, gmac) = encrypt(aesKey, iv, data) + + # Add plaintext encryption material in front of the + packer.addData(aesKey) + packer.addData(iv) + packer.addData(gmac) + packer.add(uint32(encData.len())) + let encMaterial = packer.pack() + + wipeKey(aesKey) + + cq.writeLine(fgBlack, styleBright, "[*] ", resetStyle, "Profile configuration serialized.") + return encMaterial & encData proc compile(cq: Conquest, placeholderLength: int): string = diff --git a/src/server/core/task.nim b/src/server/core/task.nim index 8190c39..96e3b55 100644 --- a/src/server/core/task.nim +++ b/src/server/core/task.nim @@ -1,9 +1,9 @@ -import times, strformat, terminal, tables, json, sequtils, strutils +import times, strformat, terminal, tables, sequtils, strutils import ../utils import ../protocol/parser import ../../modules/manager -import ../../common/[types, utils] +import ../../common/types proc displayHelp(cq: Conquest) = cq.writeLine("Available commands:") diff --git a/src/server/globals.nim b/src/server/globals.nim index d1882f0..274e63a 100644 --- a/src/server/globals.nim +++ b/src/server/globals.nim @@ -1,4 +1,4 @@ -import ../common/[types, utils] +import ../common/types # Global variable for handling listeners, agents and console output var cq*: Conquest \ No newline at end of file diff --git a/src/server/protocol/packer.nim b/src/server/protocol/packer.nim index e888e3a..b43185b 100644 --- a/src/server/protocol/packer.nim +++ b/src/server/protocol/packer.nim @@ -1,4 +1,4 @@ -import strutils, strformat, streams, times, tables +import strutils, streams, times, tables import ../utils import ../../common/[types, utils, serialize, sequence, crypto] diff --git a/src/server/protocol/parser.nim b/src/server/protocol/parser.nim index 7117cb8..c438472 100644 --- a/src/server/protocol/parser.nim +++ b/src/server/protocol/parser.nim @@ -1,7 +1,6 @@ -import strutils, strformat, times +import strutils, times -import ../utils -import ../../common/[types, utils, sequence, crypto] +import ../../common/[types, sequence, crypto, utils] proc parseInput*(input: string): seq[string] = var i = 0 @@ -36,15 +35,15 @@ proc parseInput*(input: string): seq[string] = proc parseArgument*(argument: Argument, value: string): TaskArg = - var result: TaskArg - result.argType = cast[uint8](argument.argumentType) + var arg: TaskArg + arg.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)] + arg.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 @@ -52,26 +51,26 @@ proc parseArgument*(argument: Argument, value: string): TaskArg = let intValue = cast[uint64](parseUInt(value)) for i in 0..7: data[i] = byte((intValue shr (i * 8)) and 0xFF) - result.data = data + arg.data = data of BOOL: # Length: 1 byte if value == "true": - result.data = @[1'u8] + arg.data = @[1'u8] elif value == "false": - result.data = @[0'u8] + arg.data = @[0'u8] else: raise newException(ValueError, "Invalid value for boolean argument.") of STRING: - result.data = cast[seq[byte]](value) + arg.data = cast[seq[byte]](value) of BINARY: # Read file as binary stream discard - return result + return arg proc createTask*(cq: Conquest, command: Command, arguments: seq[string]): Task = diff --git a/src/server/utils.nim b/src/server/utils.nim index 3508758..7e16fd8 100644 --- a/src/server/utils.nim +++ b/src/server/utils.nim @@ -1,7 +1,7 @@ -import strutils, terminal, tables, sequtils, times, strformat, random, prompt +import strutils, terminal, tables, sequtils, times, strformat, prompt import std/wordwrap -import ../common/[types, utils] +import ../common/types # Utility functions proc parseOctets*(ip: string): tuple[first, second, third, fourth: int] =