Implemented right-click menu to remove or download loot (files/screenshots).

This commit is contained in:
Jakob Friedl
2025-10-09 16:25:05 +02:00
parent 3259040daa
commit 504d15fa4d
10 changed files with 177 additions and 58 deletions

View File

@@ -59,3 +59,23 @@ proc sendAgentTask*(connection: WsConnection, agentId: string, command: string,
}
)
connection.ws.sendEvent(event, connection.sessionKey)
proc sendRemoveLoot*(connection: WsConnection, lootId: string) =
let event = Event(
eventType: CLIENT_LOOT_REMOVE,
timestamp: now().toTime().toUnix(),
data: %*{
"lootId": lootId
}
)
connection.ws.sendEvent(event, connection.sessionKey)
proc sendDownloadLoot*(connection: WsConnection, lootId: string) =
let event = Event(
eventType: CLIENT_LOOT_SYNC,
timestamp: now().toTime().toUnix(),
data: %*{
"lootId": lootId
}
)
connection.ws.sendEvent(event, connection.sessionKey)

View File

@@ -157,10 +157,18 @@ proc main(ip: string = "localhost", port: int = 37573) =
lootDownloads.items.add(lootItem)
of SCREENSHOT:
lootScreenshots.addItem(lootItem)
else: discard
of CLIENT_SYNC_LOOT:
of CLIENT_LOOT_SYNC:
let path = event.data["path"].getStr()
let file = decode(event.data["loot"].getStr())
try:
# TODO: Using native file dialogs to have the client select the output file path (does not work in WSL)
# let outFilePath = callDialogFileSave("Save Payload")
writeFile(path & "_download", file)
except IOError:
discard
discard
else: discard
@@ -169,8 +177,8 @@ proc main(ip: string = "localhost", port: int = 37573) =
if showSessionsTable: sessionsTable.draw(addr showSessionsTable)
if showListeners: listenersTable.draw(addr showListeners, connection)
if showEventlog: eventlog.draw(addr showEventlog)
if showDownloads: lootDownloads.draw(addr showDownloads)
if showScreenshots: lootScreenshots.draw(addr showScreenshots)
if showDownloads: lootDownloads.draw(addr showDownloads, connection)
if showScreenshots: lootScreenshots.draw(addr showScreenshots, connection)
# Show console windows
var newConsoleTable: Table[string, ConsoleComponent]

View File

@@ -3,7 +3,7 @@ import ../utils/fonticon/IconsFontAwesome6
const CONQUEST_ROOT* {.strdefine.} = ""
const WIDGET_SESSIONS* = " " & ICON_FA_LIST & " " & "Sessions [Table View]"
const WIDGET_LISTENERS* = " " & ICON_FA_HEADPHONES & " " & "Listeners"
const WIDGET_LISTENERS* = " " & ICON_FA_SATELLITE_DISH & " " & "Listeners"
const WIDGET_EVENTLOG* = "Eventlog"
const WIDGET_DOWNLOADS* = " " & ICON_FA_DOWNLOAD & " " & "Downloads"
const WIDGET_SCREENSHOTS* = " " & ICON_FA_IMAGE & " " & "Screenshots"

View File

@@ -2,6 +2,7 @@ import strformat, strutils, times, os
import imguin/[cimgui, glfw_opengl, simple]
import ../../utils/[appImGui, colors]
import ../../../common/[types, utils]
import ../../core/websocket
type
DownloadsComponent* = ref object of RootObj
@@ -16,7 +17,7 @@ proc LootDownloads*(title: string): DownloadsComponent =
result.items = @[]
result.selectedIndex = -1
proc draw*(component: DownloadsComponent, showComponent: ptr bool) =
proc draw*(component: DownloadsComponent, showComponent: ptr bool, connection: WsConnection) =
igBegin(component.title, showComponent, 0)
defer: igEnd()
@@ -61,7 +62,11 @@ proc draw*(component: DownloadsComponent, showComponent: ptr bool) =
let isSelected = component.selectedIndex == i
if igSelectable_Bool(item.lootId.cstring, isSelected, ImGuiSelectableFlags_SpanAllColumns.int32 or ImGuiSelectableFlags_AllowOverlap.int32, vec2(0, 0)):
component.selectedIndex = i
igPopID()
if igIsItemHovered(ImGuiHoveredFlags_None.int32) and igIsMouseClicked_Bool(ImGuiMouseButton_Right.int32, false):
component.selectedIndex = i
igPopID()
if igTableSetColumnIndex(1):
igText(item.agentId)
@@ -78,6 +83,23 @@ proc draw*(component: DownloadsComponent, showComponent: ptr bool) =
if igTableSetColumnIndex(5):
igText($item.size)
# Handle right-click context menu
if component.selectedIndex >= 0 and component.selectedIndex < component.items.len and igBeginPopupContextWindow("Downloads", ImGui_PopupFlags_MouseButtonRight.int32):
let item = component.items[component.selectedIndex]
if igMenuItem("Download", nil, false, true):
# Task team server to download file
connection.sendDownloadLoot(item.lootId)
igCloseCurrentPopup()
if igMenuItem("Remove", nil, false, true):
# Task team server to remove the loot item
connection.sendRemoveLoot(item.lootId)
component.items.delete(component.selectedIndex)
igCloseCurrentPopup()
igEndPopup()
igEndTable()
igEndChild()
@@ -93,9 +115,11 @@ proc draw*(component: DownloadsComponent, showComponent: ptr bool) =
igSameLine(0.0f, 0.0f)
igText(item.path.extractFilename().replace("C_", "C:/").replace("_", "/"))
igDummy(vec2(0.0f, 5.0f))
igSeparator()
igText(item.data)
igDummy(vec2(0.0f, 5.0f))
igTextUnformatted(item.data, nil)
else:
igText("Select item to preview contents")

View File

@@ -2,6 +2,7 @@ import strformat, strutils, times, os, tables
import imguin/[cimgui, glfw_opengl, simple]
import ../../utils/[appImGui, colors]
import ../../../common/[types, utils]
import ../../core/websocket
type
ScreenshotTexture* = ref object
@@ -33,7 +34,7 @@ proc addItem*(component: ScreenshotsComponent, screenshot: LootItem) =
height: height
)
proc draw*(component: ScreenshotsComponent, showComponent: ptr bool) =
proc draw*(component: ScreenshotsComponent, showComponent: ptr bool, connection: WsConnection) =
igBegin(component.title, showComponent, 0)
defer: igEnd()
@@ -43,7 +44,7 @@ proc draw*(component: ScreenshotsComponent, showComponent: ptr bool) =
# Left panel (file table)
let childFlags = ImGui_ChildFlags_ResizeX.int32 or ImGui_ChildFlags_NavFlattened.int32
if igBeginChild_Str("##Left", vec2(availableSize.x * 0.66f, 0.0f), childFlags, ImGui_WindowFlags_None.int32):
if igBeginChild_Str("##Left", vec2(availableSize.x * 0.5f, 0.0f), childFlags, ImGui_WindowFlags_None.int32):
let tableFlags = (
ImGui_TableFlags_Resizable.int32 or
@@ -77,6 +78,10 @@ proc draw*(component: ScreenshotsComponent, showComponent: ptr bool) =
let isSelected = component.selectedIndex == i
if igSelectable_Bool(item.lootId.cstring, isSelected, ImGuiSelectableFlags_SpanAllColumns.int32 or ImGuiSelectableFlags_AllowOverlap.int32, vec2(0, 0)):
component.selectedIndex = i
if igIsItemHovered(ImGuiHoveredFlags_None.int32) and igIsMouseClicked_Bool(ImGuiMouseButton_Right.int32, false):
component.selectedIndex = i
igPopID()
if igTableSetColumnIndex(1):
@@ -91,6 +96,23 @@ proc draw*(component: ScreenshotsComponent, showComponent: ptr bool) =
if igTableSetColumnIndex(4):
igText($item.size)
# Handle right-click context menu
if component.selectedIndex >= 0 and component.selectedIndex < component.items.len and igBeginPopupContextWindow("Downloads", ImGui_PopupFlags_MouseButtonRight.int32):
let item = component.items[component.selectedIndex]
if igMenuItem("Download", nil, false, true):
# Task team server to download file
connection.sendDownloadLoot(item.lootId)
igCloseCurrentPopup()
if igMenuItem("Remove", nil, false, true):
# Task team server to remove the loot item
connection.sendRemoveLoot(item.lootId)
component.items.delete(component.selectedIndex)
igCloseCurrentPopup()
igEndPopup()
igEndTable()
igEndChild()
@@ -106,5 +128,5 @@ proc draw*(component: ScreenshotsComponent, showComponent: ptr bool) =
igImage(ImTextureRef(internal_TexData: nil, internal_TexID: texture.textureId), vec2(texture.width, texture.height), vec2(0, 0), vec2(1, 1))
else:
igText("Select item to preview contents")
igText("Select item for preview.")
igEndChild()