Implemented support for binary prefix/suffix.

This commit is contained in:
Jakob Friedl
2025-11-23 20:40:48 +01:00
parent fb78ae16cc
commit d4c57cf980
5 changed files with 46 additions and 14 deletions

View File

@@ -49,7 +49,13 @@ suffix = ".######################################-####"
# encoding = [
# { type = "rot", key = 5 },
# { type = "base64" }
# ]
# ]
# Example: Binary prefix (PDF header)
# placement = { type = "body" }
# encoding = { type = "xor", key = 100 }
# prefix = [0x25, 0x50, 0x44, 0x46]
# suffix = [0x25, 0x25, 0x45, 0x4F, 0x46]
# Defines arbitrary URI parameters that are added to the request
[http-get.agent.parameters]

View File

@@ -6,13 +6,16 @@
- [Team server settings](#team-server-settings)
- [GET settings](#get-settings)
- [Data transformation](#data-transformation)
- [Chaining Encodings](#chaining-encodings)
- [Binary Prefix/Suffix](#binary-prefixsuffix)
- [More Examples](#more-examples)
- [Request options](#request-options)
- [Response options](#response-options)
- [POST settings](#post-settings)
## General
Conquest supports malleable C2 profiles written using the TOML configuration language. This allows the complete customization of network traffic using data transformation, encoding and randomization. Wildcard characters `#` are replaced by a random alphanumerical character, making it possible to add even more variation to requests via randomized parameters or cookies. There is also the `$` wildcard, which is replaced by a single digit, for randomizing numeric values.
Conquest supports malleable C2 profiles written using the TOML configuration language and fully support the TOML v1.0.0 spec. This allows the complete customization of network traffic using data transformation, encoding and randomization. Wildcard characters `#` are replaced by a random alphanumerical character, making it possible to add even more variation to requests via randomized parameters or cookies. There is also the `$` wildcard, which is replaced by a single digit, for randomizing numeric values.
General settings that are defined at the beginning of the profile are the profile name and the relative location of important files, such as the team server's private key or the Conquest database.
@@ -55,8 +58,8 @@ A huge advantage of Conquest's C2 profile is the customization of where the hear
| 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. |
| prefix | STRING/ARRAY | String to prepend before the heartbeat payload. |
| suffix | STRING/ARRAY | String to append after the heartbeat payload. |
The order of operations is:
1. Encoding
@@ -80,6 +83,8 @@ suffix = ".######################################-####"
![Heartbeat in Authorization Header](../assets/profile-1.png)
#### Chaining Encodings
Multiple encodings can be applied to a packet by defining them in an array of inline-tables, as seen in the example below. The encodings are applied in the order they are defined in the profile. During the decoding of the data transformation, this order is reversed. Hence, the example below first applies the ROT encoding with the key 5 on the data and later base64-encodes it. The reversal starts with the base64-decoding and a rotation in the opposite direction.
```toml
@@ -90,7 +95,18 @@ encoding = [
]
```
#### Binary Prefix/Suffix
Instead of using strings for the prefix and suffix, it is also possible to use an array of integers to define the bytes that will be prepended/appended. Hex-formatting is supported, so something like the following can be used. This is useful to create requests that resemble binary data, such as PNGs and PDFs.
```toml
placement = { type = "body" }
encoding = { type = "xor", key = 100 }
prefix = [0x25, 0x50, 0x44, 0x46] # %PDF
suffix = [0x25, 0x25, 0x45, 0x4F, 0x46] # %%EOF
```
#### More Examples
Check the [default profile](../data/profile.toml) for more examples.

File diff suppressed because one or more lines are too long

View File

@@ -41,6 +41,7 @@ proc getStringValue*(key: TomlValueRef, default: string = ""): string =
value = randomElem.strVal
# Replace '#' with random alphanumerical character
# Replace '$' with a random digit
return value.mapIt(if it == '#': randomChar() elif it == '$': randomNumber() else: it).join("")
proc getString*(profile: Profile, path: string, default: string = ""): string =
@@ -69,6 +70,15 @@ proc isArray*(profile: Profile, path: string): bool =
let key = profile.findKey(path)
return key.kind == Array
# Retrieve string or binary prefix
proc getStringOrByteArray*(profile: Profile, path: string): string =
result = ""
if profile.isArray(path):
for element in profile.getArray(path):
result &= char(element.getInt())
else:
result = profile.getString(path)
#[
Data transformation
]#
@@ -98,16 +108,16 @@ proc applyDataTransformation*(profile: Profile, path: string, data: seq[byte]):
discard
# 2. Add prefix & suffix
let prefix = profile.getString(path & protect(".prefix"))
let suffix = profile.getString(path & protect(".suffix"))
let
prefix = profile.getStringOrByteArray(path & protect(".prefix"))
suffix = profile.getStringOrByteArray(path & protect(".suffix"))
return prefix & dataString & suffix
proc reverseDataTransformation*(profile: Profile, path: string, data: string): seq[byte] =
# 1. Remove prefix & suffix
let
prefix = profile.getString(path & protect(".prefix"))
suffix = profile.getString(path & protect(".suffix"))
prefix = profile.getStringOrByteArray(path & protect(".prefix"))
suffix = profile.getStringOrByteArray(path & protect(".suffix"))
var dataString = data[len(prefix) ..^ len(suffix) + 1]
# 2. Decoding

View File

@@ -241,17 +241,17 @@ proc getElems*(value: TomlValueRef): seq[TomlValueRef] =
continue
# Try string
let strVal = toml_array_string(arr, i.cint)
let strVal {.volatile.} = toml_array_string(arr, i.cint)
if strVal.ok:
let strPtr {.volatile.} = cast[ptr cstring](cast[int](addr strVal) + 8)[]
let strPtr = cast[ptr cstring](cast[int](addr strVal) + 8)[]
if not strPtr.isNil:
result.add(TomlValueRef(kind: String, strVal: $strPtr))
continue
# Try int
let intVal = toml_array_int(arr, i.cint)
let intVal {.volatile.} = toml_array_int(arr, i.cint)
if intVal.ok:
let intPtr {.volatile.} = cast[ptr int64](cast[int](addr intVal) + 8)[]
let intPtr = cast[ptr int64](cast[int](addr intVal) + 8)[]
result.add(TomlValueRef(kind: Int, intVal: intPtr))
proc getTableKeys*(profile: TomlTableRef, path: string): seq[tuple[key: string, value: TomlValueRef]] =