194 lines
5.6 KiB
Nim
194 lines
5.6 KiB
Nim
import prompt
|
|
import tables
|
|
import times
|
|
import streams
|
|
|
|
# Custom Binary Task structure
|
|
const
|
|
MAGIC* = 0x514E3043'u32 # Magic value: C0NQ
|
|
VERSION* = 1'u8 # Version 1
|
|
HEADER_SIZE* = 52'u8 # 48 bytes fixed packet header size
|
|
|
|
type
|
|
PacketType* = enum
|
|
MSG_TASK = 0'u8
|
|
MSG_RESPONSE = 1'u8
|
|
MSG_REGISTER = 2'u8
|
|
MSG_HEARTBEAT = 100'u8
|
|
|
|
ArgType* = enum
|
|
STRING = 0'u8
|
|
INT = 1'u8
|
|
LONG = 2'u8
|
|
BOOL = 3'u8
|
|
BINARY = 4'u8
|
|
|
|
HeaderFlags* = enum
|
|
# Flags should be powers of 2 so they can be connected with or operators
|
|
FLAG_PLAINTEXT = 0'u16
|
|
FLAG_ENCRYPTED = 1'u16
|
|
FLAG_COMPRESSED = 2'u16
|
|
|
|
CommandType* = enum
|
|
CMD_SLEEP = 0'u16
|
|
CMD_SHELL = 1'u16
|
|
CMD_PWD = 2'u16
|
|
CMD_CD = 3'u16
|
|
CMD_LS = 4'u16
|
|
CMD_RM = 5'u16
|
|
CMD_RMDIR = 6'u16
|
|
CMD_MOVE = 7'u16
|
|
CMD_COPY = 8'u16
|
|
|
|
StatusType* = enum
|
|
STATUS_COMPLETED = 0'u8
|
|
STATUS_FAILED = 1'u8
|
|
|
|
ResultType* = enum
|
|
RESULT_STRING = 0'u8
|
|
RESULT_BINARY = 1'u8
|
|
RESULT_NO_OUTPUT = 2'u8
|
|
|
|
# Encryption
|
|
type
|
|
Key* = array[32, byte]
|
|
PublicKey* = array[32, byte]
|
|
PrivateKey* = array[64, byte]
|
|
Iv* = array[12, byte]
|
|
AuthenticationTag* = array[16, byte]
|
|
|
|
# Packet structure
|
|
type
|
|
Header* = object
|
|
magic*: uint32 # [4 bytes ] magic value
|
|
version*: uint8 # [1 byte ] protocol version
|
|
packetType*: uint8 # [1 byte ] message type
|
|
flags*: uint16 # [2 bytes ] message flags
|
|
size*: uint32 # [4 bytes ] size of the payload body
|
|
agentId*: uint32 # [4 bytes ] agent id, used as AAD for encryptio
|
|
seqNr*: uint64 # [8 bytes ] sequence number, used as AAD for encryption
|
|
iv*: Iv # [12 bytes] random IV for AES256 GCM encryption
|
|
gmac*: AuthenticationTag # [16 bytes] authentication tag for AES256 GCM encryption
|
|
|
|
TaskArg* = object
|
|
argType*: uint8 # [1 byte ] argument type
|
|
data*: seq[byte] # variable length data (for variable data types (STRING, BINARY), the first 4 bytes indicate data length)
|
|
|
|
Task* = object
|
|
header*: Header
|
|
|
|
taskId*: uint32 # [4 bytes ] task id
|
|
listenerId*: uint32 # [4 bytes ] listener id
|
|
timestamp*: uint32 # [4 bytes ] unix timestamp
|
|
command*: uint16 # [2 bytes ] command id
|
|
argCount*: uint8 # [1 byte ] number of arguments
|
|
args*: seq[TaskArg] # variable length arguments
|
|
|
|
TaskResult* = object
|
|
header*: Header
|
|
|
|
taskId*: uint32 # [4 bytes ] task id
|
|
listenerId*: uint32 # [4 bytes ] listener id
|
|
timestamp*: uint32 # [4 bytes ] unix timestamp
|
|
command*: uint16 # [2 bytes ] command id
|
|
status*: uint8 # [1 byte ] success flag
|
|
resultType*: uint8 # [1 byte ] result data type (string, binary)
|
|
length*: uint32 # [4 bytes ] result length
|
|
data*: seq[byte] # variable length result
|
|
|
|
# Structure for command module definitions
|
|
Argument* = object
|
|
name*: string
|
|
description*: string
|
|
argumentType*: ArgType
|
|
isRequired*: bool
|
|
|
|
Command* = object
|
|
name*: string
|
|
commandType*: CommandType
|
|
description*: string
|
|
example*: string
|
|
arguments*: seq[Argument]
|
|
dispatchMessage*: string
|
|
|
|
# Checkin binary structure
|
|
type
|
|
Heartbeat* = object
|
|
header*: Header # [48 bytes ] fixed header
|
|
listenerId*: uint32 # [4 bytes ] listener id
|
|
timestamp*: uint32 # [4 bytes ] unix timestamp
|
|
|
|
# Registration binary structure
|
|
type
|
|
# All variable length fields are stored as seq[byte], prefixed with 4 bytes indicating the length of the following data
|
|
AgentMetadata* = object
|
|
listenerId*: uint32
|
|
username*: seq[byte]
|
|
hostname*: seq[byte]
|
|
domain*: seq[byte]
|
|
ip*: seq[byte]
|
|
os*: seq[byte]
|
|
process*: seq[byte]
|
|
pid*: uint32
|
|
isElevated*: uint8
|
|
sleep*: uint32
|
|
|
|
AgentRegistrationData* = object
|
|
header*: Header
|
|
agentPublicKey*: Key # [32 bytes ] Public key of the connecting agent for key exchange
|
|
metadata*: AgentMetadata
|
|
|
|
# Agent structure
|
|
type
|
|
Agent* = ref object
|
|
agentId*: string
|
|
listenerId*: string
|
|
username*: string
|
|
hostname*: string
|
|
domain*: string
|
|
ip*: string
|
|
os*: string
|
|
process*: string
|
|
pid*: int
|
|
elevated*: bool
|
|
sleep*: int
|
|
tasks*: seq[Task]
|
|
firstCheckin*: DateTime
|
|
latestCheckin*: DateTime
|
|
sessionKey*: Key
|
|
|
|
# Listener structure
|
|
type
|
|
Protocol* = enum
|
|
HTTP = "http"
|
|
|
|
Listener* = ref object
|
|
listenerId*: string
|
|
address*: string
|
|
port*: int
|
|
protocol*: Protocol
|
|
|
|
# Server structure
|
|
type
|
|
KeyPair* = object
|
|
privateKey*: PrivateKey
|
|
publicKey*: Key
|
|
|
|
Conquest* = ref object
|
|
prompt*: Prompt
|
|
dbPath*: string
|
|
listeners*: Table[string, Listener]
|
|
agents*: Table[string, Agent]
|
|
interactAgent*: Agent
|
|
keyPair*: KeyPair
|
|
|
|
# Agent Config
|
|
type
|
|
AgentConfig* = ref object
|
|
agentId*: string
|
|
listenerId*: string
|
|
ip*: string
|
|
port*: int
|
|
sleep*: int
|
|
sessionKey*: Key
|
|
agentPublicKey*: PublicKey |