Updated to TOML v1.0.0.

This commit is contained in:
Jakob Friedl
2025-11-21 15:55:41 +01:00
parent 2f2130927e
commit 6a20c25085
18 changed files with 2533 additions and 115 deletions

View File

@@ -20,7 +20,6 @@ task client, "Build conquest client binary":
requires "nim >= 2.2.4" requires "nim >= 2.2.4"
requires "parsetoml >= 0.7.2"
requires "nimcrypto >= 0.6.4" requires "nimcrypto >= 0.6.4"
requires "tiny_sqlite >= 0.2.0" requires "tiny_sqlite >= 0.2.0"
requires "winim >= 3.9.4" requires "winim >= 3.9.4"

View File

@@ -29,10 +29,10 @@ endpoints = [
# Metadata can be stored in a Header (e.g. JWT Token, Session Cookie), URI parameter 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" }
# encoding = { type = "base64", url-safe = true } encoding = { type = "base64", url-safe = true }
# prefix = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." prefix = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
# suffix = ".######################################-####" suffix = ".######################################-####"
# Example: PHP session cookie # Example: PHP session cookie
# placement = { type = "header", name = "Cookie" } # placement = { type = "header", name = "Cookie" }
@@ -45,8 +45,8 @@ endpoints = [
# encoding = { type = "hex" } # encoding = { type = "hex" }
# Example: Raw data in GET request body # Example: Raw data in GET request body
placement = { type = "body" } # placement = { type = "body" }
encoding = { type = "rot", key = 2 } # encoding = { "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]

View File

@@ -36,14 +36,14 @@ v = "###########"
# 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
[http-get.agent.headers] [http-get.agent.headers]
Host = "www.youtube.com" Host = "www.youtube.com"
Sec-Ch-Ua = "'Not.A/Brand';v='99', 'Chromium';v='136'" Sec-Ch-Ua = "\"Not.A/Brand\";v=\"99\", \"Chromium\";v=\"136\""
Sec-Ch-Ua-Mobile = "?0" Sec-Ch-Ua-Mobile = "?0"
Sec-Ch-Ua-Full-Version = "''" Sec-Ch-Ua-Full-Version = "\"\""
Sec-Ch-Ua-Arch = "''" Sec-Ch-Ua-Arch = "\"\""
Sec-Ch-Ua-Platform = "'Windows'" Sec-Ch-Ua-Platform = "\"Windows\""
Sec-Ch-Ua-Platform-Version = "''" Sec-Ch-Ua-Platform-Version = "\"\""
Sec-Ch-Ua-Model = "''" Sec-Ch-Ua-Model = "\"\""
Sec-Ch-Ua-Bitness = "''" Sec-Ch-Ua-Bitness = "\"\""
Sec-Ch-Ua-Wow64 = "?0" Sec-Ch-Ua-Wow64 = "?0"
Accept-Language = [ Accept-Language = [
"en-US,en;q=0.9", "en-US,en;q=0.9",
@@ -58,7 +58,7 @@ Sec-Fetch-User = "?1"
Sec-Fetch-Dest = "document" Sec-Fetch-Dest = "document"
Priority = "u=0, i" Priority = "u=0, i"
# Defines arbitrary headers that are added to the server's response # Defines arbitrary headers that are added to the server\"s response
[http-get.server.headers] [http-get.server.headers]
Content-Type = "text/html; charset=utf-8" Content-Type = "text/html; charset=utf-8"
X-Content-Type-Options = "nosniff" X-Content-Type-Options = "nosniff"
@@ -67,19 +67,19 @@ Pragma = "no-cache"
Expires = "Mon, 01 Jan 1990 00:00:00 GMT" Expires = "Mon, 01 Jan 1990 00:00:00 GMT"
Strict-Transport-Security = "max-age=31536000" Strict-Transport-Security = "max-age=31536000"
X-Frame-Options = "SAMEORIGIN" X-Frame-Options = "SAMEORIGIN"
Content-Security-Policy = "require-trusted-types-for 'script'" Content-Security-Policy = "require-trusted-types-for \"script\""
Server = "ESF" Server = "ESF"
X-Xss-Protection = "0" X-Xss-Protection = "0"
P3p = "CP='This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl=de for more info.'" P3p = "CP=\"This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl=de for more info.\""
Alt-Svc = "h3=':443'; ma=2592000,h3-29=':443'; ma=2592000" Alt-Svc = "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"
Set-Cookie = "__Secure-YEC=##############################################################################; Domain=.youtube.com; Expires=Mon, 07-Dec-2026 11:39:54 GMT; Path=/; Secure; HttpOnly; SameSite=lax" Set-Cookie = "__Secure-YEC=##############################################################################; Domain=.youtube.com; Expires=Mon, 07-Dec-2026 11:39:54 GMT; Path=/; Secure; HttpOnly; SameSite=lax"
# Defines how the server's response to the task retrieval request is rendered # Defines how the server"s response to the task retrieval request is rendered
[http-get.server.output] [http-get.server.output]
placement = { type = "body" } placement = { type = "body" }
encoding = { type = "base64" } encoding = { type = "base64" }
prefix = "<!DOCTYPE html><html style='font-size: 10px;font-family: Roboto, Arial, sans-serif;' lang='de-DE'><head><script data-id='_gd' nonce='iqZzTrtVB86B0KRGblxg9Q'>window.WIZ_global_data = {'HiPsbb':0,'MUE6Ne':'youtube_web','MuJWjd':false};</script><meta http-equiv='origin-trial' content='" prefix = "<!DOCTYPE html><html style=\"font-size: 10px;font-family: Roboto, Arial, sans-serif;\" lang=\"de-DE\"><head><script data-id=\"_gd\" nonce=\"iqZzTrtVB86B0KRGblxg9Q\">window.WIZ_global_data = {\"HiPsbb\":0,\"MUE6Ne\":\"youtube_web\",\"MuJWjd\":false};</script><meta http-equiv=\"origin-trial\" content=\""
suffix = "'/><script nonce='iqZzTrtVB86B0KRGblxg9Q'>var ytcfg={d:function(){return window.yt&&yt.config_||ytcfg.data_||(ytcfg.data_={})},get:function(k,o){return k in ytcfg.d()?ytcfg.d()[k]:o},set:function(){var a=arguments;if(a.length>1)ytcfg.d()[a[0]]=a[1];else{var k;for(k in a[0])ytcfg.d()[k]=a[0][k]}}};window.ytcfg.set('EMERGENCY_BASE_URL', '/error_204?" suffix = "\"/><script nonce=\"iqZzTrtVB86B0KRGblxg9Q\">var ytcfg={d:function(){return window.yt&&yt.config_||ytcfg.data_||(ytcfg.data_={})},get:function(k,o){return k in ytcfg.d()?ytcfg.d()[k]:o},set:function(){var a=arguments;if(a.length>1)ytcfg.d()[a[0]]=a[1];else{var k;for(k in a[0])ytcfg.d()[k]=a[0][k]}}};window.ytcfg.set(\"EMERGENCY_BASE_URL\", \"/error_204?"
# ---------------------------------------------------------- # ----------------------------------------------------------
# HTTP POST # HTTP POST
@@ -103,14 +103,14 @@ Referer = "https://www.youtube.com/watch?v=###########"
Content-Type = "application/json" Content-Type = "application/json"
Connection = "Keep-Alive" Connection = "Keep-Alive"
Cache-Control = "no-cache" Cache-Control = "no-cache"
Sec-Ch-Ua = "'Not.A/Brand';v='99', 'Chromium';v='136'" Sec-Ch-Ua = "\"Not.A/Brand\";v=\"99\", \"Chromium\";v=\"136\""
Sec-Ch-Ua-Mobile = "?0" Sec-Ch-Ua-Mobile = "?0"
Sec-Ch-Ua-Full-Version = "''" Sec-Ch-Ua-Full-Version = "\"\""
Sec-Ch-Ua-Arch = "''" Sec-Ch-Ua-Arch = "\"\""
Sec-Ch-Ua-Platform = "'Windows'" Sec-Ch-Ua-Platform = "\"Windows\""
Sec-Ch-Ua-Platform-Version = "''" Sec-Ch-Ua-Platform-Version = "\"\""
Sec-Ch-Ua-Model = "''" Sec-Ch-Ua-Model = "\"\""
Sec-Ch-Ua-Bitness = "''" Sec-Ch-Ua-Bitness = "\"\""
Sec-Ch-Ua-Wow64 = "?0" Sec-Ch-Ua-Wow64 = "?0"
Cookie = "YSC=###########; SOCS=##############################################; VISITOR_PRIVACY_METADATA=##################################################################; __Secure-1PSIDTS=sidts-#######_##########################################_#########################; __Secure-3PSIDTS=sidts-#######_##########################################_#########################; HSID=####################;" Cookie = "YSC=###########; SOCS=##############################################; VISITOR_PRIVACY_METADATA=##################################################################; __Secure-1PSIDTS=sidts-#######_##########################################_#########################; __Secure-3PSIDTS=sidts-#######_##########################################_#########################; HSID=####################;"
@@ -123,8 +123,8 @@ pretty-print = [
[http-post.agent.output] [http-post.agent.output]
placement = { type = "body" } placement = { type = "body" }
encoding = { type = "base64", url-safe = true } encoding = { type = "base64", url-safe = true }
prefix = "{'context':{'client':{'hl':'de','gl':'AT','remoteHost':'$$.1$$.$$.1$$','deviceMake':'','deviceModel':'','visitorData':'Cgt1M016MzRrZmhTUSj12MbIBjInCgJBVBIhEh0SGwsMDg8QERITFBUWFxgZGhscHR4fICEiIyQlJiBe','userAgent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36,gzip(gfe)','clientName':'WEB','clientVersion':'2.20251107.01.00','osName':'Windows','osVersion':'10.0','originalUrl':'https://www.youtube.com/','screenPixelDensity':2,'platform':'DESKTOP','clientFormFactor':'UNKNOWN_FORM_FACTOR','configInfo':{'appInstallData':'" prefix = "{\"context\":{\"client\":{\"hl\":\"de\",\"gl\":\"AT\",\"remoteHost\":\"$$.1$$.$$.1$$\",\"deviceMake\":\"\",\"deviceModel\":\"\",\"visitorData\":\"Cgt1M016MzRrZmhTUSj12MbIBjInCgJBVBIhEh0SGwsMDg8QERITFBUWFxgZGhscHR4fICEiIyQlJiBe\",\"userAgent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36,gzip(gfe)\",\"clientName\":\"WEB\",\"clientVersion\":\"2.20251107.01.00\",\"osName\":\"Windows\",\"osVersion\":\"10.0\",\"originalUrl\":\"https://www.youtube.com/\",\"screenPixelDensity\":2,\"platform\":\"DESKTOP\",\"clientFormFactor\":\"UNKNOWN_FORM_FACTOR\",\"configInfo\":{\"appInstallData\":\""
suffix = "'},'screenDensityFloat':1.5,'userInterfaceTheme':'USER_INTERFACE_THEME_DARK','timeZone':'Europe/Vienna','browserName':'Chrome','browserVersion':'142.0.0.0','acceptHeader':'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7','deviceExperimentId':'ChxOelUzTVRBeU1qQTJPVEV4TkRFNU5qUXhOQT09EPXYxsgGGPXYxsgG','rolloutToken':'CJu4u9qz64jjcxCr8dad-t-QAxjzyIbunueQAw%3D%3D','screenWidthPoints':1920,'screenHeightPoints':1065,'utcOffsetMinutes':60,'connectionType':'CONN_CELLULAR_3G','memoryTotalKbytes':'8000000','mainAppWebInfo':{'graftUrl':'https://www.youtube.com/watch?v=###########&list=RD4WIMyqBG9gs&start_radio=1','pwaInstallabilityStatus':'PWA_INSTALLABILITY_STATUS_UNKNOWN','webDisplayMode':'WEB_DISPLAY_MODE_BROWSER','isWebNativeShareAvailable':true}},'user':{'lockedSafetyMode':false},'request':{'useSsl':true,'internalExperimentFlags':[],'consistencyTokenJars':[]},'clickTracking':{'clickTrackingParams':'CJgFEKVBIhMIucGi957nkAMVneRJBx3cFhscygEErMFOaw=='},'adSignalsInfo':{'params':[{'key':'dt','value':'1762765953510'},{'key':'flash','value':'0'},{'key':'frm','value':'0'},{'key':'u_tz','value':'60'},{'key':'u_his','value':'4'},{'key':'u_h','value':'1200'},{'key':'u_w','value':'1920'},{'key':'u_ah','value':'1152'},{'key':'u_aw','value':'1920'},{'key':'u_cd','value':'24'},{'key':'bc','value':'31'},{'key':'bih','value':'1065'},{'key':'biw','value':'1905'},{'key':'brdim','value':'0,0,0,0,1920,0,1920,1152,1920,1065'},{'key':'vis','value':'1'},{'key':'wgl','value':'true'},{'key':'ca_type','value':'image'}],'bid':'ANyPxKqp2RGW0TLEXMjNbBRm6ZPDYteE8iHnYK0DaJMOiTEHrbqefZtn6qfK_MhA2-ZgnoosEwKaN8pi77jJRptRzz5Rsm-P_w'}},'target':{'videoId':'###########'},'params':'Cg0KCzRXSU15cUJHOWdzIAAyDAiJ2cbIBhCm6ueLAQ%3D%3D'}" suffix = "\"},\"screenDensityFloat\":1.5,\"userInterfaceTheme\":\"USER_INTERFACE_THEME_DARK\",\"timeZone\":\"Europe/Vienna\",\"browserName\":\"Chrome\",\"browserVersion\":\"142.0.0.0\",\"acceptHeader\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\",\"deviceExperimentId\":\"ChxOelUzTVRBeU1qQTJPVEV4TkRFNU5qUXhOQT09EPXYxsgGGPXYxsgG\",\"rolloutToken\":\"CJu4u9qz64jjcxCr8dad-t-QAxjzyIbunueQAw%3D%3D\",\"screenWidthPoints\":1920,\"screenHeightPoints\":1065,\"utcOffsetMinutes\":60,\"connectionType\":\"CONN_CELLULAR_3G\",\"memoryTotalKbytes\":\"8000000\",\"mainAppWebInfo\":{\"graftUrl\":\"https://www.youtube.com/watch?v=###########&list=RD4WIMyqBG9gs&start_radio=1\",\"pwaInstallabilityStatus\":\"PWA_INSTALLABILITY_STATUS_UNKNOWN\",\"webDisplayMode\":\"WEB_DISPLAY_MODE_BROWSER\",\"isWebNativeShareAvailable\":true}},\"user\":{\"lockedSafetyMode\":false},\"request\":{\"useSsl\":true,\"internalExperimentFlags\":[],\"consistencyTokenJars\":[]},\"clickTracking\":{\"clickTrackingParams\":\"CJgFEKVBIhMIucGi957nkAMVneRJBx3cFhscygEErMFOaw==\"},\"adSignalsInfo\":{\"params\":[{\"key\":\"dt\",\"value\":\"1762765953510\"},{\"key\":\"flash\",\"value\":\"0\"},{\"key\":\"frm\",\"value\":\"0\"},{\"key\":\"u_tz\",\"value\":\"60\"},{\"key\":\"u_his\",\"value\":\"4\"},{\"key\":\"u_h\",\"value\":\"1200\"},{\"key\":\"u_w\",\"value\":\"1920\"},{\"key\":\"u_ah\",\"value\":\"1152\"},{\"key\":\"u_aw\",\"value\":\"1920\"},{\"key\":\"u_cd\",\"value\":\"24\"},{\"key\":\"bc\",\"value\":\"31\"},{\"key\":\"bih\",\"value\":\"1065\"},{\"key\":\"biw\",\"value\":\"1905\"},{\"key\":\"brdim\",\"value\":\"0,0,0,0,1920,0,1920,1152,1920,1065\"},{\"key\":\"vis\",\"value\":\"1\"},{\"key\":\"wgl\",\"value\":\"true\"},{\"key\":\"ca_type\",\"value\":\"image\"}],\"bid\":\"ANyPxKqp2RGW0TLEXMjNbBRm6ZPDYteE8iHnYK0DaJMOiTEHrbqefZtn6qfK_MhA2-ZgnoosEwKaN8pi77jJRptRzz5Rsm-P_w\"}},\"target\":{\"videoId\":\"###########\"},\"params\":\"Cg0KCzRXSU15cUJHOWdzIAAyDAiJ2cbIBhCm6ueLAQ%3D%3D\"}"
[http-post.server.headers] [http-post.server.headers]
Content-Type = "application/json; charset=utf-8" Content-Type = "application/json; charset=utf-8"
@@ -135,7 +135,7 @@ Expires = "Mon, 01 Jan 1990 00:00:00 GMT"
Server = "ESF" Server = "ESF"
X-Xss-Protection = "0" X-Xss-Protection = "0"
Strict-Transport-Security = "max-age=31536000" Strict-Transport-Security = "max-age=31536000"
Alt-Svc = "h3=':443'; ma=2592000,h3-29=':443'; ma=2592000" Alt-Svc = "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"
[http-post.server.output] [http-post.server.output]
body = "{'responseContext': {}}" body = "{\"responseContext\": {}}"

View File

@@ -1,6 +1,5 @@
import parsetoml
import ../utils/io import ../utils/io
import ../../common/[types, utils, crypto, serialize] import ../../common/[types, utils, crypto, profile, serialize]
const CONFIGURATION {.strdefine.}: string = "" const CONFIGURATION {.strdefine.}: string = ""

View File

@@ -1,4 +1,4 @@
import httpclient, strformat, strutils, asyncdispatch, base64, tables, parsetoml, random import httpclient, strformat, strutils, asyncdispatch, base64, tables, random
import ../utils/io import ../utils/io
import ../../common/[types, utils, profile] import ../../common/[types, utils, profile]
@@ -11,8 +11,8 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
var body: string = "" var body: string = ""
# Define request headers, as defined in profile # Define request headers, as defined in profile
for header, value in ctx.profile.getTable(protect("http-get.agent.headers")): for header in ctx.profile.getTableKeys(protect("http-get.agent.headers")):
client.headers.add(header, value.getStringValue()) client.headers.add(header.key, 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.getString(protect("http-get.endpoints")) var endpoint = ctx.profile.getString(protect("http-get.endpoints"))
@@ -32,8 +32,8 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
discard discard
# Define additional request parameters # Define additional request parameters
for param, value in ctx.profile.getTable(protect("http-get.agent.parameters")): for param in ctx.profile.getTableKeys(protect("http-get.agent.parameters")):
endpoint &= fmt"{param}={value.getStringValue()}&" endpoint &= fmt"{param.key}={param.value.getStringValue()}&"
try: try:
# Retrieve binary task data from listener and convert it to seq[bytes] for deserialization # Retrieve binary task data from listener and convert it to seq[bytes] for deserialization
@@ -68,8 +68,8 @@ proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} =
let client = newAsyncHttpClient(userAgent = ctx.profile.getString(protect("http-post.user-agent"))) let client = newAsyncHttpClient(userAgent = ctx.profile.getString(protect("http-post.user-agent")))
# Define request headers, as defined in profile # Define request headers, as defined in profile
for header, value in ctx.profile.getTable(protect("http-post.agent.headers")): for header in ctx.profile.getTableKeys(protect("http-post.agent.headers")):
client.headers.add(header, value.getStringValue()) client.headers.add(header.key, 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.getString(protect("http-post.endpoints")) var endpoint = ctx.profile.getString(protect("http-post.endpoints"))
@@ -95,8 +95,8 @@ proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} =
discard discard
# Define additional request parameters # Define additional request parameters
for param, value in ctx.profile.getTable(protect("http-post.agent.parameters")): for param in ctx.profile.getTableKeys(protect("http-post.agent.parameters")):
endpoint &= fmt"{param}={value.getStringValue()}&" endpoint &= fmt"{param.key}={param.value.getStringValue()}&"
try: try:
# Send post request to team server # Send post request to team server

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,10 @@
import whisky import whisky
import tables, times, strutils, strformat, json, parsetoml, base64, native_dialogs import tables, times, strutils, strformat, json, base64, native_dialogs
import ./utils/[appImGui, globals] import ./utils/[appImGui, globals]
import ./views/[dockspace, sessions, listeners, eventlog, console] import ./views/[dockspace, sessions, listeners, eventlog, console]
import ./views/loot/[screenshots, downloads] import ./views/loot/[screenshots, downloads]
import ./views/modals/generatePayload import ./views/modals/generatePayload
import ../common/[types, utils, crypto] import ../common/[types, utils, profile, crypto]
import ./core/websocket import ./core/websocket
proc main(ip: string = "localhost", port: int = 37573) = proc main(ip: string = "localhost", port: int = 37573) =
@@ -85,7 +85,7 @@ proc main(ip: string = "localhost", port: int = 37573) =
wipeKey(clientKeyPair.privateKey) wipeKey(clientKeyPair.privateKey)
of CLIENT_PROFILE: of CLIENT_PROFILE:
profile = parsetoml.parseString(event.data["profile"].getStr()) profile = parseString(event.data["profile"].getStr())
of CLIENT_LISTENER_ADD: of CLIENT_LISTENER_ADD:
let listener = event.data.to(UIListener) let listener = event.data.to(UIListener)

View File

@@ -57,7 +57,7 @@ proc validateDecryption*(key: Key, iv: Iv, encData: seq[byte], sequenceNumber: u
Elliptic curve cryptography ensures that the actual session key is never sent over the network Elliptic curve cryptography ensures that the actual session key is never sent over the network
Private keys and shared secrets are wiped from agent memory as soon as possible Private keys and shared secrets are wiped from agent memory as soon as possible
]# ]#
{.compile: "monocypher/monocypher.c".} {.compile: protect("monocypher/monocypher.c").}
# C function imports from (monocypher/monocypher.c) # C function imports from (monocypher/monocypher.c)
proc crypto_x25519*(shared_secret: ptr byte, your_secret_key: ptr byte, their_public_key: ptr byte) {.importc, cdecl.} proc crypto_x25519*(shared_secret: ptr byte, your_secret_key: ptr byte, their_public_key: ptr byte) {.importc, cdecl.}

View File

@@ -1,25 +1,17 @@
import parsetoml, strutils, sequtils, random, base64 import strutils, sequtils, random, base64
import ./[types, utils] import ./[types, utils]
import ./toml/toml
proc findKey(profile: Profile, path: string): TomlValueRef = export parseFile, parseString, free, getTableKeys, getRandom
let keys = path.split(".")
let target = keys[keys.high]
var current = profile
for i in 0 ..< keys.high:
let temp = current.getOrDefault(keys[i])
if temp == nil:
return nil
current = temp
return current.getOrDefault(target)
# 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 # '#' characters represent wildcard characters and are replaced with a random alphanumerical character (a-zA-Z0-9)
# '$' characters are replaced with a random number (0-9)
#[
Helper functions
]#
proc randomChar(): char = proc randomChar(): char =
let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return alphabet[rand(alphabet.len - 1)] return alphabet[rand(alphabet.len - 1)]
@@ -33,48 +25,53 @@ proc getRandom*(values: seq[TomlValueRef]): TomlValueRef =
return nil return nil
return values[rand(values.len - 1)] return values[rand(values.len - 1)]
#[
Wrapper functions
]#
proc getStringValue*(key: TomlValueRef, default: string = ""): string = proc getStringValue*(key: TomlValueRef, default: string = ""): string =
# In some cases, the profile can define multiple values for a key, e.g. for HTTP headers if key.isNil or key.kind == None:
# A random entry is selected from these specifications return default
var value: string = ""
if key.kind == TomlValueKind.String:
value = key.getStr(default)
elif key.kind == TomlValueKind.Array:
value = key.getElems().getRandom().getStr(default)
# Replace '#' with a random alphanumerical character and return the resulting string var value: string = ""
if key.kind == String:
value = key.strVal
elif key.kind == Array:
let randomElem = getRandom(key.arrayVal)
if randomElem != nil and randomElem.kind == String:
value = randomElem.strVal
# Replace '#' with random alphanumerical character
return value.mapIt(if it == '#': randomChar() elif it == '$': randomNumber() else: it).join("") return value.mapIt(if it == '#': randomChar() elif it == '$': randomNumber() 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:
return default
return key.getStringValue(default) return key.getStringValue(default)
proc getBool*(profile: Profile, path: string, default: bool = false): bool = proc getInt*(profile: Profile, path: string, default: int = 0): int =
let key = profile.findKey(path) let key = profile.findKey(path)
if key == nil:
return default
return key.getBool(default)
proc getInt*(profile: Profile, path: string, default = 0): int =
let key = profile.findKey(path)
if key == nil:
return default
return key.getInt(default) return key.getInt(default)
proc getTable*(profile: Profile, path: string): TomlTableRef = proc getBool*(profile: Profile, path: string, default: bool = false): bool =
let key = profile.findKey(path)
return key.getBool(default)
proc getTable*(profile: Profile, path: string): TomlTableRef =
let key = profile.findKey(path) let key = profile.findKey(path)
if key == nil:
return new TomlTableRef
return key.getTable() return key.getTable()
proc getArray*(profile: Profile, path: string): seq[TomlValueRef] = proc getArray*(profile: Profile, path: string): seq[TomlValueRef] =
let key = profile.findKey(path) let key = profile.findKey(path)
if key == nil: if key.kind != Array:
return @[] return @[]
return key.getElems() return key.getElems()
proc isArray*(profile: Profile, path: string): bool =
let key = profile.findKey(path)
return key.kind == Array
#[
Data transformation
]#
proc applyDataTransformation*(profile: Profile, path: string, data: seq[byte]): string = proc applyDataTransformation*(profile: Profile, path: string, data: seq[byte]): string =
# 1. Encoding # 1. Encoding
var dataString: string var dataString: string

1983
src/common/toml/toml.c Normal file

File diff suppressed because it is too large Load Diff

137
src/common/toml/toml.h Normal file
View File

@@ -0,0 +1,137 @@
#ifndef TOML_H
#define TOML_H
#ifdef _MSC_VER
# pragma warning(disable : 4996)
#endif
#ifdef __cplusplus
# define TOML_EXTERN extern "C"
#else
# define TOML_EXTERN extern
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
typedef struct toml_table_t toml_table_t;
typedef struct toml_array_t toml_array_t;
typedef struct toml_value_t toml_value_t;
typedef struct toml_timestamp_t toml_timestamp_t;
typedef struct toml_keyval_t toml_keyval_t;
typedef struct toml_arritem_t toml_arritem_t;
typedef struct toml_pos_t toml_pos_t;
// TOML table.
struct toml_table_t {
const char* key; // Key for this table
int keylen; // length of key.
bool implicit; // Table was created implicitly
bool readonly; // No more modification allowed
int nkval; // key-values in the table
toml_keyval_t** kval;
int narr; // arrays in the table
toml_array_t** arr;
int ntbl; // tables in the table
toml_table_t** tbl;
};
// TOML array.
struct toml_array_t {
const char* key; // key to this array
int keylen; // length of key.
int kind; // element kind: 'v'alue, 'a'rray, or 't'able, 'm'ixed
int type; // for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp, 'm'ixed
int nitem; // number of elements
toml_arritem_t* item;
};
struct toml_arritem_t {
int valtype; // for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp
char* val;
toml_array_t* arr;
toml_table_t* tbl;
};
// TOML key/value pair.
struct toml_keyval_t {
const char* key; // key to this value
int keylen; // length of key.
const char* val; // the raw value
};
// Token position.
struct toml_pos_t {
int line;
int col;
};
// Timestamp type; some values may be empty depending on the value of kind.
struct toml_timestamp_t {
// datetime type:
//
// 'd'atetime Full date + time + TZ
// 'l'local-datetime Full date + time but without TZ
// 'D'ate-local Date only, without TZ
// 't'ime-local Time only, without TZ
char kind;
int year, month, day;
int hour, minute, second, millisec;
int tz; // Timezone offset in minutes
};
// Parsed TOML value.
//
// The string value s is a regular NULL-terminated C string, but the string
// length is also given in sl since TOML values may contain NULL bytes. The
// value is guaranteed to be correct UTF-8.
struct toml_value_t {
bool ok; // Was this value present?
union {
struct {
char* s; // string value; must be freed after use.
int sl; // string length, excluding NULL.
};
toml_timestamp_t ts; // datetime
bool b; // bool
int64_t i; // int
double d; // double
} u;
};
// toml_parse() parses a TOML document from a string. Returns 0 on error, with
// the error message stored in errbuf.
//
// toml_parse_file() is identical, but reads from a file descriptor.
//
// Use toml_free() to free the return value; this will invalidate all handles
// for this table.
TOML_EXTERN toml_table_t* toml_parse(char* toml, char* errbuf, int errbufsz);
TOML_EXTERN toml_table_t* toml_parse_file(FILE* fp, char* errbuf, int errbufsz);
TOML_EXTERN void toml_free(toml_table_t* table);
// Table functions.
//
// toml_table_len() gets the number of direct keys for this table;
// toml_table_key() gets the nth direct key in this table.
TOML_EXTERN int toml_table_len(const toml_table_t* table);
TOML_EXTERN const char* toml_table_key(const toml_table_t* table, int keyidx, int* keylen);
TOML_EXTERN toml_value_t toml_table_string(const toml_table_t* table, const char* key);
TOML_EXTERN toml_value_t toml_table_bool(const toml_table_t* table, const char* key);
TOML_EXTERN toml_value_t toml_table_int(const toml_table_t* table, const char* key);
TOML_EXTERN toml_value_t toml_table_double(const toml_table_t* table, const char* key);
TOML_EXTERN toml_value_t toml_table_timestamp(const toml_table_t* table, const char* key);
TOML_EXTERN toml_array_t* toml_table_array(const toml_table_t* table, const char* key);
TOML_EXTERN toml_table_t* toml_table_table(const toml_table_t* table, const char* key);
// Array functions.
TOML_EXTERN int toml_array_len(const toml_array_t* array);
TOML_EXTERN toml_value_t toml_array_string(const toml_array_t* array, int idx);
TOML_EXTERN toml_value_t toml_array_bool(const toml_array_t* array, int idx);
TOML_EXTERN toml_value_t toml_array_int(const toml_array_t* array, int idx);
TOML_EXTERN toml_value_t toml_array_double(const toml_array_t* array, int idx);
TOML_EXTERN toml_value_t toml_array_timestamp(const toml_array_t* array, int idx);
TOML_EXTERN toml_array_t* toml_array_array(const toml_array_t* array, int idx);
TOML_EXTERN toml_table_t* toml_array_table(const toml_array_t* array, int idx);
#endif // TOML_H

301
src/common/toml/toml.nim Normal file
View File

@@ -0,0 +1,301 @@
import random, strutils
# Wrapper for the toml-c library
# Original: github.com/arp242/toml-c/
{.compile: "toml.c".}
type
TomlKeyVal = object
key: cstring
keylen: cint
val: cstring
TomlArrItem = object
valtype: cint
val: cstring
arr: ptr TomlArray
tbl: ptr TomlTable
TomlTable = object
key: cstring
keylen: cint
implicit: bool
readonly: bool
nkval: cint
kval: ptr ptr TomlKeyVal
narr: cint
arr: ptr ptr TomlArray
ntbl: cint
tbl: ptr ptr TomlTable
TomlArray = object
key: cstring
keylen: cint
kind: cint
`type`: cint
nitem: cint
item: ptr TomlArrItem
TomlValue = object
case ok: bool
of false: discard
of true:
s: cstring
sl: cint
TomlTableRef* = ptr TomlTable
TomlValueKind* = enum
String, Int, Bool, Float, Table, Array, None
TomlValueRef* = ref object
case kind*: TomlValueKind
of String:
strVal*: string
of Int:
intVal*: int64
of Bool:
boolVal*: bool
of Float:
floatVal*: float64
of Table:
tableVal*: TomlTableRef
of Array:
arrayVal*: ptr TomlArray
of None:
discard
# C library functions
proc toml_parse(toml: cstring, errbuf: cstring, errbufsz: cint): TomlTableRef {.importc, cdecl.}
proc toml_parse_file(fp: File, errbuf: cstring, errbufsz: cint): TomlTableRef {.importc, cdecl.}
proc toml_free(tab: TomlTableRef) {.importc, cdecl.}
proc toml_table_len(tab: TomlTableRef): cint {.importc, cdecl.}
proc toml_table_key(tab: TomlTableRef, keyidx: cint, keylen: ptr cint): cstring {.importc, cdecl.}
proc toml_table_string(tab: TomlTableRef, key: cstring): TomlValue {.importc, cdecl.}
proc toml_table_int(tab: TomlTableRef, key: cstring): TomlValue {.importc, cdecl.}
proc toml_table_bool(tab: TomlTableRef, key: cstring): TomlValue {.importc, cdecl.}
proc toml_table_double(tab: TomlTableRef, key: cstring): TomlValue {.importc, cdecl.}
proc toml_table_array(tab: TomlTableRef, key: cstring): ptr TomlArray {.importc, cdecl.}
proc toml_table_table(tab: TomlTableRef, key: cstring): TomlTableRef {.importc, cdecl.}
proc toml_array_len(arr: ptr TomlArray): cint {.importc, cdecl.}
proc toml_array_table(arr: ptr TomlArray, idx: cint): TomlTableRef {.importc, cdecl.}
proc toml_array_string(arr: ptr TomlArray, idx: cint): TomlValue {.importc, cdecl.}
proc toml_array_int(arr: ptr TomlArray, idx: cint): TomlValue {.importc, cdecl.}
#[
Retrieve a random element from a TOML array
]#
proc getRandom*(arr: ptr TomlArray): TomlValueRef =
if arr.isNil:
return nil
let n = toml_array_len(arr)
if n == 0:
return nil
let idx = rand(n.int - 1)
# String
let strVal {.volatile.} = toml_array_string(arr, idx.cint)
if strVal.ok:
let strPtr = cast[ptr cstring](cast[int](addr strVal) + 8)[]
if not strPtr.isNil:
return TomlValueRef(kind: String, strVal: $strPtr)
# Table
let table {.volatile.} = toml_array_table(arr, idx.cint)
if not table.isNil:
return TomlValueRef(kind: Table, tableVal: table)
# Int
let intVal {.volatile.} = toml_array_int(arr, idx.cint)
if intVal.ok:
let intPtr = cast[ptr int64](cast[int](addr intVal) + 8)[]
return TomlValueRef(kind: Int, intVal: intPtr)
return nil
#[
Parse TOML string or configuration file
]#
proc parseString*(toml: string): TomlTableRef =
var errbuf: array[200, char]
var tomlCopy = toml
result = toml_parse(tomlCopy.cstring, cast[cstring](addr errbuf[0]), 200)
if result.isNil:
raise newException(ValueError, "TOML parse error: " & $cast[cstring](addr errbuf[0]))
proc parseFile*(path: string): TomlTableRef =
var errbuf: array[200, char]
let fp = open(path, fmRead)
if fp.isNil:
raise newException(IOError, "Cannot open file: " & path)
result = toml_parse_file(fp, cast[cstring](addr errbuf[0]), 200)
fp.close()
if result.isNil:
raise newException(ValueError, "TOML parse error: " & $cast[cstring](addr errbuf[0]))
proc free*(table: TomlTableRef) =
if not table.isNil:
toml_free(table)
#[
Takes a specific "."-separated path as input and returns the TOML Value that it finds
]#
proc findKey*(profile: TomlTableRef, path: string): TomlValueRef =
if profile.isNil:
return TomlValueRef(kind: None)
let keys = path.split(".")
var current = profile
# Navigate through nested tables
for i in 0 ..< keys.len - 1:
let nextTable = toml_table_table(current, keys[i].cstring)
if nextTable.isNil:
return TomlValueRef(kind: None)
current = nextTable
let finalKey = keys[^1].cstring
# Try different types
# {.volatile.} is added to avoid dangling pointers
block findStr:
let val {.volatile.} = toml_table_string(current, finalKey)
if val.ok:
let strPtr = cast[ptr cstring](cast[int](addr val) + 8)[]
if not strPtr.isNil:
return TomlValueRef(kind: String, strVal: $strPtr)
block checkInt:
let val {.volatile.} = toml_table_int(current, finalKey)
if val.ok:
let intPtr = cast[ptr int64](cast[int](addr val) + 8)[]
return TomlValueRef(kind: Int, intVal: intPtr)
block checkBool:
let val {.volatile.} = toml_table_bool(current, finalKey)
if val.ok:
let boolPtr = cast[ptr bool](cast[int](addr val) + 8)[]
return TomlValueRef(kind: Bool, boolVal: boolPtr)
block checkDouble:
let val {.volatile.} = toml_table_double(current, finalKey)
if val.ok:
let dblPtr = cast[ptr float64](cast[int](addr val) + 8)[]
return TomlValueRef(kind: Float, floatVal: dblPtr)
block checkArray:
let arr {.volatile.} = toml_table_array(current, finalKey)
if not arr.isNil:
return TomlValueRef(kind: Array, arrayVal: arr)
block checkTable:
let table {.volatile.} = toml_table_table(current, finalKey)
if not table.isNil:
return TomlValueRef(kind: Table, tableVal: table)
return TomlValueRef(kind: None)
#[
Retrieve the actual value from a TOML value
]#
proc getStr*(value: TomlValueRef, default: string = ""): string =
if value.kind == String:
return value.strVal
return default
proc getInt*(value: TomlValueRef, default: int = 0): int =
if value.kind == Int:
return value.intVal.int
return default
proc getBool*(value: TomlValueRef, default: bool = false): bool =
if value.kind == Bool:
return value.boolVal
return default
proc getTable*(value: TomlValueRef): TomlTableRef =
if value.kind == Table:
return value.tableVal
return nil
proc getElems*(value: TomlValueRef): seq[TomlValueRef] =
if value.kind != Array:
return @[]
let arr = value.arrayVal
let n = toml_array_len(arr)
result = @[]
for i in 0 ..< n:
# Try table first
let table {.volatile.} = toml_array_table(arr, i.cint)
if not table.isNil:
result.add(TomlValueRef(kind: Table, tableVal: table))
continue
# Try string
let strVal = toml_array_string(arr, i.cint)
if strVal.ok:
let strPtr {.volatile.} = cast[ptr cstring](cast[int](addr strVal) + 8)[]
if not strPtr.isNil:
result.add(TomlValueRef(kind: String, strVal: $strPtr))
continue
# Try int
let intVal = toml_array_int(arr, i.cint)
if intVal.ok:
let intPtr {.volatile.} = cast[ptr int64](cast[int](addr intVal) + 8)[]
result.add(TomlValueRef(kind: Int, intVal: intPtr))
proc getTableKeys*(profile: TomlTableRef, path: string): seq[tuple[key: string, value: TomlValueRef]] =
result = @[]
let key = profile.findKey(path)
let table = key.getTable()
if table.isNil:
return
let numKeys = toml_table_len(table)
for i in 0 ..< numKeys:
var keylen: cint
let keyPtr = toml_table_key(table, i.cint, addr keylen)
if keyPtr.isNil:
continue
let key = $keyPtr
let value = profile.findKey(path & "." & key)
if value.kind != None:
result.add((key: key, value: value))
proc getTableValue*(table: TomlTableRef, key: string): TomlValueRef =
if table.isNil:
return TomlValueRef(kind: None)
let ckey = key.cstring
block checkString:
let val {.volatile.} = toml_table_string(table, ckey)
if val.ok:
let strPtr = cast[ptr cstring](cast[int](addr val) + 8)[]
if not strPtr.isNil:
return TomlValueRef(kind: String, strVal: $strPtr)
block checkInt:
let val {.volatile.} = toml_table_int(table, ckey)
if val.ok:
let intPtr = cast[ptr int64](cast[int](addr val) + 8)[]
return TomlValueRef(kind: Int, intVal: intPtr)
block checkBool:
let val {.volatile.} = toml_table_bool(table, ckey)
if val.ok:
let boolPtr = cast[ptr bool](cast[int](addr val) + 8)[]
return TomlValueRef(kind: Bool, boolVal: boolPtr)
return TomlValueRef(kind: None)

View File

@@ -1,10 +1,12 @@
import tables import tables
import parsetoml, json import json
import system import system
import mummy import mummy
when defined(client): when defined(client):
import whisky import whisky
import ./toml/toml
# Custom Binary Task structure # Custom Binary Task structure
const const
MAGIC* = 0x514E3043'u32 # Magic value: C0NQ MAGIC* = 0x514E3043'u32 # Magic value: C0NQ
@@ -285,7 +287,7 @@ type
privateKey*: Key privateKey*: Key
publicKey*: Key publicKey*: Key
Profile* = TomlValueRef Profile* = TomlTableRef
WsConnection* = ref object WsConnection* = ref object
when defined(server): when defined(server):
@@ -300,6 +302,7 @@ type
threads*: Table[string, Thread[Listener]] threads*: Table[string, Thread[Listener]]
agents*: Table[string, Agent] agents*: Table[string, Agent]
keyPair*: KeyPair keyPair*: KeyPair
profileString*: string
profile*: Profile profile*: Profile
client*: WsConnection client*: WsConnection

View File

@@ -1,5 +1,5 @@
import mummy, terminal, parsetoml, tables import mummy, terminal
import strutils, strformat, base64 import strutils, strformat
import ./handlers import ./handlers
import ../globals import ../globals
@@ -81,8 +81,8 @@ proc httpGet*(request: Request) =
# Add headers, as defined in the team server profile # Add headers, as defined in the team server profile
var headers: HttpHeaders var headers: HttpHeaders
for header, value in cq.profile.getTable("http-get.server.headers"): for header in cq.profile.getTableKeys("http-get.server.headers"):
headers.add((header, value.getStringValue())) headers.add((header.key, header.value.getStringValue()))
request.respond(200, headers = headers, body = payload) request.respond(200, headers = headers, body = payload)
@@ -129,8 +129,8 @@ proc httpPost*(request: Request) =
# Add response headers, as defined in team server profile # Add response headers, as defined in team server profile
var headers: HttpHeaders var headers: HttpHeaders
for header, value in cq.profile.getTable("http-post.server.headers"): for header in cq.profile.getTableKeys("http-post.server.headers"):
headers.add((header, value.getStringValue())) headers.add((header.key, header.value.getStringValue()))
# Differentiate between registration and task result packet # Differentiate between registration and task result packet
var unpacker = Unpacker.init(Bytes.toString(data)) var unpacker = Unpacker.init(Bytes.toString(data))

View File

@@ -1,4 +1,4 @@
import terminal, strformat, strutils, sequtils, tables, system, osproc, streams, parsetoml import terminal, strformat, strutils, sequtils, tables, system, osproc, streams
import ../globals import ../globals
import ../core/[logger, websocket] import ../core/[logger, websocket]
@@ -38,7 +38,7 @@ proc serializeConfiguration(cq: Conquest, listener: Listener, sleepSettings: Sle
packer.addData(cq.keyPair.publicKey) packer.addData(cq.keyPair.publicKey)
# C2 profile # C2 profile
packer.addDataWithLengthPrefix(string.toBytes(cq.profile.toTomlString())) packer.addDataWithLengthPrefix(string.toBytes(cq.profileString))
let data = packer.pack() let data = packer.pack()
packer.reset() packer.reset()

View File

@@ -1,6 +1,5 @@
import strformat, strutils, terminal import strformat, strutils, terminal, tables
import mummy, mummy/routers import mummy, mummy/routers
import parsetoml
import ../api/routes import ../api/routes
import ../db/database import ../db/database

View File

@@ -1,4 +1,4 @@
import times, json, base64, parsetoml, strformat import times, json, base64, strformat
import stb_image/write as stbiw import stb_image/write as stbiw
import ./logger import ./logger
import ../../common/[types, utils, event] import ../../common/[types, utils, event]
@@ -46,12 +46,12 @@ proc sendPublicKey*(client: WsConnection, publicKey: Key) =
if client != nil: if client != nil:
client.ws.sendEvent(event, client.sessionKey) client.ws.sendEvent(event, client.sessionKey)
proc sendProfile*(client: WsConnection, profile: Profile) = proc sendProfile*(client: WsConnection, profileString: string) =
let event = Event( let event = Event(
eventType: CLIENT_PROFILE, eventType: CLIENT_PROFILE,
timestamp: now().toTime().toUnix(), timestamp: now().toTime().toUnix(),
data: %*{ data: %*{
"profile": profile.toTomlString() "profile": profileString
} }
) )
if client != nil: if client != nil:

View File

@@ -1,5 +1,5 @@
import mummy, mummy/routers import mummy, mummy/routers
import terminal, parsetoml, json, math, base64, times import terminal, json, math, base64, times
import strutils, strformat, system, tables import strutils, strformat, system, tables
import ./globals import ./globals
@@ -15,14 +15,15 @@ proc header() =
echo "".repeat(21) echo "".repeat(21)
echo "" echo ""
proc init*(T: type Conquest, profile: Profile): Conquest = proc init*(T: type Conquest, profileString: string): Conquest =
var cq = new Conquest var cq = new Conquest
cq.listeners = initTable[string, Listener]() cq.listeners = initTable[string, Listener]()
cq.threads = initTable[string, Thread[Listener]]() cq.threads = initTable[string, Thread[Listener]]()
cq.agents = initTable[string, Agent]() cq.agents = initTable[string, Agent]()
cq.profile = profile cq.profileString = profileString
cq.keyPair = loadKeyPair(CONQUEST_ROOT & "/" & profile.getString("private-key-file")) cq.profile = parseString(profileString)
cq.dbPath = CONQUEST_ROOT & "/" & profile.getString("database-file") cq.keyPair = loadKeyPair(CONQUEST_ROOT & "/" & cq.profile.getString("private-key-file"))
cq.dbPath = CONQUEST_ROOT & "/" & cq.profile.getString("database-file")
cq.client = nil cq.client = nil
return cq return cq
@@ -54,7 +55,7 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.
# Send relevant information to the client # Send relevant information to the client
# C2 profile # C2 profile
cq.client.sendProfile(cq.profile) cq.client.sendProfile(cq.profileString)
# Listeners # Listeners
for id, listener in cq.listeners: for id, listener in cq.listeners:
@@ -140,11 +141,10 @@ proc startServer*(profilePath: string) =
try: try:
# Initialize framework context # Initialize framework context
# Load and parse profile let profileString = readFile(profilePath)
let profile = parsetoml.parseFile(profilePath) cq = Conquest.init(profileString)
cq = Conquest.init(profile)
cq.info("Using profile \"", profile.getString("name"), "\" (", profilePath ,").") cq.info("Using profile \"", cq.profile.getString("name"), "\" (", profilePath ,").")
# Initialize database # Initialize database
cq.dbInit() cq.dbInit()