2025-08-13 19:32:51 +02:00
|
|
|
import prompt, terminal, argparse, parsetoml
|
2025-05-13 23:42:04 +02:00
|
|
|
import strutils, strformat, times, system, tables
|
2025-05-09 12:04:14 +02:00
|
|
|
|
2025-07-16 10:33:13 +02:00
|
|
|
import ./[agent, listener]
|
2025-07-16 14:45:45 +02:00
|
|
|
import ../[globals, utils]
|
2025-07-16 10:33:13 +02:00
|
|
|
import ../db/database
|
2025-07-24 15:31:46 +02:00
|
|
|
import ../../common/[types, utils, crypto]
|
2025-05-02 18:10:30 +02:00
|
|
|
|
2025-05-12 21:53:37 +02:00
|
|
|
#[
|
|
|
|
|
Argument parsing
|
|
|
|
|
]#
|
|
|
|
|
var parser = newParser:
|
|
|
|
|
help("Conquest Command & Control")
|
2025-05-24 16:15:41 +02:00
|
|
|
nohelpflag()
|
2025-05-12 21:53:37 +02:00
|
|
|
|
|
|
|
|
command("listener"):
|
|
|
|
|
help("Manage, start and stop listeners.")
|
|
|
|
|
|
|
|
|
|
command("list"):
|
|
|
|
|
help("List all active listeners.")
|
|
|
|
|
command("start"):
|
|
|
|
|
help("Starts a new HTTP listener.")
|
2025-06-02 21:14:13 +02:00
|
|
|
option("-i", "--ip", default=some("127.0.0.1"), help="IPv4 address to listen on.", required=false)
|
2025-05-24 16:15:41 +02:00
|
|
|
option("-p", "--port", help="Port to listen on.", required=true)
|
|
|
|
|
|
2025-05-15 14:27:45 +02:00
|
|
|
# TODO: Future features:
|
2025-05-12 21:53:37 +02:00
|
|
|
# flag("--dns", help="Use the DNS protocol for C2 communication.")
|
2025-05-13 23:42:04 +02:00
|
|
|
# flag("--doh", help="Use DNS over HTTPS for C2 communication.)
|
2025-05-12 21:53:37 +02:00
|
|
|
command("stop"):
|
|
|
|
|
help("Stop an active listener.")
|
2025-05-24 16:15:41 +02:00
|
|
|
option("-n", "--name", help="Name of the listener.", required=true)
|
2025-05-12 21:53:37 +02:00
|
|
|
|
|
|
|
|
command("agent"):
|
|
|
|
|
help("Manage, build and interact with agents.")
|
|
|
|
|
|
|
|
|
|
command("list"):
|
|
|
|
|
help("List all agents.")
|
2025-05-24 16:15:41 +02:00
|
|
|
option("-l", "--listener", help="Name of the listener.")
|
2025-05-12 21:53:37 +02:00
|
|
|
|
2025-05-15 14:27:45 +02:00
|
|
|
command("info"):
|
|
|
|
|
help("Display details for a specific agent.")
|
2025-05-24 16:15:41 +02:00
|
|
|
option("-n", "--name", help="Name of the agent.", required=true)
|
2025-05-15 14:27:45 +02:00
|
|
|
|
2025-05-14 15:48:01 +02:00
|
|
|
command("kill"):
|
|
|
|
|
help("Terminate the connection of an active listener and remove it from the interface.")
|
2025-05-24 16:15:41 +02:00
|
|
|
option("-n", "--name", help="Name of the agent.", required=true)
|
|
|
|
|
# flag("--self-delete", help="Remove agent executable from target system.")
|
2025-05-12 21:53:37 +02:00
|
|
|
|
|
|
|
|
command("interact"):
|
|
|
|
|
help("Interact with an active agent.")
|
2025-05-24 16:15:41 +02:00
|
|
|
option("-n", "--name", help="Name of the agent.", required=true)
|
|
|
|
|
|
|
|
|
|
command("build"):
|
|
|
|
|
help("Generate a new agent to connect to an active listener.")
|
|
|
|
|
option("-l", "--listener", help="Name of the listener.", required=true)
|
|
|
|
|
option("-s", "--sleep", help="Sleep delay in seconds.", default=some("10") )
|
2025-05-28 10:39:30 +02:00
|
|
|
option("-p", "--payload", help="Agent type.\n\t\t\t ", default=some("monarch"), choices = @["monarch"],)
|
2025-05-12 21:53:37 +02:00
|
|
|
|
|
|
|
|
command("help"):
|
|
|
|
|
nohelpflag()
|
|
|
|
|
|
|
|
|
|
command("exit"):
|
|
|
|
|
nohelpflag()
|
|
|
|
|
|
2025-07-16 10:33:13 +02:00
|
|
|
proc handleConsoleCommand(cq: Conquest, args: string) =
|
2025-05-12 21:53:37 +02:00
|
|
|
|
|
|
|
|
# Return if no command (or just whitespace) is entered
|
2025-07-14 22:14:27 +02:00
|
|
|
if args.replace(" ", "").len == 0: return
|
2025-05-02 18:10:30 +02:00
|
|
|
|
2025-05-12 21:53:37 +02:00
|
|
|
let date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
2025-07-14 22:14:27 +02:00
|
|
|
cq.writeLine(fgBlue, styleBright, fmt"[{date}] ", resetStyle, styleBright, args)
|
2025-05-06 22:46:36 +02:00
|
|
|
|
2025-05-12 21:53:37 +02:00
|
|
|
try:
|
2025-07-14 22:14:27 +02:00
|
|
|
let opts = parser.parse(args.split(" ").filterIt(it.len > 0))
|
2025-05-12 21:53:37 +02:00
|
|
|
|
|
|
|
|
case opts.command
|
|
|
|
|
|
|
|
|
|
of "exit": # Exit program
|
|
|
|
|
echo "\n"
|
|
|
|
|
quit(0)
|
|
|
|
|
|
|
|
|
|
of "help": # Display help menu
|
|
|
|
|
cq.writeLine(parser.help())
|
|
|
|
|
|
|
|
|
|
of "listener":
|
|
|
|
|
case opts.listener.get.command
|
|
|
|
|
of "list":
|
|
|
|
|
cq.listenerList()
|
|
|
|
|
of "start":
|
2025-05-24 16:15:41 +02:00
|
|
|
cq.listenerStart(opts.listener.get.start.get.ip, opts.listener.get.start.get.port)
|
2025-05-12 21:53:37 +02:00
|
|
|
of "stop":
|
|
|
|
|
cq.listenerStop(opts.listener.get.stop.get.name)
|
|
|
|
|
else:
|
|
|
|
|
cq.listenerUsage()
|
|
|
|
|
|
|
|
|
|
of "agent":
|
|
|
|
|
case opts.agent.get.command
|
2025-05-15 15:24:46 +02:00
|
|
|
of "list":
|
|
|
|
|
cq.agentList(opts.agent.get.list.get.listener)
|
2025-05-15 14:27:45 +02:00
|
|
|
of "info":
|
|
|
|
|
cq.agentInfo(opts.agent.get.info.get.name)
|
2025-05-14 15:48:01 +02:00
|
|
|
of "kill":
|
|
|
|
|
cq.agentKill(opts.agent.get.kill.get.name)
|
2025-05-12 21:53:37 +02:00
|
|
|
of "interact":
|
2025-05-18 12:51:26 +02:00
|
|
|
cq.agentInteract(opts.agent.get.interact.get.name)
|
2025-05-24 16:15:41 +02:00
|
|
|
of "build":
|
|
|
|
|
cq.agentBuild(opts.agent.get.build.get.listener, opts.agent.get.build.get.sleep, opts.agent.get.build.get.payload)
|
2025-05-12 21:53:37 +02:00
|
|
|
else:
|
|
|
|
|
cq.agentUsage()
|
|
|
|
|
|
|
|
|
|
# Handle help flag
|
|
|
|
|
except ShortCircuit as err:
|
|
|
|
|
if err.flag == "argparse_help":
|
|
|
|
|
cq.writeLine(err.help)
|
|
|
|
|
|
|
|
|
|
# Handle invalid arguments
|
2025-05-24 16:15:41 +02:00
|
|
|
except CatchableError:
|
2025-05-12 21:53:37 +02:00
|
|
|
cq.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg())
|
|
|
|
|
|
|
|
|
|
cq.writeLine("")
|
|
|
|
|
|
2025-08-13 21:42:58 +02:00
|
|
|
proc header() =
|
|
|
|
|
echo ""
|
|
|
|
|
echo "┏┏┓┏┓┏┓┓┏┏┓┏╋"
|
|
|
|
|
echo "┗┗┛┛┗┗┫┗┻┗ ┛┗ V0.1"
|
|
|
|
|
echo " ┗ @jakobfriedl"
|
|
|
|
|
echo "─".repeat(21)
|
|
|
|
|
echo ""
|
2025-07-16 10:33:13 +02:00
|
|
|
|
2025-08-13 19:32:51 +02:00
|
|
|
proc init*(T: type Conquest, profile: Profile): Conquest =
|
2025-07-16 14:45:45 +02:00
|
|
|
var cq = new Conquest
|
|
|
|
|
var prompt = Prompt.init()
|
|
|
|
|
cq.prompt = prompt
|
|
|
|
|
cq.listeners = initTable[string, Listener]()
|
|
|
|
|
cq.agents = initTable[string, Agent]()
|
|
|
|
|
cq.interactAgent = nil
|
2025-08-13 19:32:51 +02:00
|
|
|
|
|
|
|
|
cq.keyPair = loadKeyPair(profile["private_key_file"].getStr())
|
|
|
|
|
cq.dbPath = profile["database_file"].getStr()
|
|
|
|
|
cq.profile = profile
|
2025-07-16 14:45:45 +02:00
|
|
|
|
|
|
|
|
return cq
|
|
|
|
|
|
2025-08-13 21:42:58 +02:00
|
|
|
proc startServer*(profilePath: string) =
|
2025-08-13 19:32:51 +02:00
|
|
|
|
2025-05-12 21:53:37 +02:00
|
|
|
# Handle CTRL+C,
|
|
|
|
|
proc exit() {.noconv.} =
|
|
|
|
|
echo "Received CTRL+C. Type \"exit\" to close the application.\n"
|
|
|
|
|
setControlCHook(exit)
|
|
|
|
|
|
2025-08-13 21:42:58 +02:00
|
|
|
header()
|
2025-08-13 19:32:51 +02:00
|
|
|
|
2025-07-24 15:31:46 +02:00
|
|
|
try:
|
2025-08-13 21:42:58 +02:00
|
|
|
# Load and parse profile
|
|
|
|
|
let profile = parseFile(profilePath).getTable
|
|
|
|
|
styledEcho(fgGreen, styleBright, "[+] Using profile \"", profile["name"].getStr(), "\" (", profilePath ,").")
|
|
|
|
|
styledEcho(fgGreen, styleBright, "[+] ", profile["private_key_file"].getStr(), ": Private key found.")
|
|
|
|
|
|
|
|
|
|
# # dump table.getTable()
|
|
|
|
|
# let headers = table["http-get"]["agent"]["headers"].getTable()
|
|
|
|
|
# for key, value in headers:
|
|
|
|
|
# if value.kind == TomlValueKind.Table:
|
|
|
|
|
# echo value["encoding"]
|
|
|
|
|
# echo value["append"]
|
|
|
|
|
# echo value["prepend"]
|
|
|
|
|
# echo key
|
|
|
|
|
|
|
|
|
|
# Initialize framework context
|
2025-08-13 19:32:51 +02:00
|
|
|
cq = Conquest.init(profile)
|
2025-07-24 22:34:12 +02:00
|
|
|
|
2025-07-24 15:31:46 +02:00
|
|
|
except CatchableError as err:
|
|
|
|
|
echo err.msg
|
|
|
|
|
quit(0)
|
2025-05-12 21:53:37 +02:00
|
|
|
|
|
|
|
|
# Initialize database
|
|
|
|
|
cq.dbInit()
|
|
|
|
|
cq.restartListeners()
|
2025-05-18 12:51:26 +02:00
|
|
|
cq.addMultiple(cq.dbGetAllAgents())
|
2025-07-24 15:31:46 +02:00
|
|
|
|
2025-05-12 21:53:37 +02:00
|
|
|
# Main loop
|
|
|
|
|
while true:
|
|
|
|
|
cq.setIndicator("[conquest]> ")
|
2025-05-14 15:48:01 +02:00
|
|
|
cq.setStatusBar(@[("[mode]", "manage"), ("[listeners]", $len(cq.listeners)), ("[agents]", $len(cq.agents))])
|
2025-05-12 21:53:37 +02:00
|
|
|
cq.showPrompt()
|
2025-05-14 15:48:01 +02:00
|
|
|
|
2025-05-12 21:53:37 +02:00
|
|
|
var command: string = cq.readLine()
|
|
|
|
|
cq.withOutput(handleConsoleCommand, command)
|