diff --git a/MythicReactUI/src/components/pages/Callbacks/CallbacksTableEditTriggerOnCheckinDialog.js b/MythicReactUI/src/components/pages/Callbacks/CallbacksTableEditTriggerOnCheckinDialog.js index 53b03720..54b4f2d2 100644 --- a/MythicReactUI/src/components/pages/Callbacks/CallbacksTableEditTriggerOnCheckinDialog.js +++ b/MythicReactUI/src/components/pages/Callbacks/CallbacksTableEditTriggerOnCheckinDialog.js @@ -1,18 +1,17 @@ import React, {useEffect} from 'react'; -import Button from '@mui/material/Button'; -import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogTitle from '@mui/material/DialogTitle'; -import Box from '@mui/material/Box'; -import AceEditor from 'react-ace'; -import "ace-builds/src-noconflict/mode-json"; -import "ace-builds/src-noconflict/theme-github"; -import "ace-builds/src-noconflict/theme-monokai"; -import "ace-builds/src-noconflict/ext-searchbox"; -import {useTheme} from '@mui/material/styles'; -import {HexColorInput, HexColorPicker} from 'react-colorful'; import Typography from '@mui/material/Typography'; +import NotificationsActiveTwoToneIcon from '@mui/icons-material/NotificationsActiveTwoTone'; import MythicTextField from "../../MythicComponents/MythicTextField"; +import { + MythicDialogBody, + MythicDialogButton, + MythicDialogFooter, + MythicDialogSection, + MythicForm, + MythicFormField +} from "../../MythicComponents/MythicDialogLayout"; export function CallbacksTableEditTriggerOnCheckinDialog(props) { const [comment, setComment] = React.useState(0); @@ -31,29 +30,73 @@ export function CallbacksTableEditTriggerOnCheckinDialog(props) { } return ( - {"Adjust this callback's trigger threshold"} - - - This adjusts how long, in minutes, this callback must not checkin before finally checking in to trigger an eventing workflow (trigger is callback_checkin). - - - A zero value means never trigger. - - - If no eventing workflow for callback_checkin that matches the right payload_types and supported_os restrictions, then nothing will happen. - - + Callback check-in alert + + +
+ 0 ? "mythic-callback-trigger-summary-icon-active" : ""}`}> + + +
+ + {comment > 0 ? `Alert after ${comment} minute${comment === 1 ? "" : "s"} without a check-in` : "Alerting disabled"} + + + This setting only triggers when the callback checks in after crossing the configured threshold. + +
+
+ + + +
+ +
+
+
+
+ +
+
+ Trigger name: callback_checkin +
+
+ Matching workflow filters still apply, including payload type and supported OS restrictions. +
+
+ If no matching workflow exists, no workflow will run. +
+
+
+
- - + {props.onSubmit && - + + Save Threshold + } - +
); -} \ No newline at end of file +} diff --git a/MythicReactUI/src/components/pages/Callbacks/CallbacksTableRow.js b/MythicReactUI/src/components/pages/Callbacks/CallbacksTableRow.js index 91896aed..4f2140b0 100644 --- a/MythicReactUI/src/components/pages/Callbacks/CallbacksTableRow.js +++ b/MythicReactUI/src/components/pages/Callbacks/CallbacksTableRow.js @@ -15,7 +15,6 @@ import { } from './CallbackMutations'; import {useMutation } from '@apollo/client'; import SnoozeIcon from '@mui/icons-material/Snooze'; -import {useTheme} from '@mui/material/styles'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import {faQuestion, faSkullCrossbones, faRobot} from '@fortawesome/free-solid-svg-icons'; import {faLinux, faApple, faWindows, faChrome, faAndroid} from '@fortawesome/free-brands-svg-icons'; @@ -133,20 +132,23 @@ export const CallbacksTableIDCell = React.memo(({rowData, callbackDropdown}) =>{ {rowDataStatic.locked && - + - Locked } {rowDataStatic.trigger_on_checkin_after_time > 0 && - + + + } {rowDataStatic.callbackports.length > 0 && - + + + } @@ -168,32 +170,34 @@ export const CallbacksTableLastCheckinCell = React.memo( ({rowData, cellData, me return moment(rowData.last_checkin + "Z", "YYYY-MM-DDTHH:mm:ss.SSSSSSZ").subtract(me?.user?.server_skew || 0, 'millisecond').fromNow(true); //return newTime; } - const theme = useTheme(); if(rowData?.payload?.payloadtype?.agent_type !== "agent"){ return "" } if(rowData.last_checkin === "1970-01-01T00:00:00"){ return ( - <> - {rowData.dead && - - - - } - {"Streaming Now"} - +
+ {rowData.dead && + + + + + + } + Streaming Now +
) } return ( -
+
{rowData.dead && - + + + } + {rowData.last_checkin + "Z"} +
) @@ -231,7 +236,8 @@ export const CallbacksTableIPCell = React.memo(({cellData, rowData}) => { const updateIPsInfo = React.useCallback( ({callback_display_id, ips}) => { updateIPs({variables: {callback_display_id: callback_display_id, ips}}) }, []); - const onClick = () => { + const onClick = (event) => { + event.stopPropagation(); setOpenPickIP(true); } const editIPSubmit = (selected_ip) => { @@ -251,13 +257,18 @@ export const CallbacksTableIPCell = React.memo(({cellData, rowData}) => { }, [cellData]); return ( <> -
+
{options.length > 1 && - + + + } - {displayIP} + {displayIP}
{openPickIP && {setOpenPickIP(false);}} @@ -275,11 +286,10 @@ export const CallbacksTableIPCell = React.memo(({cellData, rowData}) => { ) }, areEqual) export const CallbacksTableC2Cell = React.memo(({rowData}) => { - const theme = useTheme(); const [localRowData, setLocalRowData] = React.useState(rowData); const initialCallbackGraphEdges = useContext(CallbackGraphEdgesContext); const onOpenTab = useContext(OnOpenTabContext); - const [activeEgress, setActiveEgress] = React.useState(theme.palette.success.main); + const [egressActive, setEgressActive] = React.useState(true); const [hasEgressRoute, setHasEgressRoute] = React.useState(true); const [openC2Dialog, setOpenC2Dialog] = React.useState(false); const [callbackgraphedges, setCallbackgraphedges] = React.useState([]); @@ -363,11 +373,11 @@ export const CallbacksTableC2Cell = React.memo(({rowData}) => { return false }); if(activeRoutes.length === 0){ - setActiveEgress(theme.palette.error.main); + setEgressActive(false); }else{ - setActiveEgress(theme.palette.success.main); + setEgressActive(true); } - }, [callbackgraphedges, theme.palette.success.main, theme.palette.error.main]); + }, [callbackgraphedges]); useEffect( () => { if(rowData.id !== localRowData.id){ setLocalRowData(rowData); @@ -376,12 +386,20 @@ export const CallbacksTableC2Cell = React.memo(({rowData}) => { if(rowData?.payload?.payloadtype?.agent_type !== "agent"){ return null } + const c2ButtonClass = `mythic-callback-iconButton mythic-callback-cellIconButton ${egressActive && hasEgressRoute ? "mythic-callback-cellIconButtonSuccess" : "mythic-callback-cellIconButtonError"}`; + const c2Tooltip = hasEgressRoute ? + (egressActive ? "View C2 path information" : "No active egress route. View C2 path information") : + "No egress route detected. View C2 path information"; return ( -
- {hasEgressRoute ? - : - - } +
+ + + {hasEgressRoute ? + : + + } + + {openC2Dialog && { const [openOSDialog, setOpenOSDialog] = React.useState(false); const getOSIcon = useCallback( () => { if(rowData?.payload?.payloadtype?.agent_type !== "agent"){ - return + return } switch(rowData.payload.os.toLowerCase()){ case "windows": - return + return case "linux": case "centos": case "redhat": case "debian": case "fedora": case "freebsd": - return + return case "macos": - return + return case "chrome": - return + return case "android": - return + return default: - return + return } }, [rowData?.payload?.os]); - const displayOSInfo = React.useCallback( () => { + const displayOSInfo = React.useCallback( (event) => { + event.stopPropagation(); setOpenOSDialog(true); }, []); return ( -
- {getOSIcon()} +
+ + + {getOSIcon()} + + { openOSDialog && {setOpenOSDialog(false);}} @@ -452,7 +478,6 @@ export const CallbacksTableOSCell = React.memo( ({rowData, cellData}) => { ) }, areEqual); export const CallbacksTableSleepCell = React.memo( ({rowData, cellData, updateSleepInfo}) => { - const theme = useTheme(); const [openSleepDialog, setOpenSleepDialog] = React.useState(false); const editSleepSubmit = (sleep) => { updateSleepInfo({sleep_info: sleep, callback_display_id: rowData.display_id}); @@ -465,10 +490,15 @@ export const CallbacksTableSleepCell = React.memo( ({rowData, cellData, updateSl return null } return ( -
- +
+ + + + + { openSleepDialog && span[data-tooltip-id="my-tooltip"] { + align-items: center; + display: inline-flex !important; + flex: 0 0 auto; + height: 22px; + line-height: 1; +} +.mythic-callback-cellInline { + align-items: center; + display: inline-flex; + gap: 0.28rem; + height: 100%; + max-width: 100%; + min-width: 0; + overflow: hidden; + width: 100%; +} +.mythic-callback-cellInlineCenter { + justify-content: center; +} +.mythic-callback-cellInline > span[data-tooltip-id="my-tooltip"] { + align-items: center; + display: inline-flex !important; + flex: 0 0 auto; + height: 22px; + line-height: 1; +} +.mythic-callback-cellText { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} .mythic-callback-iconButton.MuiIconButton-root { border: 1px solid transparent; border-radius: ${(props) => props.theme.shape.borderRadius}px; @@ -598,6 +631,59 @@ tspan { background-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.16 : 0.09)}; color: ${(props) => props.theme.palette.primary.main}; } +.mythic-callback-cellIconButton.MuiIconButton-root { + background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.045)" : "rgba(0,0,0,0.025)"}; + border-color: ${(props) => props.theme.table?.borderSoft || props.theme.borderColor}; +} +.mythic-callback-cellIconButton.MuiIconButton-root svg { + display: block; + font-size: 0.92rem; +} +.mythic-callback-cellIconButtonNeutral.MuiIconButton-root:hover { + background-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.16 : 0.09)}; + border-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.42 : 0.28)}; + color: ${(props) => props.theme.palette.primary.main}; +} +.mythic-callback-cellIconButtonSuccess.MuiIconButton-root { + background-color: ${(props) => alpha(props.theme.palette.success.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)}; + border-color: ${(props) => alpha(props.theme.palette.success.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)}; + color: ${(props) => props.theme.palette.success.main}; +} +.mythic-callback-cellIconButtonSuccess.MuiIconButton-root:hover { + background-color: ${(props) => alpha(props.theme.palette.success.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)}; + border-color: ${(props) => alpha(props.theme.palette.success.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)}; + color: ${(props) => props.theme.palette.success.main}; +} +.mythic-callback-cellIconButtonInfo.MuiIconButton-root { + background-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)}; + border-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)}; + color: ${(props) => props.theme.palette.info.main}; +} +.mythic-callback-cellIconButtonInfo.MuiIconButton-root:hover { + background-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)}; + border-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)}; + color: ${(props) => props.theme.palette.info.main}; +} +.mythic-callback-cellIconButtonWarning.MuiIconButton-root { + background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)}; + border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)}; + color: ${(props) => props.theme.palette.warning.main}; +} +.mythic-callback-cellIconButtonWarning.MuiIconButton-root:hover { + background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)}; + border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)}; + color: ${(props) => props.theme.palette.warning.main}; +} +.mythic-callback-cellIconButtonError.MuiIconButton-root { + background-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)}; + border-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)}; + color: ${(props) => props.theme.palette.error.main}; +} +.mythic-callback-cellIconButtonError.MuiIconButton-root:hover { + background-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)}; + border-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)}; + color: ${(props) => props.theme.palette.error.main}; +} .mythic-callback-interactButtonHighIntegrity.MuiIconButton-root { background-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.18 : 0.1)}; border-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.48 : 0.32)}; @@ -615,44 +701,130 @@ tspan { color: ${(props) => props.theme.palette.error.main}; } .mythic-callback-displayId { + align-items: center; color: ${(props) => props.theme.palette.text.primary}; + display: inline-flex; flex: 0 0 auto; font-weight: 700; - line-height: 1; + height: 22px; + line-height: 22px; max-width: 3.8rem; overflow: hidden; text-overflow: ellipsis; } -.mythic-callback-lockBadge { +.mythic-callback-statusBadge { align-items: center; - background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.18 : 0.12)}; - border: 1px solid ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.46 : 0.34)}; + background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.045)" : "rgba(0,0,0,0.025)"}; + border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor}; border-radius: ${(props) => props.theme.shape.borderRadius}px; - color: ${(props) => props.theme.palette.warning.main}; + color: ${(props) => props.theme.palette.text.secondary}; display: inline-flex; - flex: 0 1 auto; - font-size: 0.68rem; - font-weight: 700; - gap: 0.12rem; - height: 18px; - line-height: 1; - min-width: 0; - overflow: hidden; - padding: 0 0.28rem; - text-overflow: ellipsis; -} -.mythic-callback-lockBadge span { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; -} -.mythic-callback-statusIcon { flex: 0 0 auto; - font-size: 0.9rem; + font-size: 0.82rem; + height: 20px; + justify-content: center; + line-height: 1; margin-left: 0.08rem; + transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease; + width: 22px; } -.mythic-callback-statusIconSuccess { - color: ${(props) => props.theme.palette.success.main}; +.mythic-callback-statusBadgeAlert { + background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.14 : 0.08)}; + border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.36 : 0.24)}; + color: ${(props) => props.theme.palette.warning.main}; +} +.mythic-callback-statusBadgeProxy { + background-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)}; + border-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)}; + color: ${(props) => props.theme.palette.info.main}; +} +.mythic-callback-statusBadgeLock { + background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)}; + border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)}; + color: ${(props) => props.theme.palette.warning.main}; +} +.mythic-callback-statusBadgeDead { + background-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.12 : 0.07)}; + border-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.32 : 0.2)}; + color: ${(props) => props.theme.palette.error.main}; +} +.mythic-callback-statusBadgeAlert:hover { + background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.22 : 0.13)}; + border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.58 : 0.42)}; +} +.mythic-callback-statusBadgeProxy:hover { + background-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)}; + border-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)}; +} +.mythic-callback-statusBadgeLock:hover { + background-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)}; + border-color: ${(props) => alpha(props.theme.palette.info.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)}; +} +.mythic-callback-statusBadgeDead:hover { + background-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.2 : 0.12)}; + border-color: ${(props) => alpha(props.theme.palette.error.main, props.theme.palette.mode === "dark" ? 0.54 : 0.36)}; +} +.mythic-callback-statusBadge svg { + display: block; + font-size: inherit; +} +.mythic-callback-trigger-summary { + align-items: flex-start; + background-color: ${(props) => props.theme.surfaces?.muted || props.theme.palette.background.paper}; + border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor}; + border-radius: ${(props) => props.theme.shape.borderRadius}px; + display: flex; + gap: 0.75rem; + min-width: 0; + padding: 0.75rem; +} +.mythic-callback-trigger-summary-icon { + align-items: center; + background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.045)" : "rgba(0,0,0,0.025)"}; + border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor}; + border-radius: ${(props) => props.theme.shape.borderRadius}px; + color: ${(props) => props.theme.palette.text.secondary}; + display: inline-flex; + flex: 0 0 auto; + height: 34px; + justify-content: center; + width: 34px; +} +.mythic-callback-trigger-summary-icon-active { + background-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.16 : 0.1)}; + border-color: ${(props) => alpha(props.theme.palette.warning.main, props.theme.palette.mode === "dark" ? 0.42 : 0.3)}; + color: ${(props) => props.theme.palette.warning.main}; +} +.mythic-callback-trigger-summary-copy { + min-width: 0; +} +.mythic-callback-trigger-summary-title.MuiTypography-root { + color: ${(props) => props.theme.palette.text.primary}; + font-size: 0.9rem; + font-weight: 800; + line-height: 1.25; +} +.mythic-callback-trigger-summary-description.MuiTypography-root { + color: ${(props) => props.theme.palette.text.secondary}; + font-size: 0.78rem; + line-height: 1.4; + margin-top: 0.2rem; +} +.mythic-callback-trigger-rule-list { + display: grid; + gap: 0.45rem; +} +.mythic-callback-trigger-rule { + background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.035)" : "rgba(0,0,0,0.025)"}; + border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor}; + border-radius: ${(props) => props.theme.shape.borderRadius}px; + color: ${(props) => props.theme.palette.text.secondary}; + font-size: 0.78rem; + line-height: 1.4; + padding: 0.55rem 0.65rem; +} +.mythic-callback-trigger-rule strong { + color: ${(props) => props.theme.palette.text.primary}; } .MythicInteractiveTerminal { background-color: ${(props) => props.theme.outputBackgroundColor};