updates to task and response database tracking

This commit is contained in:
its-a-feature
2026-05-08 19:05:05 -05:00
parent f15b7a6b56
commit ada9667a7f
30 changed files with 347 additions and 284 deletions
@@ -62,6 +62,7 @@ import {
gridValuePassesFilter,
isGridColumnFilterActive
} from "../../MythicComponents/MythicResizableGrid/GridColumnFilterDialog";
import {MythicConfirmDialog} from "../../MythicComponents/MythicConfirmDialog";
export const getCustomBrowsersQuery = gql(`
query getCustomBrowsersQuery{
@@ -261,6 +262,7 @@ function CallbacksTablePreMemo(props){
const [openContextMenu, setOpenContextMenu] = React.useState(false);
const [openHideMultipleDialog, setOpenHideMultipleDialog] = React.useState(false);
const [openTriggerDialog, setOpenTriggerDialog] = React.useState({open: false, trigger_on_checkin_after_time: 0, display_id: 0});
const [openUnlockDialog, setOpenUnlockDialog] = React.useState({open: false, display_id: 0, lockOwner: ""});
const [openTaskMultipleDialog, setOpenTaskMultipleDialog] = React.useState({open: false, data: {}});
const [filterOptions, setFilterOptions] = React.useState({});
const [selectedColumn, setSelectedColumn] = React.useState({});
@@ -388,6 +390,28 @@ function CallbacksTablePreMemo(props){
updateTrigger({variables: {callback_display_id: openTriggerDialog.display_id, trigger_on_checkin_after_time: newTriggerValue}})
setOpenTriggerDialog({...openTriggerDialog, open: false});
}
const onOpenTriggerDialog = React.useCallback((callback) => {
setOpenTriggerDialog({
open: true,
trigger_on_checkin_after_time: callback.trigger_on_checkin_after_time,
display_id: callback.display_id,
});
}, []);
const onOpenUnlockDialog = React.useCallback((callback) => {
setOpenUnlockDialog({
open: true,
display_id: callback.display_id,
lockOwner: callback.locked_operator?.username || "unknown operator",
});
}, []);
const onCloseUnlockDialog = React.useCallback(() => {
setOpenUnlockDialog({open: false, display_id: 0, lockOwner: ""});
}, []);
const onSubmitUnlockDialog = React.useCallback(() => {
if(openUnlockDialog.display_id > 0){
unlockCallback({variables: {callback_display_id: openUnlockDialog.display_id}});
}
}, [openUnlockDialog.display_id, unlockCallback]);
async function getCustomBrowsersForContextMenu(){
return await getCustomBrowsers({}).then(result => {return result.data?.custombrowser});
}
@@ -521,13 +545,13 @@ function CallbacksTablePreMemo(props){
}, type: "item"
},
{
name: rowDataStatic.locked ? 'Unlock (Locked by ' + rowDataStatic.locked_operator.username + ')' : 'Lock Callback',
name: rowDataStatic.locked ? 'Unlock (Locked by ' + (rowDataStatic.locked_operator?.username || "unknown operator") + ')' : 'Lock Callback',
icon: callbackMenuIcon(rowDataStatic.locked ? <LockIcon fontSize="small"/> : <LockOpenIcon fontSize="small" />, rowDataStatic.locked ? "warning" : "neutral"),
className: "mythic-callback-action-menu-item",
click: ({event}) => {
event.stopPropagation();
if(rowDataStatic.locked){
unlockCallback({variables: {callback_display_id: rowDataStatic.display_id}})
onOpenUnlockDialog(rowDataStatic);
}else{
lockCallback({variables: {callback_display_id: rowDataStatic.display_id}})
}
@@ -540,7 +564,7 @@ function CallbacksTablePreMemo(props){
className: "mythic-callback-action-menu-item",
click: ({event}) => {
event.stopPropagation();
setOpenTriggerDialog({open: true, trigger_on_checkin_after_time: rowDataStatic.trigger_on_checkin_after_time, display_id: rowDataStatic.display_id})
onOpenTriggerDialog(rowDataStatic);
}
},
{
@@ -894,6 +918,8 @@ function CallbacksTablePreMemo(props){
}}
key={`callback${row.id}_${c.name}`}
callbackDropdown={callbackDropdown}
onOpenTriggerDialog={onOpenTriggerDialog}
onOpenUnlockDialog={onOpenUnlockDialog}
/>;
case "Groups":
return <CallbacksTableStringCell
@@ -1008,7 +1034,7 @@ function CallbacksTablePreMemo(props){
})];
}
}, [])
}, [callbacks, callbackLocalUpdates, sortData, filterOptions, columnVisibility, clickedCallbackID]);
}, [callbacks, callbackLocalUpdates, sortData, filterOptions, columnVisibility, clickedCallbackID, onOpenTriggerDialog, onOpenUnlockDialog]);
const onSubmitFilterOptions = (value) => {
const nextFilterOptions = getUpdatedGridFilterOptions(filterOptions, selectedColumn.key, value);
setFilterOptions(nextFilterOptions);
@@ -1217,6 +1243,17 @@ function CallbacksTablePreMemo(props){
}
/>
}
{openUnlockDialog.open &&
<MythicConfirmDialog
open={openUnlockDialog.open}
title={"Unlock Callback?"}
dialogText={`Unlock callback ${openUnlockDialog.display_id} currently locked by ${openUnlockDialog.lockOwner}?`}
acceptText={"Unlock"}
acceptColor={"warning"}
onClose={onCloseUnlockDialog}
onSubmit={onSubmitUnlockDialog}
/>
}
{openReorderDialog &&
<MythicDialog fullWidth={true} maxWidth="sm" open={openReorderDialog}
onClose={()=>{setOpenReorderDialog(false);}}
@@ -16,7 +16,7 @@ import {
import {useMutation } from '@apollo/client';
import SnoozeIcon from '@mui/icons-material/Snooze';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import {faQuestion, faSkullCrossbones, faRobot} from '@fortawesome/free-solid-svg-icons';
import {faQuestion, faRobot} from '@fortawesome/free-solid-svg-icons';
import {faLinux, faApple, faWindows, faChrome, faAndroid} from '@fortawesome/free-brands-svg-icons';
import { MythicStyledTooltip } from '../../MythicComponents/MythicStyledTooltip';
import { MythicSelectFromRawListDialog } from '../../MythicComponents/MythicSelectFromListDialog';
@@ -29,10 +29,11 @@ import {GetMythicSetting} from "../../MythicComponents/MythicSavedUserSetting";
import TerminalIcon from '@mui/icons-material/Terminal';
import VerticalSplitIcon from '@mui/icons-material/VerticalSplit';
import NotificationsActiveTwoToneIcon from '@mui/icons-material/NotificationsActiveTwoTone';
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
import {faSocks} from '@fortawesome/free-solid-svg-icons';
import {TagsDisplay, ViewEditTags} from "../../MythicComponents/MythicTag";
export const CallbacksTableIDCell = React.memo(({rowData, callbackDropdown}) =>{
export const CallbacksTableIDCell = React.memo(({rowData, callbackDropdown, onOpenTriggerDialog, onOpenUnlockDialog}) =>{
const dropdownAnchorRef = React.useRef(null);
const onOpenTab = useContext(OnOpenTabContext);
const interactType = GetMythicSetting({setting_name: "interactType", default_value: "interact"})
@@ -99,6 +100,14 @@ export const CallbacksTableIDCell = React.memo(({rowData, callbackDropdown}) =>{
event.stopPropagation();
callbackDropdown({rowDataStatic, event});
};
const handleOpenTriggerDialog = (event) => {
event.stopPropagation();
onOpenTriggerDialog?.(rowDataStatic);
};
const handleOpenUnlockDialog = (event) => {
event.stopPropagation();
onOpenUnlockDialog?.(rowDataStatic);
};
const localOnOpenTab = () => {
onOpenTab({tabType: interactType, tabID: rowDataStatic.id + interactType, callbackID: rowDataStatic.id, displayID: rowDataStatic.display_id});
}
@@ -132,16 +141,22 @@ export const CallbacksTableIDCell = React.memo(({rowData, callbackDropdown}) =>{
</IconButton>
{rowDataStatic.locked &&
<MythicStyledTooltip title={`Locked by ${lockOwner}`}>
<span className="mythic-callback-statusBadge mythic-callback-statusBadgeLock">
<button type="button"
className="mythic-callback-statusBadge mythic-callback-statusBadgeButton mythic-callback-statusBadgeLock"
aria-label={`Unlock callback ${rowDataStatic.display_id}`}
onClick={handleOpenUnlockDialog}>
<LockIcon fontSize="inherit" />
</span>
</button>
</MythicStyledTooltip>
}
{rowDataStatic.trigger_on_checkin_after_time > 0 &&
<MythicStyledTooltip title={`Alert on callback after no checkin for ${rowDataStatic.trigger_on_checkin_after_time} minutes`}>
<span className="mythic-callback-statusBadge mythic-callback-statusBadgeAlert">
<button type="button"
className="mythic-callback-statusBadge mythic-callback-statusBadgeButton mythic-callback-statusBadgeAlert"
aria-label={`Adjust alert trigger for callback ${rowDataStatic.display_id}`}
onClick={handleOpenTriggerDialog}>
<NotificationsActiveTwoToneIcon fontSize="inherit" />
</span>
</button>
</MythicStyledTooltip>
}
{rowDataStatic.callbackports.length > 0 &&
@@ -179,7 +194,7 @@ export const CallbacksTableLastCheckinCell = React.memo( ({rowData, cellData, me
{rowData.dead &&
<MythicStyledTooltip title={"Based on callback's last checkin and sleep info, it's likely dead"}>
<span className="mythic-callback-statusBadge mythic-callback-statusBadgeDead">
<FontAwesomeIcon icon={faSkullCrossbones} />
<WarningAmberIcon fontSize="inherit" />
</span>
</MythicStyledTooltip>
}
@@ -193,7 +208,7 @@ export const CallbacksTableLastCheckinCell = React.memo( ({rowData, cellData, me
{rowData.dead &&
<MythicStyledTooltip title={"Based on callback's last checkin and sleep info, it's likely dead"}>
<span className="mythic-callback-statusBadge mythic-callback-statusBadgeDead">
<FontAwesomeIcon icon={faSkullCrossbones} />
<WarningAmberIcon fontSize="inherit" />
</span>
</MythicStyledTooltip>
}
@@ -493,7 +508,7 @@ export const CallbacksTableSleepCell = React.memo( ({rowData, cellData, updateSl
<div className="mythic-callback-cellInline mythic-callback-cellInlineCenter">
<MythicStyledTooltip title={cellData === "" ? "No sleep information set" : "View or edit sleep information"}>
<IconButton
className={`mythic-callback-iconButton mythic-callback-cellIconButton ${cellData === "" ? "mythic-callback-cellIconButtonWarning" : "mythic-callback-cellIconButtonInfo"}`}
className={`mythic-callback-iconButton mythic-callback-cellIconButton ${cellData === "" ? "mythic-callback-cellIconButtonHoverWarning" : "mythic-callback-cellIconButtonHoverInfo"}`}
onClick={onOpenSleepDialog}
>
<SnoozeIcon fontSize="small" />
@@ -111,9 +111,6 @@ function CallbackSearchTableRow(props){
label={props.active ? "Active" : "Inactive"}
status={props.active ? "active" : "inactive"}
/>
{props.dead &&
<MythicStatusChip label="Likely dead" status="warning" />
}
{props.locked &&
<MythicStatusChip label="Locked" status="locked" />
}
@@ -20,7 +20,7 @@ import {MythicPageHeader, MythicPageHeaderChip} from "../../MythicComponents/Myt
export function Search(props){
const navigate = useNavigate();
const tabTypes = ["callbacks", "tasks", "payloads", "files", "credentials", "keylogs", "artifacts", "tokens", "socks", "processes", "tags", "browsers"];
const tabTypes = ["callbacks", "tasks", "payloads", "files", "credentials", "keylogs", "artifacts", "tokens", "proxies", "processes", "tags", "browsers"];
const tabLabels = {
artifacts: "Artifacts",
browsers: "Browser scripts",
@@ -30,7 +30,7 @@ export function Search(props){
keylogs: "Keylogs",
payloads: "Payloads",
processes: "Processes",
socks: "SOCKS",
proxies: "proxies",
tags: "Tags",
tasks: "Tasks",
tokens: "Tokens",
@@ -70,8 +70,8 @@ export function Search(props){
return <SearchTabTokensPanel key={"tokenspanel"} index={value} me={props.me} value={value} changeSearchParam={changeSearchParam} />
case "artifacts":
return <SearchTabArtifactsPanel key={"artifactspanel"} index={value} me={props.me} value={value} changeSearchParam={changeSearchParam} />
case "socks":
return <SearchTabSocksPanel key={"sockspanel"} index={value} me={props.me} value={value} changeSearchParam={changeSearchParam} />
case "proxies":
return <SearchTabSocksPanel key={"proxiespanel"} index={value} me={props.me} value={value} changeSearchParam={changeSearchParam} />
case "processes":
return <SearchTabProcessPanel key={"processpanel"} index={value} me={props.me} value={value} changeSearchParam={changeSearchParam} />
case "tags":
@@ -133,7 +133,7 @@ export function Search(props){
return <SearchTabCallbacksLabel key={"callbackstab"} me={props.me}/>;
case "artifacts":
return <SearchTabArtifactsLabel key={"artifactstab"} me={props.me}/>;
case "socks":
case "proxies":
return <SearchTabSocksLabel key={"sockstab"} me={props.me}/>;
case "processes":
return <SearchTabProcessesLabel key={"processtab"} me={props.me}/>;
@@ -47,7 +47,7 @@ subscription portsSub{
export function SearchTabSocksLabel(props){
return (
<MythicSearchTabLabel label={"PROXIES"} iconComponent={
<MythicSearchTabLabel label={"Proxies"} iconComponent={
<FontAwesomeIcon icon={faSocks} size="lg" style={{marginTop: "4px", marginBottom: "5px"}}/>} {...props}/>
)
}
+27 -22
View File
@@ -877,6 +877,16 @@ tspan {
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-cellIconButtonHoverInfo.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-cellIconButtonHoverWarning.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)};
@@ -917,55 +927,50 @@ tspan {
}
.mythic-callback-statusBadge {
align-items: center;
appearance: none;
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;
box-sizing: border-box;
color: ${(props) => props.theme.palette.text.secondary};
display: inline-flex;
flex: 0 0 auto;
font-size: 0.82rem;
font-family: inherit;
height: 20px;
justify-content: center;
line-height: 1;
margin-left: 0.08rem;
padding: 0;
transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
width: 22px;
}
.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-statusBadgeButton {
cursor: pointer;
}
.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-statusBadgeButton:focus-visible {
outline: 2px solid ${(props) => alpha(props.theme.palette.primary.main, 0.55)};
outline-offset: 2px;
}
.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)};
color: ${(props) => props.theme.palette.warning.main};
}
.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)};
color: ${(props) => props.theme.palette.info.main};
}
.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)};
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)};
color: ${(props) => props.theme.palette.warning.main};
}
.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)};
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)};
color: ${(props) => props.theme.palette.warning.main};
}
.mythic-callback-statusBadge svg {
display: block;
-1
View File
@@ -1 +0,0 @@
From grafana/grafana
@@ -1,53 +0,0 @@
{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ if gt (.Alerts.Resolved | len) 0 }}, RESOLVED:{{ .Alerts.Resolved | len }}{{ end }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }}
{{ define "__text_values_list" }}{{ $len := len .Values }}{{ if $len }}{{ $first := gt $len 1 }}{{ range $refID, $value := .Values -}}
{{ $refID }}={{ $value }}{{ if $first }}, {{ end }}{{ $first = false }}{{ end -}}
{{ else }}[no value]{{ end }}{{ end }}
{{ define "__text_alert_list" }}{{ range . }}
Value: {{ template "__text_values_list" . }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}{{ if gt (len .GeneratorURL) 0 }}Source: {{ .GeneratorURL }}
{{ end }}{{ if gt (len .SilenceURL) 0 }}Silence: {{ .SilenceURL }}
{{ end }}{{ if gt (len .DashboardURL) 0 }}Dashboard: {{ .DashboardURL }}
{{ end }}{{ if gt (len .PanelURL) 0 }}Panel: {{ .PanelURL }}
{{ end }}{{ end }}{{ end }}
{{ define "default.title" }}{{ template "__subject" . }}{{ end }}
{{ define "default.message" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing**
{{ template "__text_alert_list" .Alerts.Firing }}{{ if gt (len .Alerts.Resolved) 0 }}
{{ end }}{{ end }}{{ if gt (len .Alerts.Resolved) 0 }}**Resolved**
{{ template "__text_alert_list" .Alerts.Resolved }}{{ end }}{{ end }}
{{ define "__teams_text_alert_list" }}{{ range . }}
Value: {{ template "__text_values_list" . }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}
Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}
{{ if gt (len .GeneratorURL) 0 }}Source: [{{ .GeneratorURL }}]({{ .GeneratorURL }})
{{ end }}{{ if gt (len .SilenceURL) 0 }}Silence: [{{ .SilenceURL }}]({{ .SilenceURL }})
{{ end }}{{ if gt (len .DashboardURL) 0 }}Dashboard: [{{ .DashboardURL }}]({{ .DashboardURL }})
{{ end }}{{ if gt (len .PanelURL) 0 }}Panel: [{{ .PanelURL }}]({{ .PanelURL }})
{{ end }}
{{ end }}{{ end }}
{{ define "teams.default.message" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing**
{{ template "__teams_text_alert_list" .Alerts.Firing }}{{ if gt (len .Alerts.Resolved) 0 }}
{{ end }}{{ end }}{{ if gt (len .Alerts.Resolved) 0 }}**Resolved**
{{ template "__teams_text_alert_list" .Alerts.Resolved }}{{ end }}{{ end }}
Binary file not shown.
+17 -16
View File
@@ -110,6 +110,14 @@ type Mutation {
): createArtifactOutput
}
type Mutation {
createC2Instance(
c2_instance: String!
instance_name: String!
c2profile_id: Int!
): createc2instanceOutput
}
type Mutation {
createCallback(
newCallback: newCallbackConfig
@@ -206,14 +214,6 @@ type Mutation {
): createTaskOutput
}
type Mutation {
createC2Instance(
c2_instance: String!
instance_name: String!
c2profile_id: Int!
): createc2instanceOutput
}
type Mutation {
custombrowserExportFunction(
tree_type: String!
@@ -415,6 +415,14 @@ type Query {
): ProfileOutput
}
type Mutation {
importC2Instance(
c2_instance: jsonb!
instance_name: String!
c2profile_name: String!
): createc2instanceOutput
}
type Mutation {
importCallbackConfig(
config: jsonb!
@@ -427,14 +435,6 @@ type Mutation {
): importTagtypesOutput
}
type Mutation {
importC2Instance(
c2_instance: jsonb!
instance_name: String!
c2profile_name: String!
): createc2instanceOutput
}
type Query {
meHook: meHookOutput
}
@@ -1324,3 +1324,4 @@ type custombrowserExportFunctionOutput {
status: String!
error: String
}
+21 -21
View File
@@ -181,6 +181,17 @@ actions:
- role: mythic_admin
- role: developer
comment: Generate an artifact and optionally associate it with a specific task by its task ID
- name: createC2Instance
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/create_c2parameter_instance_webhook'
forward_client_headers: true
permissions:
- role: operator
- role: operation_admin
- role: mythic_admin
- role: developer
comment: Creates a new saved instance of a C2 Profile with a given name
- name: createCallback
definition:
kind: synchronous
@@ -292,17 +303,6 @@ actions:
- role: operation_admin
- role: operator
comment: Creates a new task in Mythic for the specified callback_display_id or callback_display_ids
- name: createC2Instance
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/create_c2parameter_instance_webhook'
forward_client_headers: true
permissions:
- role: operator
- role: operation_admin
- role: mythic_admin
- role: developer
comment: Creates a new saved instance of a C2 Profile with a given name
- name: custombrowserExportFunction
definition:
kind: synchronous
@@ -613,6 +613,16 @@ actions:
- role: mythic_admin
- role: operation_admin
- role: operator
- name: importC2Instance
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/import_c2parameter_instance_webhook'
forward_client_headers: true
permissions:
- role: operator
- role: operation_admin
- role: mythic_admin
- role: developer
- name: importCallbackConfig
definition:
kind: synchronous
@@ -633,16 +643,6 @@ actions:
- role: operation_admin
- role: mythic_admin
- role: developer
- name: importC2Instance
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/import_c2parameter_instance_webhook'
forward_client_headers: true
permissions:
- role: operator
- role: operation_admin
- role: mythic_admin
- role: developer
- name: meHook
definition:
kind: ""
@@ -183,10 +183,10 @@ select_permissions:
filter:
_and:
- _or:
- created_by:
_eq: X-Hasura-User-Id
- operator_id:
_eq: X-Hasura-User-Id
- created_by:
_eq: X-Hasura-User-Id
- operator_id:
_eq: X-Hasura-User-Id
- operator_id:
_lte: X-Hasura-Scope-apitoken-read-operator
- role: operator
@@ -251,13 +251,13 @@ update_permissions:
filter:
_and:
- _or:
- created_by:
_eq: X-Hasura-User-Id
- operator_id:
_eq: X-Hasura-User-Id
- operator:
account_type:
_eq: bot
- created_by:
_eq: X-Hasura-User-Id
- operator_id:
_eq: X-Hasura-User-Id
- operator:
account_type:
_eq: bot
- operator_id:
_lte: X-Hasura-Scope-apitoken-write-operator
check: null
@@ -11,14 +11,6 @@ object_relationships:
- name: payloadtype
using:
foreign_key_constraint_on: payload_type_id
array_relationships:
- name: browserscriptoperations
using:
foreign_key_constraint_on:
column: browserscript_id
table:
name: browserscriptoperation
schema: public
insert_permissions:
- role: mythic_admin
permission:
@@ -81,9 +73,9 @@ select_permissions:
filter:
_and:
- operator_id:
_eq: X-Hasura-User-Id
_eq: X-Hasura-User-Id
- operator_id:
_lte: X-Hasura-Scope-operator-read-operator
_lte: X-Hasura-Scope-operator-read-operator
- role: operation_admin
permission:
columns:
@@ -1,10 +0,0 @@
table:
name: browserscriptoperation
schema: public
object_relationships:
- name: browserscript
using:
foreign_key_constraint_on: browserscript_id
- name: operation
using:
foreign_key_constraint_on: operation_id
@@ -70,7 +70,6 @@ select_permissions:
- updated_at
filter: {}
comment: ""
update_permissions:
- role: mythic_admin
permission:
@@ -22,10 +22,10 @@ select_permissions:
_and:
- eventgroup:
operation_id:
_eq: x-hasura-current-operation-id
_eq: x-hasura-current-operation-id
- eventgroup:
operation_id:
_lte: x-hasura-scope-eventing-read-operation
_lte: x-hasura-scope-eventing-read-operation
comment: ""
- role: operation_admin
permission:
@@ -40,10 +40,10 @@ select_permissions:
_and:
- eventgroup:
operation_id:
_eq: x-hasura-current-operation-id
_eq: x-hasura-current-operation-id
- eventgroup:
operation_id:
_lte: x-hasura-scope-eventing-read-operation
_lte: x-hasura-scope-eventing-read-operation
comment: ""
- role: operator
permission:
@@ -58,10 +58,10 @@ select_permissions:
_and:
- eventgroup:
operation_id:
_eq: x-hasura-current-operation-id
_eq: x-hasura-current-operation-id
- eventgroup:
operation_id:
_lte: x-hasura-scope-eventing-read-operation
_lte: x-hasura-scope-eventing-read-operation
comment: ""
- role: spectator
permission:
@@ -76,8 +76,8 @@ select_permissions:
_and:
- eventgroup:
operation_id:
_eq: x-hasura-current-operation-id
_eq: x-hasura-current-operation-id
- eventgroup:
operation_id:
_lte: x-hasura-scope-eventing-read-operation
_lte: x-hasura-scope-eventing-read-operation
comment: ""
@@ -20,11 +20,11 @@ insert_permissions:
check:
_and:
- callback:
operation_id:
_eq: X-Hasura-current-operation-id
operation_id:
_eq: X-Hasura-current-operation-id
- callback:
operation_id:
_lte: X-Hasura-Scope-callback-write-operation
operation_id:
_lte: X-Hasura-Scope-callback-write-operation
set:
operator_id: x-hasura-user-id
columns:
@@ -36,11 +36,11 @@ insert_permissions:
check:
_and:
- callback:
operation_id:
_eq: X-Hasura-current-operation-id
operation_id:
_eq: X-Hasura-current-operation-id
- callback:
operation_id:
_lte: X-Hasura-Scope-callback-write-operation
_lte: X-Hasura-Scope-callback-write-operation
set:
operator_id: x-hasura-user-id
columns:
@@ -9,13 +9,6 @@ object_relationships:
using:
foreign_key_constraint_on: apitokens_id
array_relationships:
- name: browserscriptoperations
using:
foreign_key_constraint_on:
column: operation_id
table:
name: browserscriptoperation
schema: public
- name: c2profileparametersinstances
using:
foreign_key_constraint_on:
@@ -151,4 +151,4 @@ select_permissions:
callback:
operation_id:
_lte: X-Hasura-Scope-response-read-operation
allow_aggregations: true
allow_aggregations: true
@@ -318,7 +318,6 @@ update_permissions:
- operation_id:
_lte: X-Hasura-Scope-tag-write-operation
delete_permissions:
- role: mythic_admin
permission:
filter:
@@ -4,7 +4,6 @@
- "!include public_attackcommand.yaml"
- "!include public_attacktask.yaml"
- "!include public_browserscript.yaml"
- "!include public_browserscriptoperation.yaml"
- "!include public_buildparameter.yaml"
- "!include public_buildparameterinstance.yaml"
- "!include public_c2profile.yaml"
+1 -1
View File
@@ -15,7 +15,7 @@ import (
)
var DB *sqlx.DB
var currentMigrationVersion int64 = 3003019
var currentMigrationVersion int64 = 3003020
// initial structs made with './tables-to-go -u mythic_user -p [password here] -h [ip here] -v -d mythic_db -of output -pn database_structs'
// package pulled from https://github.com/fraenky8/tables-to-go
@@ -2,6 +2,7 @@
-- SQL in section 'Up' is executed when this migration is applied
alter table "public"."apitokens" add column IF NOT EXISTS "scopes" text[] not null default '{}'::text[];
drop table IF EXISTS "public"."browserscriptoperation";
-- +migrate Down
-- SQL in section 'Down' is executed when this migration is rolled back
@@ -0,0 +1,170 @@
-- +migrate Up
-- SQL in section 'Up' is executed when this migration is applied
create table if not exists "public"."operation_display_counters" (
operation_id integer primary key references "public"."operation"(id) on delete cascade,
last_task_display_id integer not null default 0,
last_callback_display_id integer not null default 0
);
insert into "public"."operation_display_counters" (
operation_id,
last_task_display_id,
last_callback_display_id
)
select
op.id,
coalesce((select max(display_id) from "public"."task" where operation_id = op.id), 0),
coalesce((select max(display_id) from "public"."callback" where operation_id = op.id), 0)
from "public"."operation" op
on conflict (operation_id) do update
set
last_task_display_id = greatest(
"public"."operation_display_counters".last_task_display_id,
excluded.last_task_display_id
),
last_callback_display_id = greatest(
"public"."operation_display_counters".last_callback_display_id,
excluded.last_callback_display_id
);
do $$
begin
if exists (
select 1
from "public"."task"
group by operation_id, display_id
having count(*) > 1
) then
raise exception 'Cannot add task(operation_id, display_id) uniqueness because duplicate task display IDs already exist';
end if;
if exists (
select 1
from "public"."callback"
group by operation_id, display_id
having count(*) > 1
) then
raise exception 'Cannot add callback(operation_id, display_id) uniqueness because duplicate callback display IDs already exist';
end if;
end $$;
create unique index if not exists task_operation_display_id_unique
on "public"."task" using btree (operation_id, display_id);
create unique index if not exists callback_operation_display_id_unique
on "public"."callback" using btree (operation_id, display_id);
create or replace function public.new_task_display_id() returns trigger
language plpgsql
as $$
begin
insert into "public"."operation_display_counters" (operation_id)
values (new.operation_id)
on conflict (operation_id) do nothing;
update "public"."operation_display_counters"
set last_task_display_id = last_task_display_id + 1
where operation_id = new.operation_id
returning last_task_display_id into new.display_id;
return new;
end;
$$;
create or replace function public.new_callback_display_id() returns trigger
language plpgsql
as $$
begin
insert into "public"."operation_display_counters" (operation_id)
values (new.operation_id)
on conflict (operation_id) do nothing;
update "public"."operation_display_counters"
set last_callback_display_id = last_callback_display_id + 1
where operation_id = new.operation_id
returning last_callback_display_id into new.display_id;
return new;
end;
$$;
update "public"."task"
set response_count = response_counts.total
from (
select
task.id,
count(response.id)::integer as total
from "public"."task"
left join "public"."response" on response.task_id = task.id
group by task.id
) response_counts
where task.id = response_counts.id
and task.response_count is distinct from response_counts.total;
create or replace function public.update_task_response_count() returns trigger
language plpgsql
as $$
begin
update "public"."task"
set response_count = response_count + 1
where id = new.task_id;
return new;
end;
$$;
-- +migrate Down
-- SQL in section 'Down' is executed when this migration is rolled back
create or replace function public.update_task_response_count() returns trigger
language plpgsql
as $$
declare
current_max integer;
begin
select count(*)
into current_max
from response
where task_id = new.task_id;
update task set response_count = current_max where id = new.task_id;
return new;
end;
$$;
create or replace function public.new_callback_display_id() returns trigger
language plpgsql
as $$
declare
current_max integer;
begin
select greatest(0, max(display_id))
into current_max
from callback
where operation_id = new.operation_id;
new.display_id := current_max + 1;
return new;
end;
$$;
create or replace function public.new_task_display_id() returns trigger
language plpgsql
as $$
declare
current_max integer;
begin
select greatest(0, max(display_id))
into current_max
from task
where operation_id = new.operation_id;
new.display_id := current_max + 1;
return new;
end;
$$;
drop index if exists "public"."callback_operation_display_id_unique";
drop index if exists "public"."task_operation_display_id_unique";
drop table if exists "public"."operation_display_counters";
@@ -152,12 +152,6 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
return response
}
defer transaction.Rollback()
_, err = transaction.Exec(`LOCK TABLE callback`)
if err != nil {
logging.LogError(err, "Failed to lock callback table")
response.Error = err.Error()
return response
}
if input.EventStepInstanceID != nil {
callback.EventStepInstanceID.Valid = true
callback.EventStepInstanceID.Int64 = int64(*input.EventStepInstanceID)
+10 -12
View File
@@ -555,11 +555,6 @@ func addTaskToDatabase(task *databaseStructs.Task) error {
return err
}
defer transaction.Rollback()
_, err = transaction.Exec(`LOCK TABLE task`)
if err != nil {
logging.LogError(err, "Failed to lock callback table")
return err
}
statement, err := transaction.PrepareNamed(`INSERT INTO task
(agent_task_id,command_name,callback_id,operator_id,command_id,token_id,params,
original_params,display_params,status,tasking_location,parameter_group_name,
@@ -571,26 +566,29 @@ func addTaskToDatabase(task *databaseStructs.Task) error {
:parent_task_id, :subtask_callback_function, :group_callback_function, :subtask_group_name, :operation_id,
:is_interactive_task, :interactive_task_type, :eventstepinstance_id, :status_timestamp_submitted,
:command_payload_type, :mythic_parsed_params)
RETURNING id`)
RETURNING id, display_id`)
if err != nil {
logging.LogError(err, "Failed to make a prepared statement for new task creation")
return err
}
err = statement.Get(&task.ID, task)
defer statement.Close()
taskIDs := struct {
ID int `db:"id"`
DisplayID int `db:"display_id"`
}{}
err = statement.Get(&taskIDs, task)
if err != nil {
logging.LogError(err, "Failed to create new task in database")
return err
}
task.ID = taskIDs.ID
task.DisplayID = taskIDs.DisplayID
err = transaction.Commit()
if err != nil {
logging.LogError(err, "Failed to commit transaction of creating new callback")
logging.LogError(err, "Failed to commit transaction of creating new task")
return err
}
go emitTaskLog(task.ID)
err = database.DB.Get(&task.DisplayID, `SELECT display_id FROM task WHERE id=$1`, task.ID)
if err != nil {
logging.LogError(err, "Failed to get task display ID from task table")
}
return nil
}
-1
View File
@@ -1 +0,0 @@
From prometheuscommunity/postgres-exporter
-61
View File
@@ -1,61 +0,0 @@
pg_stat_statements:
query: "SELECT
pg_get_userbyid(userid) as user,
pg_database.datname,
pg_stat_statements.queryid,
pg_stat_statements.query,
pg_stat_statements.calls,
pg_stat_statements.total_exec_time as time_milliseconds,
pg_stat_statements.rows,
pg_stat_statements.shared_blks_hit,
pg_stat_statements.shared_blks_read,
pg_stat_statements.shared_blks_dirtied,
pg_stat_statements.shared_blks_written,
pg_stat_statements.local_blks_hit,
pg_stat_statements.local_blks_read,
pg_stat_statements.local_blks_dirtied,
pg_stat_statements.local_blks_written,
pg_stat_statements.temp_blks_read,
pg_stat_statements.temp_blks_written,
pg_stat_statements.blk_read_time,
pg_stat_statements.blk_write_time
FROM pg_stat_statements
JOIN pg_database
ON pg_database.oid = pg_stat_statements.dbid"
metrics:
- user:
usage: "LABEL"
description: "The user who executed the statement"
- datname:
usage: "LABEL"
description: "The database in which the statement was executed"
- queryid:
usage: "LABEL"
description: "Internal hash code, computed from the statement's parse tree"
- query:
usage: "LABEL"
description: "Processed query"
- calls:
usage: "COUNTER"
description: "Number of times executed"
- time_milliseconds:
usage: "COUNTER"
description: "Total time spent in the statement, in milliseconds"
- rows:
usage: "COUNTER"
description: "Total number of rows retrieved or affected by the statement"
- shared_blks_hit:
usage: "COUNTER"
description: "Total number of shared block cache hits by the statement"
- shared_blks_read:
usage: "COUNTER"
description: "Total number of shared blocks read by the statement"
- shared_blks_dirtied:
usage: "COUNTER"
description: "Total number of shared blocks dirtied by the statement"
- shared_blks_written:
usage: "COUNTER"
description: "Total number of shared blocks written by the statement"
- local_blks_hit:
usage: "COUNTER"
description: "Total number of local block cache hits by the statement"
-1
View File
@@ -1 +0,0 @@
From prom/prometheus
-10
View File
@@ -1,10 +0,0 @@
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: mythic_prometheus
static_configs:
- targets: ["mythic_prometheus:9090"]
- job_name: mythic_postgres_exporter
static_configs:
- targets: ["mythic_postgres_exporter:9187"]