From 2f2130927e85d66d53241fa21e344ccbbdc730d5 Mon Sep 17 00:00:00 2001 From: Jakob Friedl <71284620+jakobfriedl@users.noreply.github.com> Date: Wed, 19 Nov 2025 20:42:08 +0100 Subject: [PATCH] Added ROT and XOR encoding to data transformation. --- data/profile.toml | 12 ++++++------ docs/3-PROFILE.md | 5 +++-- src/agent/nim.cfg | 4 ++-- src/common/profile.nim | 11 +++++++++-- src/common/utils.nim | 18 ++++++++++++++++++ 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/data/profile.toml b/data/profile.toml index f18a76d..baf6311 100644 --- a/data/profile.toml +++ b/data/profile.toml @@ -29,10 +29,10 @@ endpoints = [ # Metadata can be stored in a Header (e.g. JWT Token, Session Cookie), URI parameter or request body # Encoding is only applied to the payload and not the prepended or appended strings [http-get.agent.heartbeat] -placement = { type = "header", name = "Authorization" } -encoding = { type = "base64", url-safe = true } -prefix = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." -suffix = ".######################################-####" +# placement = { type = "header", name = "Authorization" } +# encoding = { type = "base64", url-safe = true } +# prefix = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +# suffix = ".######################################-####" # Example: PHP session cookie # placement = { type = "header", name = "Cookie" } @@ -45,8 +45,8 @@ suffix = ".######################################-####" # encoding = { type = "hex" } # Example: Raw data in GET request body -# placement = { type = "body" } -# encoding = { type = "none" } +placement = { type = "body" } +encoding = { type = "rot", key = 2 } # Defines arbitrary URI parameters that are added to the request [http-get.agent.parameters] diff --git a/docs/3-PROFILE.md b/docs/3-PROFILE.md index 6028b11..100eda1 100644 --- a/docs/3-PROFILE.md +++ b/docs/3-PROFILE.md @@ -50,10 +50,11 @@ A huge advantage of Conquest's C2 profile is the customization of where the hear | Name | Type | Description | | --- | --- | --- | -| placement.type | OPTION | Determine where in the request the heartbeat is placed. The following options are available: `header`, `query` and `body`| +| placement.type | OPTION | Determine where in the request the heartbeat is placed. The following options are available: `header`, `query` and `body`.| | placement.name | STRING | Name of the header/parameter to place the heartbeat in.| -| encoding.type | OPTION | Type of encoding to use. The following options are available: `base64`, `hex` and `none` (default) | +| encoding.type | OPTION | Type of encoding to use. The following options are available: `base64`, `hex`, `rot`, `xor` and `none` (default) | | encoding.url-safe | BOOL | Only used if encoding.type is set to `base64`. Uses `-` and `_` instead of `+`, `=` and `/`. Default: `false` | +| encoding.key | INTEGER | Only used if encoding.type is set to `xor` or `rot`. The `rot` setting applies a Caesar cipher, while `xor` simply XOR-encodes the data. | | prefix | STRING | String to prepend before the heartbeat payload. | | suffix | STRING | String to append after the heartbeat payload. | diff --git a/src/agent/nim.cfg b/src/agent/nim.cfg index fdcc783..5e96180 100644 --- a/src/agent/nim.cfg +++ b/src/agent/nim.cfg @@ -4,7 +4,7 @@ --opt:size --l:"-Wl,-s" # --l:"-Wl,-subsystem,windows" # Prevent console window --d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" +-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:MODULES="511" --d:VERBOSE="true" +-d:VERBOSE="false" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" \ No newline at end of file diff --git a/src/common/profile.nim b/src/common/profile.nim index c7a3303..4b334c0 100644 --- a/src/common/profile.nim +++ b/src/common/profile.nim @@ -76,14 +76,17 @@ proc getArray*(profile: Profile, path: string): seq[TomlValueRef] = return key.getElems() proc applyDataTransformation*(profile: Profile, path: string, data: seq[byte]): string = - var dataString: string - # 1. Encoding + var dataString: string case profile.getString(path & protect(".encoding.type"), default = protect("none")) of protect("base64"): dataString = encode(data, safe = profile.getBool(path & protect(".encoding.url-safe"))).replace("=", "") of protect("hex"): dataString = Bytes.toString(data).toHex().toLowerAscii() + of protect("rot"): + dataString = Bytes.toString(encodeRot(data, profile.getInt(path & ".encoding.key", default = 13))) + of protect("xor"): + dataString = Bytes.toString(xorBytes(data, profile.getInt(path & ".encoding.key", default = 1))) of protect("none"): dataString = Bytes.toString(data) @@ -106,5 +109,9 @@ proc reverseDataTransformation*(profile: Profile, path: string, data: string): s result = string.toBytes(decode(dataString)) of protect("hex"): result = string.toBytes(parseHexStr(dataString)) + of protect("rot"): + result = decodeRot(string.toBytes(dataString), profile.getInt(path & ".encoding.key", default = 13)) + of protect("xor"): + result = xorBytes(string.toBytes(dataString), profile.getInt(path & ".encoding.key", default = 1)) of protect("none"): result = string.toBytes(dataString) diff --git a/src/common/utils.nim b/src/common/utils.nim index 6d8f0f6..bb79d8f 100644 --- a/src/common/utils.nim +++ b/src/common/utils.nim @@ -38,6 +38,24 @@ macro protect*(str: untyped): untyped = # Alternate the XOR key using the FNV prime (1677619) key = (key *% 1677619) and 0x7FFFFFFF +#[ + Data encoding +]# +proc encodeRot*(data: seq[byte], key: int): seq[byte] = + result = newSeq[byte](data.len()) + for i, b in data: + result[i] = byte((int(b) + key) mod 256) + +proc decodeRot*(data: seq[byte], key: int): seq[byte] = + result = newSeq[byte](data.len()) + for i, b in data: + result[i] = byte((int(b) - key + 256) mod 256) + +proc xorBytes*(data: seq[byte], key: int): seq[byte] = + result = newSeq[byte](data.len()) + for i, b in data: + result[i] = b xor byte(key) + #[ Utility functions ]#