mirror of
https://github.com/its-a-feature/Mythic
synced 2026-06-08 14:55:38 +00:00
RabbitMQ Auth Context tracking requirements
This commit is contained in:
@@ -1,29 +1,12 @@
|
||||
import { LoginForm } from './pages/Login/LoginForm';
|
||||
import { Settings } from './pages/Settings/Settings';
|
||||
import { PayloadTypesC2Profiles } from './pages/PayloadTypesC2Profiles/PayloadTypesC2Profiles';
|
||||
import { CreatePayload } from './pages/CreatePayload/CreatePayload';
|
||||
import { CreatePayloadWrapper } from './pages/CreateWrapper/CreatePayload';
|
||||
import { EventFeed } from './pages/EventFeed/EventFeed';
|
||||
import { Operations } from './pages/Operations/Operations';
|
||||
import { BrowserScripts } from './pages/BrowserScripts/BrowserScripts';
|
||||
import { Payloads } from './pages/Payloads/Payloads';
|
||||
import { ExpandedCallback } from './pages/ExpandedCallback/ExpandedCallback';
|
||||
import { Home } from './pages/Home/Home';
|
||||
import { LoggedInRoute } from './utilities/LoggedInRoute';
|
||||
import { Callbacks } from './pages/Callbacks/Callbacks';
|
||||
import { Search } from './pages/Search/Search';
|
||||
import React, {createContext} from 'react';
|
||||
import { Typography } from '@mui/material';
|
||||
import { useReactiveVar } from '@apollo/client';
|
||||
import { useDarkMode } from './utilities/useDarkMode';
|
||||
import { SingleTaskView } from './pages/SingleTaskView/SingleTaskView';
|
||||
import { createTheme, ThemeProvider, StyledEngineProvider } from '@mui/material/styles';
|
||||
import { GlobalStyles } from '../themes/GlobalStyles';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import {FailedRefresh, mePreferences, meState, operatorSettingDefaults} from '../cache';
|
||||
import { Reporting } from './pages/Reporting/Reporting';
|
||||
import { MitreAttack } from './pages/MITRE_ATTACK/MitreAttack';
|
||||
import {Tags} from './pages/Tags/Tags';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import {useLazyQuery, gql } from '@apollo/client';
|
||||
//background-color: #282c34;
|
||||
@@ -34,14 +17,37 @@ import { RefreshTokenDialog } from './RefreshTokenDialog';
|
||||
import { MythicDialog } from './MythicComponents/MythicDialog';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import {Eventing} from "./pages/Eventing/Eventing";
|
||||
import {InviteForm} from "./pages/Login/InviteForm";
|
||||
import {snackActions} from "./utilities/Snackbar";
|
||||
import {TopAppBarVertical} from "./TopAppBarVertical";
|
||||
import {MythicLoadingState} from "./MythicComponents/MythicStateDisplay";
|
||||
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||
import * as Icons from '@fortawesome/free-solid-svg-icons';
|
||||
import {Jupyter} from "./pages/Jupyter/Jupyter";
|
||||
import {Hasura} from "./pages/Hasura/Hasura";
|
||||
|
||||
const lazyNamed = (importer, exportName) => React.lazy(() =>
|
||||
importer().then((module) => ({default: module[exportName]}))
|
||||
);
|
||||
|
||||
const LoginForm = lazyNamed(() => import('./pages/Login/LoginForm'), 'LoginForm');
|
||||
const InviteForm = lazyNamed(() => import('./pages/Login/InviteForm'), 'InviteForm');
|
||||
const Settings = lazyNamed(() => import('./pages/Settings/Settings'), 'Settings');
|
||||
const PayloadTypesC2Profiles = lazyNamed(() => import('./pages/PayloadTypesC2Profiles/PayloadTypesC2Profiles'), 'PayloadTypesC2Profiles');
|
||||
const CreatePayload = lazyNamed(() => import('./pages/CreatePayload/CreatePayload'), 'CreatePayload');
|
||||
const CreatePayloadWrapper = lazyNamed(() => import('./pages/CreateWrapper/CreatePayload'), 'CreatePayloadWrapper');
|
||||
const EventFeed = lazyNamed(() => import('./pages/EventFeed/EventFeed'), 'EventFeed');
|
||||
const Operations = lazyNamed(() => import('./pages/Operations/Operations'), 'Operations');
|
||||
const BrowserScripts = lazyNamed(() => import('./pages/BrowserScripts/BrowserScripts'), 'BrowserScripts');
|
||||
const Payloads = lazyNamed(() => import('./pages/Payloads/Payloads'), 'Payloads');
|
||||
const ExpandedCallback = lazyNamed(() => import('./pages/ExpandedCallback/ExpandedCallback'), 'ExpandedCallback');
|
||||
const Home = lazyNamed(() => import('./pages/Home/Home'), 'Home');
|
||||
const Callbacks = lazyNamed(() => import('./pages/Callbacks/Callbacks'), 'Callbacks');
|
||||
const Search = lazyNamed(() => import('./pages/Search/Search'), 'Search');
|
||||
const SingleTaskView = lazyNamed(() => import('./pages/SingleTaskView/SingleTaskView'), 'SingleTaskView');
|
||||
const Reporting = lazyNamed(() => import('./pages/Reporting/Reporting'), 'Reporting');
|
||||
const MitreAttack = lazyNamed(() => import('./pages/MITRE_ATTACK/MitreAttack'), 'MitreAttack');
|
||||
const Tags = lazyNamed(() => import('./pages/Tags/Tags'), 'Tags');
|
||||
const Eventing = lazyNamed(() => import('./pages/Eventing/Eventing'), 'Eventing');
|
||||
const Jupyter = lazyNamed(() => import('./pages/Jupyter/Jupyter'), 'Jupyter');
|
||||
const Hasura = lazyNamed(() => import('./pages/Hasura/Hasura'), 'Hasura');
|
||||
|
||||
// add all fas icons
|
||||
const iconList = Object
|
||||
@@ -757,7 +763,7 @@ export function App(props) {
|
||||
...getModernThemeAdditions(themeMode, preferences),
|
||||
})
|
||||
}catch(error){
|
||||
console.log(error);
|
||||
console.log("error creating theme", error);
|
||||
snackActions.error(error.message);
|
||||
return createTheme({
|
||||
transitions: {
|
||||
@@ -852,6 +858,20 @@ export function App(props) {
|
||||
operatorSettingDefaults.palette.navBarColor.light,
|
||||
topAppBarBottomColor: themeMode === 'dark' ? operatorSettingDefaults.palette.navBarBottomColor.dark :
|
||||
operatorSettingDefaults.palette.navBarBottomColor.light,
|
||||
typography: {
|
||||
fontSize: 12,//operatorSettingDefaults?.fontSize,
|
||||
fontFamily: operatorSettingDefaults?.fontFamily
|
||||
},
|
||||
taskPromptTextColor: themeMode === 'dark' ? operatorSettingDefaults.palette.taskPromptTextColor.dark :
|
||||
operatorSettingDefaults.palette.taskPromptTextColor.light,
|
||||
taskPromptCommandTextColor: themeMode === 'dark' ? operatorSettingDefaults.palette.taskPromptCommandTextColor.dark :
|
||||
operatorSettingDefaults.palette.taskPromptCommandTextColor.light,
|
||||
taskContextColor: themeMode === 'dark' ? operatorSettingDefaults.palette.taskContextColor.dark :
|
||||
operatorSettingDefaults.palette.taskContextColor.light,
|
||||
taskContextImpersonationColor: themeMode === 'dark' ? operatorSettingDefaults.palette.taskContextImpersonationColor.dark :
|
||||
operatorSettingDefaults.palette.taskContextImpersonationColor.light,
|
||||
taskContextExtraColor: themeMode === 'dark' ? operatorSettingDefaults.palette.taskContextExtraColor.dark :
|
||||
operatorSettingDefaults.palette.taskContextExtraColor.light,
|
||||
emptyFolderColor: themeMode === 'dark' ? operatorSettingDefaults.palette.emptyFolderColor.dark :
|
||||
operatorSettingDefaults.palette.emptyFolderColor.light,
|
||||
outputBackgroundColor: themeMode === 'dark' ? operatorSettingDefaults.palette.outputBackgroundColor.dark :
|
||||
@@ -860,10 +880,6 @@ export function App(props) {
|
||||
operatorSettingDefaults.palette.outputTextColor.light,
|
||||
borderColor: themeMode === 'dark' ? operatorSettingDefaults.palette.borderColor.dark :
|
||||
operatorSettingDefaults.palette.borderColor.light,
|
||||
typography: {
|
||||
fontSize: 12,//operatorSettingDefaults?.fontSize,
|
||||
fontFamily: operatorSettingDefaults?.fontFamily
|
||||
},
|
||||
...getModernThemeAdditions(themeMode, operatorSettingDefaults),
|
||||
})
|
||||
}
|
||||
@@ -918,6 +934,9 @@ export function App(props) {
|
||||
<ThemeProvider theme={theme}>
|
||||
<GlobalStyles theme={theme} />
|
||||
<CssBaseline />
|
||||
<div style={{width: '100%', height: '100%', display: "flex", position: "relative",}}>
|
||||
<MythicLoadingState compact title="Loading Preferences" description="Fetching user preferences." sx={{color: "inherit"}} />
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</StyledEngineProvider>
|
||||
)
|
||||
@@ -1001,59 +1020,61 @@ export function App(props) {
|
||||
flexDirection: 'column',
|
||||
overflow:"hidden"
|
||||
}}>
|
||||
<Routes>
|
||||
<Route path='/new/login' element={<LoginForm me={me}/>}/>
|
||||
<Route path='/new/invite' element={<InviteForm me={me}/>}/>
|
||||
<Route path='/' element={<LoggedInRoute me={me}><Home me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new'
|
||||
element={<LoggedInRoute me={me}><Home me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/settings'
|
||||
element={<LoggedInRoute me={me}><Settings me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/payloadtypes'
|
||||
element={<LoggedInRoute me={me}><PayloadTypesC2Profiles
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/eventfeed'
|
||||
element={<LoggedInRoute me={me}><EventFeed me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/createpayload'
|
||||
element={<LoggedInRoute me={me}><CreatePayload me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/createwrapper'
|
||||
element={<LoggedInRoute me={me}><CreatePayloadWrapper
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/payloads'
|
||||
element={<LoggedInRoute me={me}><Payloads me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/c2profiles'
|
||||
element={<LoggedInRoute me={me}><PayloadTypesC2Profiles
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/services/'
|
||||
element={<LoggedInRoute me={me}><PayloadTypesC2Profiles
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/callbacks'
|
||||
element={<LoggedInRoute me={me}><Callbacks me={me}/></LoggedInRoute>}/>
|
||||
<Route path='/new/search'
|
||||
element={<LoggedInRoute me={me}><Search history={props.history}
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/browserscripts'
|
||||
element={<LoggedInRoute me={me}><BrowserScripts me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/task/:taskId'
|
||||
element={<LoggedInRoute me={me}><SingleTaskView me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/tasks/by_range'
|
||||
element={<LoggedInRoute me={me}><SingleTaskView me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/operations'
|
||||
element={<LoggedInRoute me={me}><Operations me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/callbacks/:callbackDisplayId'
|
||||
element={<LoggedInRoute me={me}><ExpandedCallback
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/reporting'
|
||||
element={<LoggedInRoute me={me}><Reporting me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/mitre'
|
||||
element={<LoggedInRoute me={me}><MitreAttack me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/tagtypes'
|
||||
element={<LoggedInRoute me={me}><Tags me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/eventing'
|
||||
element={<LoggedInRoute me={me}><Eventing me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/jupyter' element={<LoggedInRoute me={me}><Jupyter/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/hasura' element={<LoggedInRoute me={me}><Hasura/></LoggedInRoute>}/>
|
||||
</Routes>
|
||||
<React.Suspense fallback={<div style={{height: "100%", width: "100%"}} />}>
|
||||
<Routes>
|
||||
<Route path='/new/login' element={<LoginForm me={me}/>}/>
|
||||
<Route path='/new/invite' element={<InviteForm me={me}/>}/>
|
||||
<Route path='/' element={<LoggedInRoute me={me}><Home me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new'
|
||||
element={<LoggedInRoute me={me}><Home me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/settings'
|
||||
element={<LoggedInRoute me={me}><Settings me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/payloadtypes'
|
||||
element={<LoggedInRoute me={me}><PayloadTypesC2Profiles
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/eventfeed'
|
||||
element={<LoggedInRoute me={me}><EventFeed me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/createpayload'
|
||||
element={<LoggedInRoute me={me}><CreatePayload me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/createwrapper'
|
||||
element={<LoggedInRoute me={me}><CreatePayloadWrapper
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/payloads'
|
||||
element={<LoggedInRoute me={me}><Payloads me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/c2profiles'
|
||||
element={<LoggedInRoute me={me}><PayloadTypesC2Profiles
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/services/'
|
||||
element={<LoggedInRoute me={me}><PayloadTypesC2Profiles
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/callbacks'
|
||||
element={<LoggedInRoute me={me}><Callbacks me={me}/></LoggedInRoute>}/>
|
||||
<Route path='/new/search'
|
||||
element={<LoggedInRoute me={me}><Search history={props.history}
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/browserscripts'
|
||||
element={<LoggedInRoute me={me}><BrowserScripts me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/task/:taskId'
|
||||
element={<LoggedInRoute me={me}><SingleTaskView me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/tasks/by_range'
|
||||
element={<LoggedInRoute me={me}><SingleTaskView me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/operations'
|
||||
element={<LoggedInRoute me={me}><Operations me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/callbacks/:callbackDisplayId'
|
||||
element={<LoggedInRoute me={me}><ExpandedCallback
|
||||
me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/reporting'
|
||||
element={<LoggedInRoute me={me}><Reporting me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/mitre'
|
||||
element={<LoggedInRoute me={me}><MitreAttack me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/tagtypes'
|
||||
element={<LoggedInRoute me={me}><Tags me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/eventing'
|
||||
element={<LoggedInRoute me={me}><Eventing me={me}/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/jupyter' element={<LoggedInRoute me={me}><Jupyter/></LoggedInRoute>}/>
|
||||
<Route exact path='/new/hasura' element={<LoggedInRoute me={me}><Hasura/></LoggedInRoute>}/>
|
||||
</Routes>
|
||||
</React.Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -730,8 +730,8 @@ export function TopAppBarVertical(props) {
|
||||
const [serverName, setServerName] = React.useState("...");
|
||||
useQuery(GET_SETTINGS, {fetchPolicy: "no-cache",
|
||||
onCompleted: (data) => {
|
||||
setServerVersion(data.getGlobalSettings.settings["server_config"]["version"]);
|
||||
setServerName(data.getGlobalSettings.settings["server_config"]["name"]);
|
||||
setServerVersion(data.getGlobalSettings?.settings?.["server_config"]?.["version"]);
|
||||
setServerName(data.getGlobalSettings?.settings?.["server_config"]?.["name"]);
|
||||
}
|
||||
});
|
||||
const toggleDrawerOpen = (e) => {
|
||||
@@ -841,11 +841,13 @@ export function TopAppBarVertical(props) {
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
}}>
|
||||
<StyledListItemIcon>
|
||||
<img src={ReactLogo} onClick={()=>navigate('/new')} width={"32px"} height={"28px"} alt="Mythic" style={{cursor: "pointer"}}/>
|
||||
<img src={ReactLogo} onClick={()=>navigate('/new')} width={"35px"} height={"35px"} alt="Mythic" style={{cursor: "pointer"}}/>
|
||||
</StyledListItemIcon>
|
||||
<ListItemText style={{margin: 0}} primary={
|
||||
<>
|
||||
<Typography style={{ fontSize: 12, color: theme.navigation.muted, display: "inline-block", lineHeight: 1.35}}>
|
||||
<Typography style={{ fontSize: 12, color: theme.navigation.muted, display: "inline-block",
|
||||
marginLeft: "1rem",
|
||||
lineHeight: 1.35}}>
|
||||
<b>Mythic:</b> v{serverVersion}<br/>
|
||||
<b>UI:</b> v{mythicUIVersion}<br/>
|
||||
</Typography>
|
||||
|
||||
@@ -267,7 +267,7 @@ const C2CollapsedGroupEdgeDialog = ({details, onClose, onOpenCallbackTasking}) =
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<DialogTitle className="mythic-c2-action-title">
|
||||
<DialogTitle className="mythic-accent-dialog-title">
|
||||
<Typography component="div" className="mythic-c2-action-title-text">
|
||||
{details?.groupLabel || "Callback Group"} Route Summary
|
||||
</Typography>
|
||||
@@ -385,7 +385,7 @@ const C2ManualRemoveEdgeDialog = ({options = [], onSubmit, onClose}) => {
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<DialogTitle className="mythic-c2-action-title">
|
||||
<DialogTitle className="mythic-accent-dialog-title">
|
||||
<Typography component="div" className="mythic-c2-action-title-text">
|
||||
Remove Active Edge
|
||||
</Typography>
|
||||
@@ -444,7 +444,7 @@ const C2SelectLinkCommandDialog = ({options = [], callback, onSubmit, onClose})
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<DialogTitle className="mythic-c2-action-title">
|
||||
<DialogTitle className="mythic-accent-dialog-title">
|
||||
<Typography component="div" className="mythic-c2-action-title-text">
|
||||
Select Link Command
|
||||
</Typography>
|
||||
|
||||
@@ -515,9 +515,9 @@ export const CallbacksTableSleepCell = React.memo( ({rowData, cellData, updateSl
|
||||
}, areEqual)
|
||||
export const CallbacksTableTagsCell = React.memo(({rowData, cellData}) => {
|
||||
return (
|
||||
<div className="mythic-callback-tagsCell">
|
||||
<div className="mythic-tag-cell mythic-tag-cell-fill">
|
||||
<ViewEditTags target_object={"callback_id"} target_object_id={rowData.id} />
|
||||
<div className="mythic-callback-tagsList">
|
||||
<div className="mythic-tag-list mythic-callback-tag-list">
|
||||
{cellData?.length > 0 ?
|
||||
<TagsDisplay tags={cellData} expand={false} /> :
|
||||
<span className="mythic-callback-tagsEmpty">No tags</span>
|
||||
|
||||
@@ -1328,7 +1328,7 @@ const ProcessBrowserInspector = ({nodeData, rowData, treeRootData, host, group,
|
||||
target_object_id={nodeData.id || 0}
|
||||
me={me} />
|
||||
}
|
||||
<div className="mythic-process-tags-list">
|
||||
<div className="mythic-tag-list mythic-tag-list-truncate">
|
||||
<TagsDisplay tags={nodeData?.tags || []} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -1402,12 +1402,12 @@ const FileBrowserTagsCell = ({rowData, treeRootData, host, me}) => {
|
||||
const nodeData = treeRootData?.[host]?.[rowData["full_path_text"]];
|
||||
return (
|
||||
nodeData?.id ? (
|
||||
<div className="mythic-process-tags-cell">
|
||||
<div className="mythic-tag-cell mythic-tag-cell-fill">
|
||||
<ViewEditTags
|
||||
target_object={"mythictree_id"}
|
||||
target_object_id={nodeData?.id || 0}
|
||||
me={me} />
|
||||
<div className="mythic-process-tags-list">
|
||||
<div className="mythic-tag-list mythic-tag-list-truncate">
|
||||
<TagsDisplay tags={nodeData?.tags || []} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -206,16 +206,16 @@ export function CallbacksTabsTaskingFilterDialog(props) {
|
||||
|
||||
return (
|
||||
<Box className="mythic-tasking-filter-dialog">
|
||||
<DialogTitle id="mythic-draggable-title" className="mythic-tasking-filter-dialog-title">
|
||||
<Box className="mythic-tasking-filter-title-row">
|
||||
<Box className="mythic-tasking-filter-title-icon">
|
||||
<DialogTitle id="mythic-draggable-title" className="mythic-accent-dialog-title">
|
||||
<Box className="mythic-accent-dialog-title-row">
|
||||
<Box className="mythic-accent-dialog-title-icon">
|
||||
<FilterAltIcon fontSize="small" />
|
||||
</Box>
|
||||
<Box sx={{minWidth: 0}}>
|
||||
<Typography component="div" className="mythic-tasking-filter-title-main">
|
||||
Task visibility filters
|
||||
</Typography>
|
||||
<Typography component="div" className="mythic-tasking-filter-title-subtitle">
|
||||
<Typography component="div" className="mythic-accent-dialog-title-subtitle">
|
||||
Control which tasks are shown for this callback.
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -249,7 +249,7 @@ export function CallbacksTabsTaskingFilterDialog(props) {
|
||||
emptyLabel="Any operator"
|
||||
/>
|
||||
</MythicFormField>
|
||||
<Box className="mythic-tasking-filter-switch-stack">
|
||||
<Box className="mythic-column-stack">
|
||||
<MythicFormSwitchRow
|
||||
label="Only tasks with comments"
|
||||
description="Require at least one comment."
|
||||
|
||||
@@ -133,6 +133,20 @@ const GetCommandName = (task) => {
|
||||
return task.command_name;
|
||||
}
|
||||
}
|
||||
const GetTaskHistoryDuplicateKey = (task) => {
|
||||
const commandName = task?.command_name || task?.command?.cmd || "";
|
||||
return `${commandName}\u0000${task?.original_params || ""}`;
|
||||
}
|
||||
const CollapseAdjacentDuplicateTaskHistory = (tasks) => {
|
||||
const collapsedTasks = [];
|
||||
tasks.forEach((task) => {
|
||||
const previousTask = collapsedTasks[collapsedTasks.length - 1];
|
||||
if(previousTask === undefined || GetTaskHistoryDuplicateKey(previousTask) !== GetTaskHistoryDuplicateKey(task)){
|
||||
collapsedTasks.push(task);
|
||||
}
|
||||
});
|
||||
return collapsedTasks;
|
||||
}
|
||||
const GetDefaultValueForType = (parameter_type) => {
|
||||
switch(parameter_type){
|
||||
case "string":
|
||||
@@ -556,7 +570,7 @@ export function CallbacksTabsTaskingInputPreMemo(props){
|
||||
}, [...taskOptions.current]);
|
||||
newTasks.sort((a,b) => a.id > b.id ? -1 : 1);
|
||||
taskOptions.current= newTasks;
|
||||
const filteredOptions = newTasks.filter( c => applyFilteringToTasks(c));
|
||||
const filteredOptions = CollapseAdjacentDuplicateTaskHistory(newTasks.filter( c => applyFilteringToTasks(c)));
|
||||
setFilteredTaskOptions(filteredOptions);
|
||||
}
|
||||
});
|
||||
@@ -580,7 +594,7 @@ export function CallbacksTabsTaskingInputPreMemo(props){
|
||||
});
|
||||
React.useEffect( () => {
|
||||
//console.log("filter updated")
|
||||
const filteredOptions = taskOptions.current.filter( c => applyFilteringToTasks(c));
|
||||
const filteredOptions = CollapseAdjacentDuplicateTaskHistory(taskOptions.current.filter( c => applyFilteringToTasks(c)));
|
||||
setFilteredTaskOptions(filteredOptions);
|
||||
let active = false;
|
||||
if(props.filterOptions?.commandsList?.length > 0){
|
||||
|
||||
@@ -121,7 +121,7 @@ export function ManuallyAddEdgeDialog(props) {
|
||||
if (loading) {
|
||||
return (
|
||||
<>
|
||||
<DialogTitle className="mythic-c2-action-title">Add P2P Edge</DialogTitle>
|
||||
<DialogTitle className="mythic-accent-dialog-title">Add P2P Edge</DialogTitle>
|
||||
<DialogContent dividers={true}>
|
||||
<LinearProgress />
|
||||
</DialogContent>
|
||||
@@ -132,7 +132,7 @@ export function ManuallyAddEdgeDialog(props) {
|
||||
console.error(error);
|
||||
return (
|
||||
<>
|
||||
<DialogTitle className="mythic-c2-action-title">Add P2P Edge</DialogTitle>
|
||||
<DialogTitle className="mythic-accent-dialog-title">Add P2P Edge</DialogTitle>
|
||||
<DialogContent dividers={true}>
|
||||
<MythicDialogBody>
|
||||
<MythicDialogSection title="Unable to load edge options" description={error.message} />
|
||||
@@ -146,7 +146,7 @@ export function ManuallyAddEdgeDialog(props) {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<DialogTitle className="mythic-c2-action-title">
|
||||
<DialogTitle className="mythic-accent-dialog-title">
|
||||
<div className="mythic-dialog-title-row">
|
||||
<div>
|
||||
<Typography component="div" className="mythic-c2-action-title-text">
|
||||
|
||||
@@ -1030,16 +1030,16 @@ export function TaskParametersDialog(props) {
|
||||
const commandName = commandInfo.cmd || props.command?.cmd || "Command";
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DialogTitle id="mythic-draggable-title" className="mythic-task-parameters-title">
|
||||
<Box className="mythic-task-parameters-title-row">
|
||||
<Box className="mythic-task-parameters-title-icon">
|
||||
<DialogTitle id="mythic-draggable-title" className="mythic-accent-dialog-title">
|
||||
<Box className="mythic-accent-dialog-title-row">
|
||||
<Box className="mythic-accent-dialog-title-icon">
|
||||
<TerminalIcon fontSize="small" />
|
||||
</Box>
|
||||
<Box className="mythic-task-parameters-title-copy">
|
||||
<Typography component="div" className="mythic-task-parameters-title-main">
|
||||
{commandName}
|
||||
</Typography>
|
||||
<Typography component="div" className="mythic-task-parameters-title-subtitle">
|
||||
<Typography component="div" className="mythic-accent-dialog-title-subtitle">
|
||||
Command parameters
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -880,7 +880,7 @@ export function TaskParametersDialogRow(props){
|
||||
</Typography>
|
||||
<Switch checked={treatNewlinesAsNewEntries} onChange={toggleTreatNewlinesAsNewEntries} color={"info"} />
|
||||
</Box>
|
||||
<Box className="mythic-task-array-list">
|
||||
<Box className="mythic-column-stack">
|
||||
{arrayValue.map( (a, i) => (
|
||||
<Box className="mythic-task-array-entry" key={'array' + props.name + i}>
|
||||
<MythicStyledTooltip title={"Remove array element"}>
|
||||
@@ -912,7 +912,7 @@ export function TaskParametersDialogRow(props){
|
||||
</Typography>
|
||||
<Switch checked={treatNewlinesAsNewEntries} onChange={toggleTreatNewlinesAsNewEntries} color={"info"} />
|
||||
</Box>
|
||||
<Box className="mythic-task-array-list">
|
||||
<Box className="mythic-column-stack">
|
||||
{typedArrayValue.map( (a, i) => (
|
||||
<Box className="mythic-task-typed-array-entry" key={'typedarray' + props.name + i}>
|
||||
<MythicStyledTooltip title={"Remove typed array element"}>
|
||||
@@ -1046,7 +1046,7 @@ export function TaskParametersDialogRow(props){
|
||||
return (
|
||||
<Box className="mythic-task-agent-connect-editor">
|
||||
{openAdditionalPayloadOnHostMenu ? (
|
||||
<Box className="mythic-task-agent-connect-panel">
|
||||
<Box className="mythic-task-agent-connect-panel mythic-column-stack">
|
||||
<ParameterFieldRow label="Hostname">
|
||||
<MythicTextField requiredValue={true} placeholder={"hostname"} value={agentConnectNewHost} multiline={false} autoFocus={props.autoFocus}
|
||||
onChange={onChangeAgentConnectNewHost} display="inline-block" marginBottom={"0px"}/>
|
||||
@@ -1087,7 +1087,7 @@ export function TaskParametersDialogRow(props){
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<Box className="mythic-task-agent-connect-panel">
|
||||
<Box className="mythic-task-agent-connect-panel mythic-column-stack">
|
||||
<ParameterFieldRow label="Host">
|
||||
{agentConnectHostOptions.length === 0 ? (
|
||||
<ParameterEmptyInline>No hosts available</ParameterEmptyInline>
|
||||
@@ -1183,7 +1183,7 @@ export function TaskParametersDialogRow(props){
|
||||
<Typography component="div" className="mythic-task-agent-connect-parameters-title">
|
||||
C2 profile parameters
|
||||
</Typography>
|
||||
<Box className="mythic-task-agent-connect-parameter-list">
|
||||
<Box className="mythic-column-stack">
|
||||
{(selectedC2Profile.parameters || []).map( (opt, i) => (
|
||||
<Box className="mythic-task-agent-connect-parameter" key={"agentconnectparameters" + props.name + i}>
|
||||
<Typography component="div" className="mythic-task-agent-connect-parameter-name">
|
||||
@@ -1202,7 +1202,7 @@ export function TaskParametersDialogRow(props){
|
||||
}
|
||||
case "CredentialJson":
|
||||
return (
|
||||
<Box className="mythic-task-credential-editor">
|
||||
<Box className="mythic-column-stack">
|
||||
{createCredentialDialogOpen &&
|
||||
<MythicDialog fullWidth={true} maxWidth="md" open={createCredentialDialogOpen}
|
||||
onClose={()=>{setCreateCredentialDialogOpen(false);}}
|
||||
|
||||
+2
-2
@@ -13,11 +13,11 @@ export function CreatePayloadBuildParametersTable(props){
|
||||
<div className="mythic-create-parameter-scroll">
|
||||
{buildParameters.map(b => (
|
||||
b.parameters.length > 0 &&
|
||||
<section className="mythic-create-parameter-group" key={b?.name || 'undefined'}>
|
||||
<section className="mythic-create-parameter-group mythic-column-stack" key={b?.name || 'undefined'}>
|
||||
{b.name !== '' && b.name !== undefined &&
|
||||
<div className="mythic-create-parameter-group-header">{b.name}</div>
|
||||
}
|
||||
<div className="mythic-create-parameter-list">
|
||||
<div className="mythic-column-stack">
|
||||
{b.parameters.map( (op) => (
|
||||
<CreatePayloadParameter
|
||||
displayMode="card"
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ export function CreatePayloadC2ProfileParametersTable(props){
|
||||
props.onChange(props.name, paramName, value, error);
|
||||
}
|
||||
return (
|
||||
<div className="mythic-create-parameter-list">
|
||||
<div className="mythic-column-stack">
|
||||
{props.c2profileparameters.map( (op) => (
|
||||
<CreatePayloadParameter
|
||||
displayMode="card"
|
||||
|
||||
@@ -101,7 +101,7 @@ export function Step3SelectCommands(props){
|
||||
setCommandOptions(allCommands);
|
||||
if(props.prevData !== undefined){
|
||||
const selectedCommands = allCommands.map( (c) => {
|
||||
if(props.prevData.includes(c.cmd)){
|
||||
if(props.prevData.includes(c.cmd) || c?.attributes?.builtin){
|
||||
return {...c, selected: true};
|
||||
}else{
|
||||
return {...c, selected: false};
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import {CreatePayloadParameter} from '../CreatePayload/CreatePayloadParameter';
|
||||
export function CreatePayloadBuildParametersTable(props){
|
||||
|
||||
return (
|
||||
<div className="mythic-create-parameter-list">
|
||||
<div className="mythic-column-stack">
|
||||
{props.buildParameters.map( (op) => (
|
||||
<CreatePayloadParameter displayMode="card" key={"buildparamtablerow" + op.id} onChange={props.onChange} {...op} />
|
||||
))}
|
||||
|
||||
@@ -905,7 +905,7 @@ const CreateEventingStep1 = ({finished, back, first, last, cancel, prevData}) =>
|
||||
<div className="mythic-eventing-metadata-field">
|
||||
<div className="mythic-eventing-metadata-label">Trigger data</div>
|
||||
{triggerOptionsData[trigger]?.trigger_data?.length > 0 ?
|
||||
(<div className="mythic-eventing-trigger-parameter-list">
|
||||
(<div className="mythic-eventing-trigger-parameter-list mythic-column-stack">
|
||||
{triggerData?.map( t => (
|
||||
<CreatePayloadParameter key={t.name} onChange={onChangeTriggerData} displayMode="card" {...t} />
|
||||
))}
|
||||
@@ -1005,7 +1005,7 @@ const EventingActionDataShell = ({children}) => (
|
||||
<Typography component="div" className="mythic-eventing-step-help-text">
|
||||
{eventingActionDataHelp}
|
||||
</Typography>
|
||||
<div className="mythic-eventing-action-data-list">
|
||||
<div className="mythic-column-stack">
|
||||
{children}
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -854,7 +854,7 @@ function EventStepRender({selectedEventGroup, useSuppliedData}) {
|
||||
{selectedEventGroup.id > 0 &&
|
||||
<div className="mythic-eventing-flow-badge">Event group {selectedEventGroup.id}</div>
|
||||
}
|
||||
<Controls showInteractive={false} className="mythic-eventing-flow-controls mythic-graph-controls">
|
||||
<Controls showInteractive={false} className="mythic-graph-controls-muted-hover">
|
||||
</Controls>
|
||||
</ReactFlow>
|
||||
{openContextMenu && typeof document !== "undefined" && createPortal(
|
||||
@@ -1159,7 +1159,7 @@ function EventStepInstanceRender({selectedEventGroupInstance}) {
|
||||
onNodeContextMenu={onNodeContextMenu}
|
||||
>
|
||||
<div className="mythic-eventing-flow-badge">Instance {selectedEventGroupInstance}</div>
|
||||
<Controls showInteractive={false} className="mythic-eventing-flow-controls mythic-graph-controls">
|
||||
<Controls showInteractive={false} className="mythic-graph-controls-muted-hover">
|
||||
</Controls>
|
||||
</ReactFlow>
|
||||
{openContextMenu && typeof document !== "undefined" && createPortal(
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ export function InstalledServiceMetadataSummary({items = [], description}) {
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div className="mythic-installed-service-metadata-summary">
|
||||
<div className="mythic-column-stack">
|
||||
{visibleItems.length > 0 &&
|
||||
<div className="mythic-installed-service-metadata-grid">
|
||||
{visibleItems.map((item) => (
|
||||
|
||||
@@ -10,7 +10,7 @@ import {ImportPayloadConfigDialog} from './ImportPayloadConfigDialog';
|
||||
import { MythicDialog } from '../../MythicComponents/MythicDialog';
|
||||
import {useNavigate} from 'react-router-dom';
|
||||
import { Backdrop } from '@mui/material';
|
||||
import {MythicPageHeader, MythicPageHeaderChip} from "../../MythicComponents/MythicPageHeader";
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
import {MythicTablePagination} from "../../MythicComponents/MythicTablePagination";
|
||||
import {MythicToolbarButton, MythicToolbarToggle} from "../../MythicComponents/MythicTableToolbar";
|
||||
import {MythicLoadingState, MythicTableEmptyState} from "../../MythicComponents/MythicStateDisplay";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import {ReportingTable} from './ReportingTable';
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
import {MythicPageHeader, MythicPageHeaderChip} from "../../MythicComponents/MythicPageHeader";
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
import SummarizeIcon from '@mui/icons-material/Summarize';
|
||||
|
||||
|
||||
@@ -12,12 +12,6 @@ export function Reporting() {
|
||||
icon={<SummarizeIcon fontSize="small" />}
|
||||
title={"Mythic Report Generation"}
|
||||
subtitle={"Generate operation reports from Mythic data and selected report sections."}
|
||||
meta={
|
||||
<>
|
||||
<MythicPageHeaderChip label="HTML" />
|
||||
<MythicPageHeaderChip label="JSON" />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<ReportingTable />
|
||||
</MythicPageBody>
|
||||
|
||||
@@ -210,11 +210,6 @@ export function ReportingTable(){
|
||||
Report sections, output format, and callback exclusions.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{alignItems: "center", display: "flex", flexWrap: "wrap", gap: 0.5}}>
|
||||
<MythicPageHeaderChip label={selectedOutputFormat.toUpperCase()} status="info" />
|
||||
<MythicPageHeaderChip label={`${enabledSectionCount} optional ${enabledSectionCount === 1 ? "section" : "sections"}`} status={enabledSectionCount > 0 ? "success" : "neutral"} />
|
||||
{hasExclusions && <MythicPageHeaderChip label="Exclusions active" status="warning" />}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<MythicTableToolbar>
|
||||
@@ -228,14 +223,6 @@ export function ReportingTable(){
|
||||
))}
|
||||
</MythicToolbarSelect>
|
||||
</MythicTableToolbarGroup>
|
||||
<MythicTableToolbarGroup grow label="Current selection">
|
||||
<Box sx={{alignItems: "center", display: "flex", flexWrap: "wrap", gap: 0.45}}>
|
||||
<MythicPageHeaderChip icon={<ArticleOutlinedIcon />} label={`${selectedOutputFormat.toUpperCase()} report`} />
|
||||
{includeMITREPerTask && <MythicPageHeaderChip label="MITRE per task" status="success" />}
|
||||
{includeMITREOverview && <MythicPageHeaderChip label="MITRE overview" status="success" />}
|
||||
{includeOutput && <MythicPageHeaderChip label="Command output" status="success" />}
|
||||
</Box>
|
||||
</MythicTableToolbarGroup>
|
||||
<MythicTableToolbarGroup label="Actions">
|
||||
<MythicToolbarButton
|
||||
className="mythic-toolbar-button-hover-success"
|
||||
|
||||
@@ -197,9 +197,9 @@ function CustomBrowserTableRow(props){
|
||||
</div>
|
||||
</MythicStyledTableCell>
|
||||
<MythicStyledTableCell>
|
||||
<div className="mythic-custom-browser-tags-cell">
|
||||
<div className="mythic-tag-cell">
|
||||
<ViewEditTags target_object={"mythictree_id"} target_object_id={props.id} me={me} />
|
||||
<div className="mythic-custom-browser-tags-list">
|
||||
<div className="mythic-tag-list mythic-tag-list-truncate">
|
||||
<TagsDisplay tags={props.tags} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,39 +1,100 @@
|
||||
export function copyStringToClipboard(str) {
|
||||
let el = document.createElement('textarea');
|
||||
function copyStringToClipboardWithExecCommand(text) {
|
||||
let copyTarget = null;
|
||||
const selectedRanges = [];
|
||||
const selection = document.getSelection();
|
||||
const activeElement = document.activeElement;
|
||||
const onCopy = (event) => {
|
||||
if(event.clipboardData){
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
try {
|
||||
event.clipboardData.clearData();
|
||||
} catch {
|
||||
// Some browsers expose setData without allowing clearData during copy.
|
||||
}
|
||||
event.clipboardData.setData("text/plain", text);
|
||||
}
|
||||
};
|
||||
try {
|
||||
// Create new element
|
||||
|
||||
// Set value (string to be copied)
|
||||
el.value = str;
|
||||
if(el.value === ""){
|
||||
el.value = " ";
|
||||
if(selection){
|
||||
for(let i = 0; i < selection.rangeCount; i++){
|
||||
selectedRanges.push(selection.getRangeAt(i).cloneRange());
|
||||
}
|
||||
}
|
||||
// Set non-editable to avoid focus and move outside of view
|
||||
el.setAttribute('readonly', '');
|
||||
el.style = {position: 'absolute', left: '-9999px'};
|
||||
document.body.appendChild(el);
|
||||
// Select text inside element
|
||||
el.select();
|
||||
// Copy text to clipboard
|
||||
let success = document.execCommand('copy');
|
||||
if(!success){
|
||||
console.log("failed to copy data");
|
||||
copyTarget = document.createElement('span');
|
||||
copyTarget.textContent = text;
|
||||
copyTarget.setAttribute('aria-hidden', 'true');
|
||||
copyTarget.style.position = 'fixed';
|
||||
copyTarget.style.top = '0';
|
||||
copyTarget.style.left = '0';
|
||||
copyTarget.style.width = '1px';
|
||||
copyTarget.style.height = '1px';
|
||||
copyTarget.style.margin = '-1px';
|
||||
copyTarget.style.padding = '0';
|
||||
copyTarget.style.border = '0';
|
||||
copyTarget.style.overflow = 'hidden';
|
||||
copyTarget.style.clip = 'rect(0, 0, 0, 0)';
|
||||
copyTarget.style.whiteSpace = 'pre';
|
||||
copyTarget.style.userSelect = 'text';
|
||||
copyTarget.style.webkitUserSelect = 'text';
|
||||
copyTarget.style.MozUserSelect = 'text';
|
||||
document.body.appendChild(copyTarget);
|
||||
if(selection){
|
||||
const copyRange = document.createRange();
|
||||
copyRange.selectNodeContents(copyTarget);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(copyRange);
|
||||
}
|
||||
success = document.execCommand('cut');
|
||||
if(!success){
|
||||
console.log("failed to cut data");
|
||||
}
|
||||
navigator?.clipboard?.writeText(el.value);
|
||||
// Remove temporary element
|
||||
document.body.removeChild(el);
|
||||
return true;
|
||||
document.addEventListener("copy", onCopy, true);
|
||||
return document.execCommand('copy');
|
||||
} catch (error) {
|
||||
document.body.removeChild(el);
|
||||
console.log("warning", "Failed to copy to clipboard: " + error.toString());
|
||||
console.log("warning", "Failed to copy to clipboard with fallback: " + error.toString());
|
||||
return false;
|
||||
} finally {
|
||||
document.removeEventListener("copy", onCopy, true);
|
||||
if(copyTarget?.parentNode){
|
||||
copyTarget.parentNode.removeChild(copyTarget);
|
||||
}
|
||||
if(selection){
|
||||
try {
|
||||
selection.removeAllRanges();
|
||||
selectedRanges.forEach((range) => selection.addRange(range));
|
||||
} catch (error) {
|
||||
console.log("warning", "Failed to restore selection after clipboard copy: " + error.toString());
|
||||
}
|
||||
}
|
||||
if(activeElement?.focus){
|
||||
try {
|
||||
activeElement.focus({preventScroll: true});
|
||||
} catch (error) {
|
||||
activeElement.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function copyStringToClipboard(str) {
|
||||
const text = str === undefined || str === null || String(str) === "" ? " " : String(str);
|
||||
let clipboardApiStarted = false;
|
||||
try {
|
||||
if(navigator?.clipboard?.writeText){
|
||||
clipboardApiStarted = true;
|
||||
navigator.clipboard.writeText(text).catch((error) => {
|
||||
console.log("warning", "Failed to copy to clipboard with Clipboard API: " + error.toString());
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("warning", "Failed to start Clipboard API copy: " + error.toString());
|
||||
}
|
||||
const fallbackSuccess = copyStringToClipboardWithExecCommand(text);
|
||||
if(clipboardApiStarted || fallbackSuccess){
|
||||
return true;
|
||||
}
|
||||
console.log("failed to copy data");
|
||||
return false;
|
||||
}
|
||||
|
||||
export function downloadFileFromMemory(output, filename){
|
||||
const dataBlob = new Blob([output], {type: 'application/octet-stream'});
|
||||
const ele = document.getElementById("download_config");
|
||||
|
||||
@@ -823,8 +823,7 @@ tspan {
|
||||
word-break: break-word;
|
||||
}
|
||||
.mythic-process-name-cell,
|
||||
.mythic-process-string-cell,
|
||||
.mythic-process-tags-cell {
|
||||
.mythic-process-string-cell {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
@@ -884,10 +883,18 @@ tspan {
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
.mythic-process-tags-cell {
|
||||
.mythic-tag-cell {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mythic-process-tags-cell .MuiIconButton-root {
|
||||
.mythic-tag-cell-fill {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.mythic-tag-cell .MuiIconButton-root {
|
||||
align-items: center !important;
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.045)" : "rgba(0,0,0,0.025)"} !important;
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
|
||||
@@ -902,15 +909,15 @@ tspan {
|
||||
transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
|
||||
width: 22px;
|
||||
}
|
||||
.mythic-process-tags-cell .MuiIconButton-root:hover {
|
||||
.mythic-tag-cell .MuiIconButton-root:hover {
|
||||
background-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.16 : 0.09)} !important;
|
||||
border-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.44 : 0.3)};
|
||||
color: ${(props) => props.theme.palette.primary.main} !important;
|
||||
}
|
||||
.mythic-process-tags-cell .MuiIconButton-root svg {
|
||||
.mythic-tag-cell .MuiIconButton-root svg {
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
.mythic-process-tags-list {
|
||||
.mythic-tag-list {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
@@ -919,21 +926,23 @@ tspan {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mythic-process-tags-list .MuiChip-root {
|
||||
.mythic-tag-list .MuiChip-root {
|
||||
flex: 0 0 auto;
|
||||
float: none !important;
|
||||
height: 18px !important;
|
||||
max-width: 5.5rem;
|
||||
}
|
||||
.mythic-process-tags-list .MuiChip-label {
|
||||
.mythic-tag-list .MuiChip-label {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 800;
|
||||
min-width: 0;
|
||||
overflow: hidden !important;
|
||||
padding-left: 0.35rem;
|
||||
padding-right: 0.35rem;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.mythic-tag-list-truncate .MuiChip-label {
|
||||
min-width: 0;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.mythic-process-action-button.MuiIconButton-root {
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
|
||||
border-radius: ${(props) => props.theme.shape.borderRadius}px;
|
||||
@@ -1434,7 +1443,7 @@ tspan {
|
||||
background-color: ${(props) => props.theme.surfaces?.raised || props.theme.palette.background.paper};
|
||||
color: ${(props) => props.theme.palette.text.primary};
|
||||
}
|
||||
.mythic-tasking-filter-dialog-title.MuiDialogTitle-root {
|
||||
.mythic-accent-dialog-title.MuiDialogTitle-root {
|
||||
background-image: ${getSectionHeaderGradient} !important;
|
||||
border-bottom: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
color: ${(props) => props.theme.pageHeaderText?.main || props.theme.palette.text.primary};
|
||||
@@ -1442,7 +1451,10 @@ tspan {
|
||||
padding: 1rem 1.15rem !important;
|
||||
position: relative;
|
||||
}
|
||||
.mythic-tasking-filter-dialog-title.MuiDialogTitle-root::before {
|
||||
.mythic-accent-dialog-title.MuiDialogTitle-root::before,
|
||||
.mythic-detail-section-header::before,
|
||||
.mythic-section-header::before,
|
||||
.mythic-dashboard-edit-toolbar::before {
|
||||
background-color: ${getSectionHeaderAccent};
|
||||
bottom: 0;
|
||||
box-shadow: 0 0 0 1px ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.2)};
|
||||
@@ -1452,7 +1464,7 @@ tspan {
|
||||
top: 0;
|
||||
width: 6px;
|
||||
}
|
||||
.mythic-tasking-filter-title-row {
|
||||
.mythic-accent-dialog-title-row {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 0.8rem;
|
||||
@@ -1460,7 +1472,7 @@ tspan {
|
||||
min-height: 42px;
|
||||
padding-left: 0.55rem;
|
||||
}
|
||||
.mythic-tasking-filter-title-icon {
|
||||
.mythic-accent-dialog-title-icon {
|
||||
align-items: center;
|
||||
background-color: ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.12)};
|
||||
border: 1px solid ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.24)};
|
||||
@@ -1477,7 +1489,7 @@ tspan {
|
||||
font-weight: 850;
|
||||
line-height: 1.15;
|
||||
}
|
||||
.mythic-tasking-filter-title-subtitle {
|
||||
.mythic-accent-dialog-title-subtitle {
|
||||
color: ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.78)};
|
||||
font-size: 0.76rem;
|
||||
font-weight: 600;
|
||||
@@ -1530,7 +1542,7 @@ tspan {
|
||||
border-color: ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
|
||||
color: ${(props) => props.theme.palette.text.secondary};
|
||||
}
|
||||
.mythic-tasking-filter-switch-stack {
|
||||
.mythic-column-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.55rem;
|
||||
@@ -1632,43 +1644,6 @@ tspan {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
.mythic-task-parameters-title.MuiDialogTitle-root {
|
||||
background-image: ${getSectionHeaderGradient} !important;
|
||||
border-bottom: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
color: ${(props) => props.theme.pageHeaderText?.main || props.theme.palette.text.primary};
|
||||
cursor: move;
|
||||
padding: 1rem 1.15rem !important;
|
||||
position: relative;
|
||||
}
|
||||
.mythic-task-parameters-title.MuiDialogTitle-root::before {
|
||||
background-color: ${getSectionHeaderAccent};
|
||||
bottom: 0;
|
||||
box-shadow: 0 0 0 1px ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.2)};
|
||||
content: "";
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 6px;
|
||||
}
|
||||
.mythic-task-parameters-title-row {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 0.8rem;
|
||||
min-height: 42px;
|
||||
min-width: 0;
|
||||
padding-left: 0.55rem;
|
||||
}
|
||||
.mythic-task-parameters-title-icon {
|
||||
align-items: center;
|
||||
background-color: ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.12)};
|
||||
border: 1px solid ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.24)};
|
||||
border-radius: ${(props) => props.theme.shape.borderRadius}px;
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
height: 32px;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
}
|
||||
.mythic-task-parameters-title-copy {
|
||||
flex: 1 1 auto;
|
||||
min-width: 12rem;
|
||||
@@ -1680,13 +1655,6 @@ tspan {
|
||||
line-height: 1.15;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.mythic-task-parameters-title-subtitle {
|
||||
color: ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.78)};
|
||||
font-size: 0.76rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
.mythic-task-parameters-title-meta {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@@ -2033,14 +2001,6 @@ tspan {
|
||||
gap: 0.65rem;
|
||||
min-width: 0;
|
||||
}
|
||||
.mythic-task-array-list,
|
||||
.mythic-task-agent-connect-panel,
|
||||
.mythic-task-agent-connect-parameter-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.55rem;
|
||||
min-width: 0;
|
||||
}
|
||||
.mythic-task-array-entry,
|
||||
.mythic-task-typed-array-entry {
|
||||
align-items: center;
|
||||
@@ -2116,12 +2076,6 @@ tspan {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
.mythic-task-credential-editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.55rem;
|
||||
min-width: 0;
|
||||
}
|
||||
.mythic-task-credential-actions {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@@ -2173,12 +2127,12 @@ tspan {
|
||||
padding: 0.85rem 1rem !important;
|
||||
}
|
||||
@media screen and (max-width: 900px) {
|
||||
.mythic-task-parameters-title-row,
|
||||
.mythic-accent-dialog-title-row,
|
||||
.mythic-task-parameters-group-card,
|
||||
.mythic-task-parameter-card {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.mythic-task-parameters-title-row {
|
||||
.mythic-accent-dialog-title-row {
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
@@ -2871,60 +2825,8 @@ tspan {
|
||||
display: block;
|
||||
font-size: inherit;
|
||||
}
|
||||
.mythic-callback-tagsCell {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
.mythic-callback-tagsCell .MuiIconButton-root {
|
||||
align-items: center !important;
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.045)" : "rgba(0,0,0,0.025)"} !important;
|
||||
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} !important;
|
||||
display: inline-flex !important;
|
||||
flex: 0 0 auto;
|
||||
float: none !important;
|
||||
height: 22px;
|
||||
justify-content: center !important;
|
||||
padding: 0 !important;
|
||||
transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
|
||||
width: 22px;
|
||||
}
|
||||
.mythic-callback-tagsCell .MuiIconButton-root:hover {
|
||||
background-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.16 : 0.09)} !important;
|
||||
border-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.44 : 0.3)};
|
||||
color: ${(props) => props.theme.palette.primary.main} !important;
|
||||
}
|
||||
.mythic-callback-tagsCell .MuiIconButton-root svg {
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
.mythic-callback-tagsList {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.2rem;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mythic-callback-tagsCell .MuiChip-root {
|
||||
.mythic-callback-tag-list .MuiChip-root {
|
||||
border: 1px solid ${(props) => alpha(props.theme.palette.common.black, props.theme.palette.mode === "dark" ? 0.2 : 0.08)};
|
||||
flex: 0 0 auto;
|
||||
float: none !important;
|
||||
height: 18px !important;
|
||||
max-width: 5.5rem;
|
||||
}
|
||||
.mythic-callback-tagsCell .MuiChip-label {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 800;
|
||||
padding-left: 0.35rem;
|
||||
padding-right: 0.35rem;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.mythic-callback-tagsEmpty {
|
||||
color: ${(props) => props.theme.palette.text.disabled};
|
||||
@@ -3650,24 +3552,6 @@ tspan {
|
||||
padding: 0.22rem 0.48rem;
|
||||
text-transform: none;
|
||||
}
|
||||
.mythic-c2-action-title.MuiDialogTitle-root {
|
||||
background-image: ${getSectionHeaderGradient} !important;
|
||||
border-bottom: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
color: ${(props) => props.theme.pageHeaderText?.main || props.theme.palette.text.primary};
|
||||
cursor: move;
|
||||
padding: 1rem 1.15rem !important;
|
||||
position: relative;
|
||||
}
|
||||
.mythic-c2-action-title.MuiDialogTitle-root::before {
|
||||
background-color: ${getSectionHeaderAccent};
|
||||
bottom: 0;
|
||||
box-shadow: 0 0 0 1px ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.2)};
|
||||
content: "";
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 6px;
|
||||
}
|
||||
.mythic-c2-action-title-text.MuiTypography-root {
|
||||
color: ${(props) => props.theme.pageHeaderText?.main || props.theme.palette.text.primary};
|
||||
font-size: 1rem;
|
||||
@@ -4565,16 +4449,6 @@ tspan {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.mythic-detail-section-header::before {
|
||||
background-color: ${getSectionHeaderAccent};
|
||||
bottom: 0;
|
||||
box-shadow: 0 0 0 1px ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.2)};
|
||||
content: "";
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 6px;
|
||||
}
|
||||
.mythic-section-header {
|
||||
background-image: ${getSectionHeaderGradient} !important;
|
||||
border-color: ${(props) => alpha(getSectionHeaderAccent(props), props.theme.palette.mode === "dark" ? 0.55 : 0.38)} !important;
|
||||
@@ -4583,16 +4457,6 @@ tspan {
|
||||
padding-left: 1rem !important;
|
||||
position: relative !important;
|
||||
}
|
||||
.mythic-section-header::before {
|
||||
background-color: ${getSectionHeaderAccent};
|
||||
bottom: 0;
|
||||
box-shadow: 0 0 0 1px ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.2)};
|
||||
content: "";
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 6px;
|
||||
}
|
||||
.mythic-detail-section-title {
|
||||
color: ${(props) => props.theme.pageHeaderText?.main || props.theme.palette.text.primary};
|
||||
font-size: 1.25rem;
|
||||
@@ -6332,12 +6196,6 @@ tspan {
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
}
|
||||
.mythic-create-parameter-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.55rem;
|
||||
min-width: 0;
|
||||
}
|
||||
.mythic-create-parameter-group-header {
|
||||
background-image: ${getSectionHeaderGradient};
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
|
||||
@@ -6385,12 +6243,6 @@ tspan {
|
||||
padding-left: 0.65rem;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.mythic-create-parameter-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.55rem;
|
||||
min-width: 0;
|
||||
}
|
||||
.mythic-create-parameter-card {
|
||||
background-color: ${(props) => props.theme.palette.background.paper};
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
|
||||
@@ -6637,7 +6489,8 @@ tspan {
|
||||
.mythic-dashboard-card-body-empty {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
.mythic-dashboard-icon-button {
|
||||
.mythic-dashboard-icon-button,
|
||||
.mythic-table-row-icon-action {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.035)"} !important;
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
border-radius: ${(props) => props.theme.shape.borderRadius}px !important;
|
||||
@@ -6647,7 +6500,8 @@ tspan {
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
}
|
||||
.mythic-dashboard-icon-button:hover {
|
||||
.mythic-dashboard-icon-button:hover,
|
||||
.mythic-table-row-icon-action:hover {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.06)"} !important;
|
||||
border-color: ${(props) => props.theme.table?.border || props.theme.borderColor} !important;
|
||||
}
|
||||
@@ -6662,7 +6516,10 @@ tspan {
|
||||
border-color: ${(props) => props.theme.palette.info.main + "88"} !important;
|
||||
color: ${(props) => props.theme.palette.info.main} !important;
|
||||
}
|
||||
.mythic-dashboard-icon-button.Mui-disabled {
|
||||
.mythic-dashboard-icon-button.Mui-disabled,
|
||||
.mythic-dashboard-table-action.Mui-disabled,
|
||||
.mythic-table-row-action.Mui-disabled,
|
||||
.mythic-table-row-icon-action.Mui-disabled {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.02)"} !important;
|
||||
border-color: ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
color: ${(props) => props.theme.palette.text.disabled} !important;
|
||||
@@ -6675,7 +6532,8 @@ tspan {
|
||||
min-width: 5.6rem !important;
|
||||
}
|
||||
.mythic-dashboard-primary-button,
|
||||
.mythic-dashboard-table-action {
|
||||
.mythic-dashboard-table-action,
|
||||
.mythic-table-row-action {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.035)"} !important;
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
border-radius: ${(props) => props.theme.shape.borderRadius}px !important;
|
||||
@@ -6703,11 +6561,6 @@ tspan {
|
||||
border-color: ${(props) => props.theme.palette.error.main + "88"} !important;
|
||||
color: ${(props) => props.theme.palette.error.main} !important;
|
||||
}
|
||||
.mythic-dashboard-table-action.Mui-disabled {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.02)"} !important;
|
||||
border-color: ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
color: ${(props) => props.theme.palette.text.disabled} !important;
|
||||
}
|
||||
.mythic-dashboard-chart-body {
|
||||
gap: 0.35rem;
|
||||
padding: 0.5rem 0.55rem 0.55rem;
|
||||
@@ -7268,16 +7121,6 @@ tspan {
|
||||
padding: 0.52rem 0.65rem 0.52rem 1rem;
|
||||
position: relative;
|
||||
}
|
||||
.mythic-dashboard-edit-toolbar::before {
|
||||
background-color: ${getSectionHeaderAccent};
|
||||
bottom: 0;
|
||||
box-shadow: 0 0 0 1px ${(props) => alpha(props.theme.pageHeaderText?.main || props.theme.palette.text.primary, 0.2)};
|
||||
content: "";
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 6px;
|
||||
}
|
||||
.mythic-dashboard-edit-toolbar-title {
|
||||
color: inherit;
|
||||
font-size: 0.78rem;
|
||||
@@ -7551,17 +7394,6 @@ tspan {
|
||||
gap: 0.35rem;
|
||||
min-width: 0;
|
||||
}
|
||||
.mythic-table-row-action {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.035)"} !important;
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
border-radius: ${(props) => props.theme.shape.borderRadius}px !important;
|
||||
box-shadow: none !important;
|
||||
color: ${(props) => props.theme.palette.text.primary} !important;
|
||||
font-size: 0.74rem;
|
||||
font-weight: 750;
|
||||
min-height: 30px;
|
||||
text-transform: none;
|
||||
}
|
||||
.mythic-table-row-action:hover {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.06)"} !important;
|
||||
border-color: ${(props) => props.theme.table?.border || props.theme.borderColor} !important;
|
||||
@@ -7613,25 +7445,6 @@ tspan {
|
||||
background-color: ${(props) => props.theme.palette.error.main + "2b"} !important;
|
||||
border-color: ${(props) => props.theme.palette.error.main + "88"} !important;
|
||||
}
|
||||
.mythic-table-row-action.Mui-disabled {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.02)"} !important;
|
||||
border-color: ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
color: ${(props) => props.theme.palette.text.disabled} !important;
|
||||
}
|
||||
.mythic-table-row-icon-action {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.035)"} !important;
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
border-radius: ${(props) => props.theme.shape.borderRadius}px !important;
|
||||
box-shadow: none !important;
|
||||
color: ${(props) => props.theme.palette.text.primary} !important;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
}
|
||||
.mythic-table-row-icon-action:hover {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.06)"} !important;
|
||||
border-color: ${(props) => props.theme.table?.border || props.theme.borderColor} !important;
|
||||
}
|
||||
.mythic-table-row-icon-action-danger {
|
||||
color: ${(props) => props.theme.palette.error.main} !important;
|
||||
}
|
||||
@@ -7839,60 +7652,6 @@ tspan {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.mythic-custom-browser-tags-cell {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mythic-custom-browser-tags-cell .MuiIconButton-root {
|
||||
align-items: center !important;
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.045)" : "rgba(0,0,0,0.025)"} !important;
|
||||
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} !important;
|
||||
display: inline-flex !important;
|
||||
flex: 0 0 auto;
|
||||
float: none !important;
|
||||
height: 22px;
|
||||
justify-content: center !important;
|
||||
padding: 0 !important;
|
||||
transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
|
||||
width: 22px;
|
||||
}
|
||||
.mythic-custom-browser-tags-cell .MuiIconButton-root:hover {
|
||||
background-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.16 : 0.09)} !important;
|
||||
border-color: ${(props) => alpha(props.theme.palette.primary.main, props.theme.palette.mode === "dark" ? 0.44 : 0.3)};
|
||||
color: ${(props) => props.theme.palette.primary.main} !important;
|
||||
}
|
||||
.mythic-custom-browser-tags-cell .MuiIconButton-root svg {
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
.mythic-custom-browser-tags-list {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.2rem;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mythic-custom-browser-tags-list .MuiChip-root {
|
||||
flex: 0 0 auto;
|
||||
float: none !important;
|
||||
height: 18px !important;
|
||||
max-width: 5.5rem;
|
||||
}
|
||||
.mythic-custom-browser-tags-list .MuiChip-label {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 800;
|
||||
min-width: 0;
|
||||
overflow: hidden !important;
|
||||
padding-left: 0.35rem;
|
||||
padding-right: 0.35rem;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.mythic-eventing-workspace {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
@@ -8370,12 +8129,6 @@ tspan {
|
||||
min-height: 1.65rem;
|
||||
padding: 0 0.55rem;
|
||||
}
|
||||
.mythic-eventing-trigger-parameter-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.55rem;
|
||||
min-width: 0;
|
||||
}
|
||||
.mythic-eventing-trigger-parameter-list .mythic-create-parameter-card {
|
||||
grid-template-columns: minmax(14rem, 0.36fr) minmax(0, 1fr);
|
||||
}
|
||||
@@ -8953,12 +8706,6 @@ tspan {
|
||||
margin: 0 0 0.65rem;
|
||||
padding: 0.55rem 0.65rem;
|
||||
}
|
||||
.mythic-eventing-action-data-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.55rem;
|
||||
min-width: 0;
|
||||
}
|
||||
.mythic-eventing-action-data-card {
|
||||
background-color: ${(props) => props.theme.palette.background.paper};
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
|
||||
@@ -9549,19 +9296,7 @@ tspan {
|
||||
top: 0.75rem;
|
||||
z-index: 5;
|
||||
}
|
||||
.mythic-eventing-flow-controls {
|
||||
background-color: ${(props) => props.theme.palette.background.paper};
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
|
||||
border-radius: ${(props) => props.theme.shape.borderRadius}px;
|
||||
box-shadow: 0 6px 18px ${(props) => alpha(props.theme.palette.common.black, props.theme.palette.mode === "dark" ? 0.22 : 0.08)} !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mythic-eventing-flow-controls .react-flow__controls-button {
|
||||
background-color: ${(props) => props.theme.palette.background.paper};
|
||||
border-bottom-color: ${(props) => props.theme.table?.borderSoft || props.theme.borderColor};
|
||||
color: ${(props) => props.theme.palette.text.secondary};
|
||||
}
|
||||
.mythic-eventing-flow-controls .react-flow__controls-button:hover {
|
||||
.mythic-graph-controls-muted-hover .react-flow__controls-button:hover {
|
||||
background-color: ${(props) => props.theme.tableHover};
|
||||
color: ${(props) => props.theme.palette.text.primary};
|
||||
}
|
||||
@@ -10088,12 +9823,6 @@ tspan {
|
||||
gap: 0.4rem;
|
||||
min-width: 0;
|
||||
}
|
||||
.mythic-installed-service-metadata-summary {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.55rem;
|
||||
min-width: 0;
|
||||
}
|
||||
.mythic-installed-service-metadata-grid {
|
||||
display: grid;
|
||||
gap: 0.32rem 0.7rem;
|
||||
@@ -10519,11 +10248,6 @@ tspan {
|
||||
background-color: ${(props) => props.theme.palette.error.main + "2b"} !important;
|
||||
border-color: ${(props) => props.theme.palette.error.main + "88"} !important;
|
||||
}
|
||||
.mythic-table-row-icon-action.Mui-disabled {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.02)"} !important;
|
||||
border-color: ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
color: ${(props) => props.theme.palette.text.disabled} !important;
|
||||
}
|
||||
.MuiIconButton-root.mythic-response-action-button {
|
||||
background-color: ${(props) => props.theme.palette.mode === "dark" ? alpha(props.theme.palette.common.white, 0.045) : alpha(props.theme.palette.common.black, 0.028)} !important;
|
||||
border: 1px solid ${(props) => props.theme.table?.borderSoft || props.theme.borderColor} !important;
|
||||
|
||||
@@ -1326,3 +1326,4 @@ type custombrowserExportFunctionOutput {
|
||||
status: String!
|
||||
error: String
|
||||
}
|
||||
|
||||
|
||||
@@ -86,3 +86,11 @@ select_permissions:
|
||||
- type
|
||||
filter: {}
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: mythic_admin
|
||||
permission:
|
||||
columns:
|
||||
- deleted
|
||||
filter: {}
|
||||
check: null
|
||||
comment: ""
|
||||
|
||||
@@ -23,10 +23,12 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
ContextKeyClaims = "claims"
|
||||
ContextKeyAPIToken = "apitoken_struct"
|
||||
ContextKeyUserID = "user_id"
|
||||
ContextKeyUsername = "username"
|
||||
ContextKeyClaims = "claims"
|
||||
ContextKeyAPIToken = "apitoken_struct"
|
||||
ContextKeyUserID = "user_id"
|
||||
ContextKeyUsername = "username"
|
||||
ContextKeyAPITokenID = "apitoken_id"
|
||||
ContextKeyOperatorOperationStruct = "operatorOperation_struct"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -38,7 +38,7 @@ func IPBlockMiddleware() gin.HandlerFunc {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
// make sure the ipAddr is in at least one of the alowed IP blocks
|
||||
// make sure the ipAddr is in at least one of the allowed IP blocks
|
||||
for _, subnet := range utils.MythicConfig.AllowedIPBlocks {
|
||||
if subnet.Contains(ipAddr) {
|
||||
c.Next()
|
||||
@@ -71,8 +71,8 @@ func RBACMiddleware(allowedRoles []string) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
if operatorOperation.CurrentOperator.Admin || utils.SliceContains(allowedRoles, operatorOperation.ViewMode) {
|
||||
c.Set("operatorOperation", operatorOperation)
|
||||
c.Set("apitokens-id", customClaims.APITokensID)
|
||||
c.Set(ContextKeyOperatorOperationStruct, operatorOperation)
|
||||
c.Set(ContextKeyAPITokenID, customClaims.APITokensID)
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
@@ -146,6 +146,7 @@ func TokenScopeMiddleware(requiredScopes []string) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
}
|
||||
SetRabbitMQAuthContextForGin(c)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package authentication
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/rabbitmq"
|
||||
)
|
||||
|
||||
const ContextKeyRabbitMQAuthContext = "rabbitmq_auth_context"
|
||||
|
||||
func SetRabbitMQAuthContextForGin(c *gin.Context) rabbitmq.RabbitMQAuthContext {
|
||||
authContext := rabbitmq.RabbitMQAuthContext{
|
||||
ContextID: uuid.NewString(),
|
||||
}
|
||||
if claims, err := GetClaims(c); err == nil && claims != nil {
|
||||
authContext.OperatorID = claims.UserID
|
||||
authContext.OperationID = claims.OperationID
|
||||
authContext.APITokensID = claims.APITokensID
|
||||
authContext.EventStepInstanceID = claims.EventStepInstanceID
|
||||
effectiveScopes := mythicjwt.EffectiveScopes(claims.Scopes)
|
||||
authContext.SourceScopes = append([]string{}, effectiveScopes...)
|
||||
}
|
||||
c.Set(ContextKeyRabbitMQAuthContext, authContext)
|
||||
return authContext
|
||||
}
|
||||
|
||||
func RabbitMQAuthContextFromGin(c *gin.Context) rabbitmq.RabbitMQAuthContext {
|
||||
if c == nil {
|
||||
return rabbitmq.RabbitMQAuthContext{}
|
||||
}
|
||||
if existingContext, ok := c.Get(ContextKeyRabbitMQAuthContext); ok {
|
||||
if authContext, ok := existingContext.(rabbitmq.RabbitMQAuthContext); ok {
|
||||
return authContext
|
||||
}
|
||||
}
|
||||
return SetRabbitMQAuthContextForGin(c)
|
||||
}
|
||||
@@ -63,6 +63,7 @@ type Task struct {
|
||||
InteractiveTaskType structs.NullInt64 `db:"interactive_task_type" json:"interactive_task_type"`
|
||||
EventStepInstanceID structs.NullInt64 `db:"eventstepinstance_id" json:"event_step_instance_id" mapstructure:"event_step_instance_id"`
|
||||
APITokensID structs.NullInt64 `db:"apitokens_id" json:"api_tokens_id" mapstructure:"apitokens_id"`
|
||||
APIToken Apitokens `db:"apitoken" json:"apitoken"`
|
||||
HasInterceptedResponse bool `db:"has_intercepted_response" json:"has_intercepted_response"`
|
||||
CommandPayloadType string `db:"command_payload_type" json:"command_payload_type" mapstructure:"command_payload_type"`
|
||||
ProcessAtOriginalCommand bool `db:"process_at_original_command" json:"process_at_original_command" mapstructure:"process_at_original_command"`
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package rabbitmq
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/its-a-feature/Mythic/utils"
|
||||
)
|
||||
|
||||
var containerAuthMap = make(map[string]string)
|
||||
var containerAuthMapLock sync.RWMutex
|
||||
|
||||
func NewContainerAuth(containerName string) string {
|
||||
containerAuthMapLock.Lock()
|
||||
defer containerAuthMapLock.Unlock()
|
||||
if _, ok := containerAuthMap[containerName]; ok {
|
||||
return containerAuthMap[containerName]
|
||||
}
|
||||
containerAuthMap[containerName] = utils.GenerateRandomPassword(36)
|
||||
return containerAuthMap[containerName]
|
||||
}
|
||||
|
||||
func GetContainerFromAuth(password string) (string, error) {
|
||||
containerAuthMapLock.RLock()
|
||||
defer containerAuthMapLock.RUnlock()
|
||||
for key, value := range containerAuthMap {
|
||||
if value == password {
|
||||
return key, nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("container not found")
|
||||
}
|
||||
@@ -235,7 +235,12 @@ func CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(containerName string) {
|
||||
}
|
||||
onStartMessage.APIToken = plainAPITokenValue
|
||||
go expireAPITokenAfterShortLivedTTL(apiToken.ID)
|
||||
err = RabbitMQConnection.SendContainerOnStart(onStartMessage)
|
||||
err = RabbitMQConnection.SendContainerOnStart(onStartMessage, RabbitMQAuthContext{
|
||||
OperatorID: apiToken.OperatorID,
|
||||
OperationID: onStartMessage.OperationID,
|
||||
APITokensID: apiToken.ID,
|
||||
SourceScopes: apiToken.Scopes,
|
||||
})
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to send container on start")
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ type RPCRetryPolicy int
|
||||
const (
|
||||
MYTHIC_EXCHANGE = "mythic_exchange"
|
||||
MYTHIC_TOPIC_EXCHANGE = "mythic_topic_exchange"
|
||||
MYTHIC_RABBITMQ_AUTH_CONTEXT_HEADER = "mythic-auth-context"
|
||||
RETRY_CONNECT_DELAY = 5 * time.Second
|
||||
CHECK_CONTAINER_STATUS_DELAY = 10 * time.Second
|
||||
TIME_FORMAT_STRING_YYYY_MM_DD = "2006-01-02"
|
||||
@@ -178,7 +179,6 @@ const (
|
||||
MYTHIC_RPC_FILE_SEARCH = "mythic_rpc_file_search"
|
||||
MYTHIC_RPC_FILE_UPDATE = "mythic_rpc_file_update"
|
||||
MYTHIC_RPC_FILE_GET_CONTENT = "mythic_rpc_file_get_content"
|
||||
MYTHIC_RPC_FILE_REGISTER = "mythic_rpc_file_register"
|
||||
// MYTHIC_RPC_PAYLOAD_CREATE_FROM_UUID payload operations
|
||||
MYTHIC_RPC_PAYLOAD_CREATE_FROM_UUID = "mythic_rpc_payload_create_from_uuid"
|
||||
MYTHIC_RPC_PAYLOAD_CREATE_FROM_SCRATCH = "mythic_rpc_payload_create_from_scratch"
|
||||
@@ -260,7 +260,7 @@ const (
|
||||
// Agent Message
|
||||
MYTHIC_RPC_HANDLE_AGENT_JSON = "mythic_rpc_handle_agent_message_json"
|
||||
// CustomBrowser
|
||||
CUSTOMBROWSER_SEARCH = "mythic_rpc_custombrowser_search"
|
||||
MYTHIC_RPC_CUSTOMBROWSER_SEARCH = "mythic_rpc_custombrowser_search"
|
||||
// MYTHIC_RPC_BLANK blank
|
||||
MYTHIC_RPC_BLANK = "mythic_rpc_blank"
|
||||
)
|
||||
|
||||
@@ -45,6 +45,7 @@ func (r *rabbitMQConnection) EmitSiemMessage(loggingMessage LoggingMessage) erro
|
||||
"",
|
||||
localLoggingMessage,
|
||||
true,
|
||||
nil,
|
||||
); err != nil {
|
||||
//logging.LogError(err, "Failed to emit SIEM Message")
|
||||
return err
|
||||
|
||||
@@ -77,6 +77,7 @@ func (r *rabbitMQConnection) EmitWebhookMessage(webhookMessage WebhookMessage) e
|
||||
"",
|
||||
localWebhookMessage,
|
||||
true,
|
||||
nil,
|
||||
); err != nil {
|
||||
//logging.LogError(err, "Failed to emit webhook Message")
|
||||
return err
|
||||
|
||||
@@ -749,6 +749,7 @@ func processEventFinishAndNextStepStart(eventNotification EventNotification) {
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to mark apitoken as deleted")
|
||||
}
|
||||
InvalidateRabbitMQAuthContextsForEventStepInstance(triggeringStep.ID)
|
||||
if !eventNotification.ActionSuccess && !triggeringStep.ContinueOnError {
|
||||
markStepInstanceAsError(triggeringStep, eventNotification.ActionStderr)
|
||||
return
|
||||
@@ -1174,7 +1175,12 @@ func startEventStepInstanceActionCreatePayload(eventStepInstance databaseStructs
|
||||
ID: eventStepInstance.OperationID,
|
||||
},
|
||||
}
|
||||
_, _, err = RegisterNewPayload(eventData.PayloadConfiguration, &operatorOperation)
|
||||
_, _, err = RegisterNewPayload(eventData.PayloadConfiguration, &operatorOperation, RabbitMQAuthContext{
|
||||
OperatorID: eventStepInstance.OperatorID,
|
||||
OperationID: eventStepInstance.OperationID,
|
||||
EventStepInstanceID: eventStepInstance.ID,
|
||||
SourceScopes: []string{mythicjwt.SCOPE_PAYLOAD_WRITE},
|
||||
})
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to register payload for CreatePayloadWebhook")
|
||||
}
|
||||
@@ -1199,8 +1205,7 @@ func startEventStepInstanceActionCreateCallback(eventStepInstance databaseStruct
|
||||
logging.LogError(err, "Failed to decode action data")
|
||||
return err
|
||||
}
|
||||
eventData.EventStepInstanceID = &eventStepInstance.ID
|
||||
callbackCreateResponse := MythicRPCCallbackCreate(eventData)
|
||||
callbackCreateResponse := MythicRPCCallbackCreate(eventData, rabbitMQAuthContextForEventStepInstance(eventStepInstance))
|
||||
if !callbackCreateResponse.Success {
|
||||
logging.LogError(nil, callbackCreateResponse.Error)
|
||||
return errors.New(callbackCreateResponse.Error)
|
||||
@@ -1245,6 +1250,12 @@ func startEventStepInstanceActionCreateTask(eventStepInstance databaseStructs.Ev
|
||||
ParentTaskID: eventData.ParentTaskID,
|
||||
IsInteractiveTask: eventData.IsInteractiveTask,
|
||||
InteractiveTaskType: eventData.InteractiveTaskType,
|
||||
AuthContext: RabbitMQAuthContext{
|
||||
OperatorID: eventStepInstance.OperatorID,
|
||||
OperationID: eventStepInstance.OperationID,
|
||||
EventStepInstanceID: eventStepInstance.ID,
|
||||
SourceScopes: []string{mythicjwt.SCOPE_TASK_WRITE},
|
||||
},
|
||||
}
|
||||
if eventData.ParamDictionary != nil && len(eventData.ParamDictionary) > 0 {
|
||||
paramsString, _ := json.Marshal(eventData.ParamDictionary)
|
||||
@@ -1337,6 +1348,15 @@ func startEventStepInstanceActionSendWebhook(eventStepInstance databaseStructs.E
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func rabbitMQAuthContextForEventStepInstance(eventStepInstance databaseStructs.EventStepInstance) RabbitMQAuthContext {
|
||||
return RabbitMQAuthContext{
|
||||
OperatorID: eventStepInstance.OperatorID,
|
||||
OperationID: eventStepInstance.OperationID,
|
||||
EventStepInstanceID: eventStepInstance.ID,
|
||||
}
|
||||
}
|
||||
|
||||
func startEventStepInstanceActionCustomFunction(eventStepInstance databaseStructs.EventStepInstance, inputs map[string]interface{},
|
||||
actionDataMap map[string]interface{},
|
||||
environment map[string]interface{}) error {
|
||||
@@ -1359,7 +1379,7 @@ func startEventStepInstanceActionCustomFunction(eventStepInstance databaseStruct
|
||||
ActionData: actionDataMap,
|
||||
FunctionName: functionName,
|
||||
ContainerName: containerName,
|
||||
})
|
||||
}, rabbitMQAuthContextForEventStepInstance(eventStepInstance))
|
||||
}
|
||||
func startEventStepInstanceActionConditionalCheck(eventStepInstance databaseStructs.EventStepInstance, inputs map[string]interface{},
|
||||
actionDataMap map[string]interface{},
|
||||
@@ -1386,7 +1406,7 @@ func startEventStepInstanceActionConditionalCheck(eventStepInstance databaseStru
|
||||
ActionData: actionDataMap,
|
||||
FunctionName: functionName,
|
||||
ContainerName: containerName,
|
||||
})
|
||||
}, rabbitMQAuthContextForEventStepInstance(eventStepInstance))
|
||||
}
|
||||
func startEventStepInstanceActionInterceptTask(eventStepInstance databaseStructs.EventStepInstance, inputs map[string]interface{},
|
||||
actionDataMap map[string]interface{},
|
||||
@@ -1416,7 +1436,7 @@ func startEventStepInstanceActionInterceptTask(eventStepInstance databaseStructs
|
||||
Inputs: inputs,
|
||||
ActionData: actionDataMap,
|
||||
ContainerName: containerName,
|
||||
})
|
||||
}, rabbitMQAuthContextForEventStepInstance(eventStepInstance))
|
||||
}
|
||||
func startEventStepInstanceActionInterceptResponse(eventStepInstance databaseStructs.EventStepInstance, inputs map[string]interface{},
|
||||
actionDataMap map[string]interface{},
|
||||
@@ -1440,7 +1460,7 @@ func startEventStepInstanceActionInterceptResponse(eventStepInstance databaseStr
|
||||
Inputs: inputs,
|
||||
ActionData: actionDataMap,
|
||||
ContainerName: containerName,
|
||||
})
|
||||
}, rabbitMQAuthContextForEventStepInstance(eventStepInstance))
|
||||
}
|
||||
func restartFailedJobs(eventgroupInstanceID int) error {
|
||||
_, err := database.DB.Exec(`UPDATE eventstepinstance
|
||||
@@ -1596,6 +1616,7 @@ func markStepInstanceAsError(eventStepInstance databaseStructs.EventStepInstance
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to mark apitoken as deleted")
|
||||
}
|
||||
InvalidateRabbitMQAuthContextsForEventStepInstance(eventStepInstance.ID)
|
||||
if eventStepInstance.ContinueOnError {
|
||||
processEventFinishAndNextStepStart(EventNotification{
|
||||
EventStepInstanceID: eventStepInstance.ID,
|
||||
|
||||
@@ -23,12 +23,14 @@ type RPCQueueStruct struct {
|
||||
Queue string
|
||||
RoutingKey string
|
||||
Handler RPCQueueHandler
|
||||
Scopes []string
|
||||
}
|
||||
type DirectQueueStruct struct {
|
||||
Exchange string
|
||||
Queue string
|
||||
RoutingKey string
|
||||
Handler QueueHandler
|
||||
Scopes []string
|
||||
}
|
||||
|
||||
type channelMutex struct {
|
||||
@@ -64,11 +66,13 @@ func (r *rabbitMQConnection) AddRPCQueue(input RPCQueueStruct) {
|
||||
r.addListenerMutex.Lock()
|
||||
r.RPCQueues = append(r.RPCQueues, input)
|
||||
r.addListenerMutex.Unlock()
|
||||
RegisterRabbitMQRPCScopePolicy(input.Queue, input.Scopes)
|
||||
}
|
||||
func (r *rabbitMQConnection) AddDirectQueue(input DirectQueueStruct) {
|
||||
r.addListenerMutex.Lock()
|
||||
r.DirectQueues = append(r.DirectQueues, input)
|
||||
r.addListenerMutex.Unlock()
|
||||
RegisterRabbitMQRPCScopePolicy(input.Queue, input.Scopes)
|
||||
}
|
||||
func (r *rabbitMQConnection) startListeners() {
|
||||
exclusiveQueue := true
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -89,6 +90,7 @@ func init() {
|
||||
Queue: "mythic_consume_c2_sync",
|
||||
RoutingKey: C2_SYNC_ROUTING_KEY,
|
||||
Handler: processC2SyncMessages,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -192,7 +194,7 @@ func c2Sync(in C2SyncMessage) error {
|
||||
}
|
||||
go SendAllOperationsMessage(fmt.Sprintf("Successfully synced %s with container version %s", c2Profile.Name, in.ContainerVersion), 0, "debug", database.MESSAGE_LEVEL_DEBUG, false)
|
||||
go ResolveAllOperationsMessageBySource(getDownContainerSource(c2Profile.Name), 0)
|
||||
go autoStartC2Profile(c2Profile, false)
|
||||
go autoStartC2Profile(c2Profile, false, RabbitMQAuthContext{})
|
||||
if newProfile {
|
||||
go reSyncPayloadTypes()
|
||||
}
|
||||
@@ -362,7 +364,7 @@ func updateC2Parameters(in C2SyncMessage, c2Profile databaseStructs.C2profile) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoStartC2Profile(c2Profile databaseStructs.C2profile, startC2Only bool) *C2StartServerMessageResponse {
|
||||
func autoStartC2Profile(c2Profile databaseStructs.C2profile, startC2Only bool, authContext RabbitMQAuthContext) *C2StartServerMessageResponse {
|
||||
// on a new sync, if it's not p2p, ask it to start
|
||||
var c2StartResp *C2StartServerMessageResponse
|
||||
c2StartResp = new(C2StartServerMessageResponse)
|
||||
@@ -372,7 +374,7 @@ func autoStartC2Profile(c2Profile databaseStructs.C2profile, startC2Only bool) *
|
||||
CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(c2Profile.Name)
|
||||
}
|
||||
if !c2Profile.IsP2p {
|
||||
c2StartResp, err = RabbitMQConnection.SendC2RPCStartServer(C2StartServerMessage{Name: c2Profile.Name})
|
||||
c2StartResp, err = RabbitMQConnection.SendC2RPCStartServer(C2StartServerMessage{Name: c2Profile.Name}, authContext)
|
||||
time.Sleep(10 * time.Second) // give the server a chance to start back up
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to send start message to C2 profile")
|
||||
@@ -416,6 +418,8 @@ func autoReHostFiles(c2Profile databaseStructs.C2profile) {
|
||||
FileUUID: newTagMap["agent_file_id"].(string),
|
||||
HostURL: newTagMap["host_url"].(string),
|
||||
Remove: false,
|
||||
}, RabbitMQAuthContext{
|
||||
SourceScopes: []string{mythicjwt.SCOPE_FILE_READ},
|
||||
})
|
||||
logging.LogInfo("got response from sending host file", "c2", newTagMap["c2_profile"].(string), "url", newTagMap["host_url"].(string))
|
||||
// sleep for 3 seconds to allow the internal c2 binary to start up before we send anything else
|
||||
|
||||
@@ -46,6 +46,7 @@ func init() {
|
||||
Queue: "mythic_consume_consuming_container_sync",
|
||||
RoutingKey: CONSUMING_CONTAINER_SYNC_ROUTING_KEY,
|
||||
Handler: processConsumingContainerSyncMessages,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
@@ -20,6 +21,7 @@ func init() {
|
||||
Queue: CONTAINER_ON_START_RESPONSE,
|
||||
RoutingKey: CONTAINER_ON_START_RESPONSE,
|
||||
Handler: processContainerOnStartMessageResponse,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
@@ -17,6 +18,7 @@ func init() {
|
||||
Queue: CUSTOMBROWSER_EXPORT_FUNCTION_RESPONSE,
|
||||
RoutingKey: CUSTOMBROWSER_EXPORT_FUNCTION_RESPONSE,
|
||||
Handler: processCbExportFunctionResponseMessages,
|
||||
Scopes: []string{mythicjwt.SCOPE_BROWSER_READ},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ func init() {
|
||||
Queue: CUSTOMBROWSER_SYNC_ROUTING_KEY,
|
||||
RoutingKey: CUSTOMBROWSER_SYNC_ROUTING_KEY,
|
||||
Handler: processCustomBrowserSyncMessages,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
"github.com/its-a-feature/Mythic/eventing"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -24,6 +25,7 @@ func init() {
|
||||
Queue: EVENTING_CONDITIONAL_CHECK_RESPONSE,
|
||||
RoutingKey: EVENTING_CONDITIONAL_CHECK_RESPONSE,
|
||||
Handler: processEventingConditionalCheckResponse,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
"github.com/its-a-feature/Mythic/eventing"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -23,6 +24,7 @@ func init() {
|
||||
Queue: EVENTING_CUSTOM_FUNCTION_RESPONSE,
|
||||
RoutingKey: EVENTING_CUSTOM_FUNCTION_RESPONSE,
|
||||
Handler: processEventingCustomFunctionResponse,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
"github.com/its-a-feature/Mythic/eventing"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ResponseInterceptMessageResponse struct {
|
||||
@@ -26,6 +27,7 @@ func init() {
|
||||
Queue: EVENTING_RESPONSE_INTERCEPT_RESPONSE,
|
||||
RoutingKey: EVENTING_RESPONSE_INTERCEPT_RESPONSE,
|
||||
Handler: processEventingResponseInterceptResponse,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,13 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/eventing"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TaskInterceptMessageResponse struct {
|
||||
@@ -29,6 +30,7 @@ func init() {
|
||||
Queue: EVENTING_TASK_INTERCEPT_RESPONSE,
|
||||
RoutingKey: EVENTING_TASK_INTERCEPT_RESPONSE,
|
||||
Handler: processEventingTaskInterceptResponse,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ func init() {
|
||||
Queue: MYTHIC_RPC_AGENTSTORAGE_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_AGENTSTORAGE_CREATE,
|
||||
Handler: processMythicRPCAgentstorageCreate,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_AGENTSTORAGE_CREATE
|
||||
//
|
||||
func MythicRPCAgentstorageCreate(input MythicRPCAgentstorageCreateMessage) MythicRPCAgentstorageCreateMessageResponse {
|
||||
response := MythicRPCAgentstorageCreateMessageResponse{
|
||||
Success: false,
|
||||
|
||||
@@ -23,11 +23,11 @@ func init() {
|
||||
Queue: MYTHIC_RPC_AGENTSTORAGE_REMOVE,
|
||||
RoutingKey: MYTHIC_RPC_AGENTSTORAGE_REMOVE,
|
||||
Handler: processMythicRPCAgentstorageRemove,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_AGENTSTORAGE_REMOVE
|
||||
//
|
||||
func MythicRPCAgentstorageRemove(input MythicRPCAgentstorageRemoveMessage) MythicRPCAgentstorageRemoveMessageResponse {
|
||||
response := MythicRPCAgentstorageRemoveMessageResponse{
|
||||
Success: false,
|
||||
|
||||
@@ -30,6 +30,7 @@ func init() {
|
||||
Queue: MYTHIC_RPC_AGENTSTORAGE_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_AGENTSTORAGE_SEARCH,
|
||||
Handler: processMythicRPCAgentstorageSearch,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ type MythicRPCAPITokenCreateMessage struct {
|
||||
AgentTaskID *string `json:"agent_task_id"`
|
||||
AgentCallbackID *string `json:"agent_callback_id"`
|
||||
PayloadUUID *string `json:"payload_uuid"`
|
||||
APITokenID *int `json:"apitoken_id"`
|
||||
Scopes []string `json:"scopes"`
|
||||
}
|
||||
|
||||
@@ -32,28 +31,11 @@ func init() {
|
||||
Queue: MYTHIC_RPC_APITOKEN_CREATE, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_APITOKEN_CREATE, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCAPITokenCreate, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_APITOKEN_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
var baseScopes = []string{
|
||||
mythicjwt.SCOPE_CALLBACK_WRITE,
|
||||
mythicjwt.SCOPE_CREDENTIAL_WRITE,
|
||||
mythicjwt.SCOPE_FILE_WRITE,
|
||||
mythicjwt.SCOPE_PAYLOAD_WRITE,
|
||||
mythicjwt.SCOPE_RESPONSE_WRITE,
|
||||
mythicjwt.SCOPE_TAG_WRITE,
|
||||
mythicjwt.SCOPE_TASK_WRITE,
|
||||
}
|
||||
|
||||
func getAPITokenScopes(apitokenID int) ([]string, error) {
|
||||
apiToken := databaseStructs.Apitokens{}
|
||||
if err := database.DB.Get(&apiToken, `SELECT scopes FROM apitokens WHERE id=$1`, apitokenID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []string(apiToken.Scopes), nil
|
||||
}
|
||||
|
||||
func MythicRPCAPITokenCreate(input MythicRPCAPITokenCreateMessage) MythicRPCAPITokenCreateMessageResponse {
|
||||
func MythicRPCAPITokenCreate(input MythicRPCAPITokenCreateMessage, authContext RabbitMQAuthContext) MythicRPCAPITokenCreateMessageResponse {
|
||||
response := MythicRPCAPITokenCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -67,11 +49,19 @@ func MythicRPCAPITokenCreate(input MythicRPCAPITokenCreateMessage) MythicRPCAPIT
|
||||
TokenValue: "",
|
||||
Active: true,
|
||||
Scopes: normalizedScopes,
|
||||
OperatorID: authContext.OperatorID,
|
||||
CreatedBy: authContext.OperatorID,
|
||||
}
|
||||
if authContext.EventStepInstanceID > 0 {
|
||||
apiToken.EventStepInstanceID.Valid = true
|
||||
apiToken.EventStepInstanceID.Int64 = int64(authContext.EventStepInstanceID)
|
||||
}
|
||||
if input.AgentTaskID != nil {
|
||||
task := databaseStructs.Task{AgentTaskID: *input.AgentTaskID}
|
||||
err = database.DB.Get(&task, `SELECT id, operator_id, operation_id, display_id, completed
|
||||
FROM task WHERE agent_task_id=$1`, task.AgentTaskID)
|
||||
err = database.DB.Get(&task, `SELECT
|
||||
id, operator_id, operation_id, display_id, completed
|
||||
FROM task
|
||||
WHERE agent_task_id=$1 AND operation_id=$2`, task.AgentTaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -81,15 +71,15 @@ func MythicRPCAPITokenCreate(input MythicRPCAPITokenCreateMessage) MythicRPCAPIT
|
||||
return response
|
||||
}
|
||||
apiToken.TokenType = mythicjwt.AUTH_METHOD_TASK
|
||||
apiToken.OperatorID = task.OperatorID
|
||||
apiToken.CreatedBy = task.OperatorID
|
||||
apiToken.TaskID.Valid = true
|
||||
apiToken.TaskID.Int64 = int64(task.ID)
|
||||
apiToken.Name = fmt.Sprintf("Generated Task API Token via MythicRPC for Task %d", task.DisplayID)
|
||||
} else if input.PayloadUUID != nil {
|
||||
payload := databaseStructs.Payload{UuID: *input.PayloadUUID}
|
||||
err = database.DB.Get(&payload, `SELECT id, operator_id, operation_id, deleted, build_phase
|
||||
FROM payload WHERE uuid=$1`, payload.UuID)
|
||||
err = database.DB.Get(&payload, `SELECT
|
||||
id, operator_id, operation_id, deleted, build_phase
|
||||
FROM payload
|
||||
WHERE uuid=$1 AND operation_id=$2`, payload.UuID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -99,45 +89,20 @@ func MythicRPCAPITokenCreate(input MythicRPCAPITokenCreateMessage) MythicRPCAPIT
|
||||
return response
|
||||
}
|
||||
apiToken.TokenType = mythicjwt.AUTH_METHOD_PAYLOAD
|
||||
apiToken.OperatorID = payload.OperatorID
|
||||
apiToken.CreatedBy = payload.OperatorID
|
||||
apiToken.PayloadID.Valid = true
|
||||
apiToken.PayloadID.Int64 = int64(payload.ID)
|
||||
apiToken.Name = fmt.Sprintf("Generated Payload API Token via MythicRPC for Payload %s", payload.UuID)
|
||||
} else if input.AgentCallbackID != nil {
|
||||
callback := databaseStructs.Callback{}
|
||||
callback.AgentCallbackID = *input.AgentCallbackID
|
||||
err = database.DB.Get(&callback, `SELECT operation_id, id, display_id FROM callback WHERE agent_callback_id=$1`, callback.AgentCallbackID)
|
||||
err = database.DB.Get(&callback, `SELECT
|
||||
operation_id, id, display_id
|
||||
FROM callback
|
||||
WHERE agent_callback_id=$1 AND operation_id=$2`, callback.AgentCallbackID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
operatorOperationData := []databaseStructs.Operatoroperation{}
|
||||
err = database.DB.Select(&operatorOperationData, `SELECT
|
||||
operator.account_type "operator.account_type",
|
||||
operator.id "operator.id",
|
||||
operator.deleted "operator.deleted",
|
||||
operator.active "operator.active"
|
||||
FROM operatoroperation
|
||||
JOIN operator ON operatoroperation.operator_id = operator.id
|
||||
WHERE operatoroperation.operation_id=$1`, callback.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
for i, _ := range operatorOperationData {
|
||||
if operatorOperationData[i].CurrentOperator.AccountType == databaseStructs.AccountTypeBot {
|
||||
if !operatorOperationData[i].CurrentOperator.Deleted && operatorOperationData[i].CurrentOperator.Active {
|
||||
apiToken.OperatorID = operatorOperationData[i].CurrentOperator.ID
|
||||
apiToken.CreatedBy = operatorOperationData[i].CurrentOperator.ID
|
||||
apiToken.Name = fmt.Sprintf("Generated Callback API Token via MythicRPC for Callback %d", callback.DisplayID)
|
||||
}
|
||||
}
|
||||
}
|
||||
if apiToken.OperatorID == 0 {
|
||||
response.Error = "Need a bot account assigned to this operation that's active and not deleted"
|
||||
return response
|
||||
}
|
||||
apiToken.TokenType = mythicjwt.AUTH_METHOD_CALLBACK
|
||||
apiToken.CallbackID.Valid = true
|
||||
apiToken.CallbackID.Int64 = int64(callback.ID)
|
||||
@@ -145,13 +110,10 @@ func MythicRPCAPITokenCreate(input MythicRPCAPITokenCreateMessage) MythicRPCAPIT
|
||||
response.Error = "No task or callback information provided, can't generate apitoken"
|
||||
return response
|
||||
}
|
||||
defaultScopes := baseScopes
|
||||
if input.APITokenID != nil && *input.APITokenID > 0 {
|
||||
defaultScopes, err = getAPITokenScopes(*input.APITokenID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
defaultScopes := authContext.SourceScopes
|
||||
if len(defaultScopes) == 0 {
|
||||
response.Error = "missing RabbitMQ auth context source scopes"
|
||||
return response
|
||||
}
|
||||
err = mythicjwt.CanGrantAPITokenScopes(defaultScopes, normalizedScopes)
|
||||
if err != nil {
|
||||
@@ -159,9 +121,9 @@ func MythicRPCAPITokenCreate(input MythicRPCAPITokenCreateMessage) MythicRPCAPIT
|
||||
return response
|
||||
}
|
||||
statement, err := database.DB.PrepareNamed(`INSERT INTO apitokens
|
||||
(token_value, operator_id, token_type, active, "name", created_by, task_id, callback_id, payload_id, scopes)
|
||||
(token_value, operator_id, token_type, active, "name", created_by, task_id, callback_id, payload_id, scopes, eventstepinstance_id)
|
||||
VALUES
|
||||
(:token_value, :operator_id, :token_type, :active, :name, :created_by, :task_id, :callback_id, :payload_id, :scopes)
|
||||
(:token_value, :operator_id, :token_type, :active, :name, :created_by, :task_id, :callback_id, :payload_id, :scopes, :eventstepinstance_id)
|
||||
RETURNING id`)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
@@ -204,5 +166,10 @@ func processMythicRPCAPITokenCreate(msg amqp.Delivery) interface{} {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCAPITokenCreate(incomingMessage)
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCAPITokenCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -27,11 +28,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_ARTIFACT_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_ARTIFACT_CREATE,
|
||||
Handler: processMythicRPCArtifactCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_RESPONSE_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_ARTIFACT_CREATE
|
||||
func MythicRPCArtifactCreate(input MythicRPCArtifactCreateMessage) MythicRPCArtifactCreateMessageResponse {
|
||||
func MythicRPCArtifactCreate(input MythicRPCArtifactCreateMessage, authContext RabbitMQAuthContext) MythicRPCArtifactCreateMessageResponse {
|
||||
response := MythicRPCArtifactCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -41,12 +43,12 @@ func MythicRPCArtifactCreate(input MythicRPCArtifactCreateMessage) MythicRPCArti
|
||||
taskArtifact.Artifact = []byte(input.ArtifactMessage)
|
||||
taskArtifact.BaseArtifact = input.BaseArtifactType
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
task.operation_id,
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
callback.host "callback.host"
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
WHERE task.id=$1`, input.TaskID); err != nil {
|
||||
WHERE task.id=$1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
@@ -55,34 +57,49 @@ func MythicRPCArtifactCreate(input MythicRPCArtifactCreateMessage) MythicRPCArti
|
||||
} else {
|
||||
taskArtifact.Host = task.Callback.Host
|
||||
}
|
||||
taskArtifact.OperationID = task.OperationID
|
||||
if statement, err := database.DB.PrepareNamed(`INSERT INTO taskartifact
|
||||
(task_id, artifact, base_artifact, host, operation_id)
|
||||
VALUES (:task_id, :artifact, :base_artifact, :host, :operation_id)
|
||||
RETURNING id`); err != nil {
|
||||
taskArtifact.OperationID = authContext.OperationID
|
||||
if authContext.APITokensID > 0 {
|
||||
taskArtifact.APITokensID.Valid = true
|
||||
taskArtifact.APITokensID.Int64 = int64(authContext.APITokensID)
|
||||
}
|
||||
if authContext.EventStepInstanceID > 0 {
|
||||
taskArtifact.EventStepInstanceID.Valid = true
|
||||
taskArtifact.EventStepInstanceID.Int64 = int64(authContext.EventStepInstanceID)
|
||||
}
|
||||
statement, err := database.DB.PrepareNamed(`INSERT INTO taskartifact
|
||||
(task_id, artifact, base_artifact, host, operation_id, apitokens_id, eventstepinstance_id)
|
||||
VALUES (:task_id, :artifact, :base_artifact, :host, :operation_id, :apitokens_id, :eventstepinstance_id)
|
||||
RETURNING id`)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to save taskartifact data to database")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err = statement.Get(&taskArtifact.ID, taskArtifact); err != nil {
|
||||
logging.LogError(err, "Failed to save taskartifact data to database")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
go emitArtifactLog(taskArtifact.ID)
|
||||
return response
|
||||
}
|
||||
err = statement.Get(&taskArtifact.ID, taskArtifact)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to save taskartifact data to database")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
go emitArtifactLog(taskArtifact.ID)
|
||||
return response
|
||||
}
|
||||
func processMythicRPCArtifactCreate(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCArtifactCreateMessage{}
|
||||
responseMsg := MythicRPCArtifactCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCArtifactCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCArtifactCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
@@ -12,7 +14,6 @@ import (
|
||||
)
|
||||
|
||||
type MythicRPCArtifactSearchMessage struct {
|
||||
TaskID int `json:"task_id"` //required
|
||||
SearchArtifacts MythicRPCArtifactSearchArtifactData `json:"artifact"`
|
||||
}
|
||||
type MythicRPCArtifactSearchMessageResponse struct {
|
||||
@@ -33,78 +34,72 @@ func init() {
|
||||
Queue: MYTHIC_RPC_ARTIFACT_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_ARTIFACT_SEARCH,
|
||||
Handler: processMythicRPCArtifactSearch,
|
||||
Scopes: []string{mythicjwt.SCOPE_RESPONSE_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_PROCESS_SEARCH
|
||||
func MythicRPCArtifactSearch(input MythicRPCArtifactSearchMessage) MythicRPCArtifactSearchMessageResponse {
|
||||
func MythicRPCArtifactSearch(input MythicRPCArtifactSearchMessage, authContext RabbitMQAuthContext) MythicRPCArtifactSearchMessageResponse {
|
||||
response := MythicRPCArtifactSearchMessageResponse{
|
||||
Success: false,
|
||||
Artifacts: []MythicRPCArtifactSearchArtifactData{},
|
||||
}
|
||||
paramDict := make(map[string]interface{})
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
task.id,
|
||||
callback.operation_id "callback.operation_id"
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
WHERE task.id=$1`, input.TaskID); err != nil {
|
||||
logging.LogError(err, "Failed to search for task information when searching for artifacts")
|
||||
paramDict["operation_id"] = authContext.OperationID
|
||||
searchString := `SELECT * FROM taskartifact WHERE operation_id=:operation_id `
|
||||
if input.SearchArtifacts.Host != nil {
|
||||
paramDict["host"] = fmt.Sprintf("%%%s%%", *input.SearchArtifacts.Host)
|
||||
searchString += "AND host ILIKE :host "
|
||||
}
|
||||
if input.SearchArtifacts.ArtifactMessage != nil {
|
||||
paramDict["artifact"] = fmt.Sprintf("%%%s%%", *input.SearchArtifacts.ArtifactMessage)
|
||||
searchString += "AND artifact LIKE :artifact "
|
||||
}
|
||||
if input.SearchArtifacts.TaskID != nil {
|
||||
paramDict["task_id"] = *input.SearchArtifacts.TaskID
|
||||
searchString += "AND task_id=:task_id "
|
||||
}
|
||||
if input.SearchArtifacts.ArtifactType != nil {
|
||||
paramDict["base_artifact"] = fmt.Sprintf("%%%s%%", *input.SearchArtifacts.ArtifactType)
|
||||
searchString += "AND base_artifact LIKE :base_artifact "
|
||||
}
|
||||
searchString += " ORDER BY task_id DESC"
|
||||
rows, err := database.DB.NamedQuery(searchString, paramDict)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to search artifact information")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
paramDict["operation_id"] = task.Callback.OperationID
|
||||
searchString := `SELECT * FROM taskartifact WHERE operation_id=:operation_id `
|
||||
if input.SearchArtifacts.Host != nil {
|
||||
paramDict["host"] = fmt.Sprintf("%%%s%%", *input.SearchArtifacts.Host)
|
||||
searchString += "AND host ILIKE :host "
|
||||
}
|
||||
if input.SearchArtifacts.ArtifactMessage != nil {
|
||||
paramDict["artifact"] = fmt.Sprintf("%%%s%%", *input.SearchArtifacts.ArtifactMessage)
|
||||
searchString += "AND artifact LIKE :artifact "
|
||||
}
|
||||
if input.SearchArtifacts.TaskID != nil {
|
||||
paramDict["task_id"] = *input.SearchArtifacts.TaskID
|
||||
searchString += "AND task_id=:task_id "
|
||||
}
|
||||
if input.SearchArtifacts.ArtifactType != nil {
|
||||
paramDict["base_artifact"] = fmt.Sprintf("%%%s%%", *input.SearchArtifacts.ArtifactType)
|
||||
searchString += "AND base_artifact LIKE :base_artifact "
|
||||
}
|
||||
searchString += " ORDER BY task_id DESC"
|
||||
if rows, err := database.DB.NamedQuery(searchString, paramDict); err != nil {
|
||||
logging.LogError(err, "Failed to search artifact information")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
result := MythicRPCArtifactSearchArtifactData{}
|
||||
searchResult := databaseStructs.Taskartifact{}
|
||||
if err = rows.StructScan(&searchResult); err != nil {
|
||||
logging.LogError(err, "Failed to get row from artifacts for search")
|
||||
} else if err = mapstructure.Decode(searchResult, &result); err != nil {
|
||||
logging.LogError(err, "Failed to map artifact search results into array")
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
result := MythicRPCArtifactSearchArtifactData{}
|
||||
searchResult := databaseStructs.Taskartifact{}
|
||||
if err = rows.StructScan(&searchResult); err != nil {
|
||||
logging.LogError(err, "Failed to get row from artifacts for search")
|
||||
} else if err = mapstructure.Decode(searchResult, &result); err != nil {
|
||||
logging.LogError(err, "Failed to map artifact search results into array")
|
||||
} else {
|
||||
response.Artifacts = append(response.Artifacts, result)
|
||||
}
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
response.Artifacts = append(response.Artifacts, result)
|
||||
}
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCArtifactSearch(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCArtifactSearchMessage{}
|
||||
responseMsg := MythicRPCArtifactSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCArtifactSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCArtifactSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MythicRPCC2UpdateStatusMessage struct {
|
||||
@@ -26,6 +28,7 @@ func init() {
|
||||
Queue: MYTHIC_RPC_C2_UPDATE_STATUS,
|
||||
RoutingKey: MYTHIC_RPC_C2_UPDATE_STATUS,
|
||||
Handler: processMythicRPCC2UpdateStatus,
|
||||
Scopes: []string{mythicjwt.SCOPE_C2_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -28,17 +30,17 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_ADD_COMMAND,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_ADD_COMMAND,
|
||||
Handler: processMythicRPCCallbackAddCommand,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CALLBACK_ADD_COMMAND
|
||||
func MythicRPCCallbackAddCommand(input MythicRPCCallbackAddCommandMessage) MythicRPCCallbackAddCommandMessageResponse {
|
||||
func MythicRPCCallbackAddCommand(input MythicRPCCallbackAddCommandMessage, authContext RabbitMQAuthContext) MythicRPCCallbackAddCommandMessageResponse {
|
||||
response := MythicRPCCallbackAddCommandMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
CallbackID := 0
|
||||
PayloadTypeID := 0
|
||||
OperatorID := 0
|
||||
task := databaseStructs.Task{}
|
||||
callback := databaseStructs.Callback{}
|
||||
if input.TaskID > 0 {
|
||||
@@ -49,7 +51,7 @@ func MythicRPCCallbackAddCommand(input MythicRPCCallbackAddCommandMessage) Mythi
|
||||
FROM task
|
||||
JOIN callback on task.callback_id = callback.id
|
||||
JOIN payload on callback.registered_payload_id = payload.id
|
||||
WHERE task.id=$1`, input.TaskID)
|
||||
WHERE task.id=$1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find task in MythicRPCCallbackAddCommand")
|
||||
response.Error = err.Error()
|
||||
@@ -57,14 +59,13 @@ func MythicRPCCallbackAddCommand(input MythicRPCCallbackAddCommandMessage) Mythi
|
||||
}
|
||||
CallbackID = task.Callback.ID
|
||||
PayloadTypeID = task.Callback.Payload.PayloadTypeID
|
||||
OperatorID = task.OperatorID
|
||||
} else if input.AgentCallbackID != "" {
|
||||
err := database.DB.Get(&callback, `SELECT
|
||||
callback.id, callback.operator_id,
|
||||
payload.payload_type_id "callback.payload.payload_type_id"
|
||||
FROM callback
|
||||
JOIN payload on callback.registered_payload_id = payload.id
|
||||
WHERE callback.agent_callback_id = $1`, input.AgentCallbackID)
|
||||
WHERE callback.agent_callback_id = $1 AND callback.operation_id=$2`, input.AgentCallbackID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find task in MythicRPCCallbackAddCommand")
|
||||
response.Error = err.Error()
|
||||
@@ -72,7 +73,6 @@ func MythicRPCCallbackAddCommand(input MythicRPCCallbackAddCommandMessage) Mythi
|
||||
}
|
||||
CallbackID = callback.ID
|
||||
PayloadTypeID = callback.Payload.PayloadTypeID
|
||||
OperatorID = callback.OperatorID
|
||||
}
|
||||
if input.PayloadType != "" {
|
||||
err := database.DB.Get(&PayloadTypeID, `SELECT id FROM payloadtype WHERE "name"=$1`, input.PayloadType)
|
||||
@@ -82,70 +82,79 @@ func MythicRPCCallbackAddCommand(input MythicRPCCallbackAddCommandMessage) Mythi
|
||||
return response
|
||||
}
|
||||
}
|
||||
if CallbackID == 0 {
|
||||
if len(input.CallbackIDs) == 0 && CallbackID == 0 {
|
||||
response.Error = "No callback supplied"
|
||||
return response
|
||||
}
|
||||
if len(input.CallbackIDs) > 0 {
|
||||
for _, c := range input.CallbackIDs {
|
||||
err := CallbackAddCommand(c, PayloadTypeID, OperatorID, input.Commands)
|
||||
err := CallbackAddCommand(c, PayloadTypeID, authContext.OperatorID, input.Commands, authContext.APITokensID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to add commands to callback")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
err := CallbackAddCommand(CallbackID, PayloadTypeID, OperatorID, input.Commands)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to add commands to callback")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
if CallbackID != 0 {
|
||||
err := CallbackAddCommand(CallbackID, PayloadTypeID, authContext.OperatorID, input.Commands, authContext.APITokensID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to add commands to callback")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func CallbackAddCommand(callbackID int, payloadtypeID int, operatorID int, commands []string) error {
|
||||
func CallbackAddCommand(callbackID int, payloadtypeID int, operatorID int, commands []string, apitokensID int, operationID int) error {
|
||||
for _, command := range commands {
|
||||
logging.LogDebug("trying to add command", "cmd", command)
|
||||
// first check if the command is already loaded
|
||||
// if not, try to add it as a loaded command
|
||||
databaseCommand := databaseStructs.Command{}
|
||||
loadedCommand := databaseStructs.Loadedcommands{}
|
||||
if err := database.DB.Get(&databaseCommand, `SELECT
|
||||
err := database.DB.Get(&databaseCommand, `SELECT
|
||||
id, "version"
|
||||
FROM command
|
||||
WHERE command.cmd=$1 AND command.payload_type_id=$2`,
|
||||
command, payloadtypeID); err != nil {
|
||||
command, payloadtypeID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find command to load")
|
||||
return errors.New("Failed to find command: " + command)
|
||||
} else if err := database.DB.Get(&loadedCommand, `SELECT id
|
||||
}
|
||||
err = database.DB.Get(&loadedCommand, `SELECT
|
||||
loadedcommands.id
|
||||
FROM loadedcommands
|
||||
WHERE command_id=$1 AND callback_id=$2`,
|
||||
databaseCommand.ID, callbackID); err == nil {
|
||||
JOIN callback on callback.id = loadedcommands.callback_id
|
||||
WHERE command_id=$1 AND callback_id=$2 AND callback.operation_id=$3`,
|
||||
databaseCommand.ID, callbackID, operationID)
|
||||
if err == nil {
|
||||
continue
|
||||
} else if err == sql.ErrNoRows {
|
||||
}
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// this never existed, so let's add it as a loaded command
|
||||
loadedCommand.Version = databaseCommand.Version
|
||||
loadedCommand.CallbackID = callbackID
|
||||
loadedCommand.CommandID = databaseCommand.ID
|
||||
loadedCommand.OperatorID = operatorID
|
||||
if _, err := database.DB.NamedExec(`INSERT INTO loadedcommands
|
||||
("version", callback_id, command_id, operator_id)
|
||||
VALUES (:version, :callback_id, :command_id, :operator_id)`,
|
||||
loadedCommand); err != nil {
|
||||
if apitokensID > 0 {
|
||||
loadedCommand.APITokensID.Valid = true
|
||||
loadedCommand.APITokensID.Int64 = int64(apitokensID)
|
||||
}
|
||||
_, err = database.DB.NamedExec(`INSERT INTO loadedcommands
|
||||
("version", callback_id, command_id, operator_id, apitokens_id)
|
||||
VALUES (:version, :callback_id, :command_id, :operator_id, :apitokens_id)`,
|
||||
loadedCommand)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to mark command as loaded in callback")
|
||||
return err
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// we got some other sort of error
|
||||
logging.LogError(err, "Failed to query database for loaded command")
|
||||
return err
|
||||
continue
|
||||
}
|
||||
// we got some other sort of error
|
||||
logging.LogError(err, "Failed to query database for loaded command")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -154,11 +163,16 @@ func processMythicRPCCallbackAddCommand(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackAddCommandMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackAddCommand(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackAddCommand(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/utils"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
@@ -45,18 +47,19 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_SEARCH_COMMAND, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_SEARCH_COMMAND, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCCallbackSearchCommand, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCCallbackSearchCommand(input MythicRPCCallbackSearchCommandMessage) MythicRPCCallbackSearchCommandMessageResponse {
|
||||
func MythicRPCCallbackSearchCommand(input MythicRPCCallbackSearchCommandMessage, authContext RabbitMQAuthContext) MythicRPCCallbackSearchCommandMessageResponse {
|
||||
response := MythicRPCCallbackSearchCommandMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
foundCommands := []MythicRPCCommandSearchCommandData{}
|
||||
loadedCommands := []databaseStructs.Loadedcommands{}
|
||||
if input.CallbackID != nil {
|
||||
if err := database.DB.Select(&loadedCommands, `SELECT
|
||||
err := database.DB.Select(&loadedCommands, `SELECT
|
||||
command.needs_admin "command.needs_admin",
|
||||
command.help_cmd "command.help_cmd",
|
||||
command.description "command.description",
|
||||
@@ -70,18 +73,25 @@ func MythicRPCCallbackSearchCommand(input MythicRPCCallbackSearchCommandMessage)
|
||||
FROM
|
||||
loadedcommands
|
||||
JOIN command on loadedcommands.command_id = command.id
|
||||
WHERE loadedcommands.callback_id=$1`, input.CallbackID); err != nil {
|
||||
JOIN callback on callback.id = loadedcommands.callback_id
|
||||
WHERE loadedcommands.callback_id=$1 AND callback.operation_id=$2`, input.CallbackID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to search loaded commands for callback_id")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
} else if input.TaskID != nil {
|
||||
task := databaseStructs.Task{ID: *input.TaskID}
|
||||
if err := database.DB.Get(&task.CallbackID, `SELECT callback_id FROM task WHERE id=$1`, task.ID); err != nil {
|
||||
err := database.DB.Get(&task.CallbackID, `SELECT
|
||||
callback_id
|
||||
FROM task
|
||||
WHERE id=$1 AND operation_id=$2`, task.ID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find task number")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err := database.DB.Select(&loadedCommands, `SELECT
|
||||
}
|
||||
err = database.DB.Select(&loadedCommands, `SELECT
|
||||
command.needs_admin "command.needs_admin",
|
||||
command.help_cmd "command.help_cmd",
|
||||
command.description "command.description",
|
||||
@@ -95,7 +105,8 @@ func MythicRPCCallbackSearchCommand(input MythicRPCCallbackSearchCommandMessage)
|
||||
FROM
|
||||
loadedcommands
|
||||
JOIN command on loadedcommands.command_id = command.id
|
||||
WHERE loadedcommands.callback_id=$1`, task.CallbackID); err != nil {
|
||||
WHERE loadedcommands.callback_id=$1`, task.CallbackID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to search loaded commands for callback_id")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -181,11 +192,16 @@ func processMythicRPCCallbackSearchCommand(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackSearchCommandMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackSearchCommand(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackSearchCommand(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/eventing"
|
||||
@@ -39,12 +40,11 @@ type MythicRPCCallbackCreateMessage struct {
|
||||
ProcessName string `json:"process_name" mapstructure:"process_name"`
|
||||
Cwd string `json:"cwd" mapstructure:"cwd"`
|
||||
ImpersonationContext string `json:"impersonation_context" mapstructure:"impersonation_context"`
|
||||
EventStepInstanceID *int `json:"eventstepinstance_id" mapstructure:"eventstepinstance_id"`
|
||||
}
|
||||
type MythicRPCCallbackCreateMessageResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error"`
|
||||
CallbackUUID string `json:"callback_uuid"`
|
||||
AgentCallbackID string `json:"agent_callback_id"`
|
||||
CallbackID int `json:"callback_id"`
|
||||
CallbackDisplayID int `json:"callback_display_id"`
|
||||
}
|
||||
@@ -55,11 +55,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_CREATE,
|
||||
Handler: processMythicRPCCallbackCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CALLBACK_CREATE
|
||||
func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCallbackCreateMessageResponse {
|
||||
func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage, authContext RabbitMQAuthContext) MythicRPCCallbackCreateMessageResponse {
|
||||
response := MythicRPCCallbackCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -115,7 +116,7 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
|
||||
JOIN operation ON payload.operation_id = operation.id
|
||||
JOIN payloadtype ON payload.payload_type_id = payloadtype.id
|
||||
JOIN operator ON payload.operator_id = operator.id
|
||||
WHERE uuid=$1`, input.PayloadUUID)
|
||||
WHERE uuid=$1 AND operation_id=$2`, input.PayloadUUID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find payload when calling MythicRPCCallbackCreate")
|
||||
response.Error = err.Error()
|
||||
@@ -130,8 +131,11 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
|
||||
return response
|
||||
}
|
||||
callback.RegisteredPayloadID = payload.ID
|
||||
callback.OperationID = payload.Operation.ID
|
||||
callback.OperatorID = payload.OperatorID
|
||||
callback.OperationID = authContext.OperationID
|
||||
callback.OperatorID = authContext.OperatorID
|
||||
if callback.OperatorID == 0 {
|
||||
callback.OperatorID = payload.OperatorID
|
||||
}
|
||||
if input.Description == "" {
|
||||
callback.Description = payload.Description
|
||||
} else {
|
||||
@@ -152,9 +156,9 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
|
||||
return response
|
||||
}
|
||||
defer transaction.Rollback()
|
||||
if input.EventStepInstanceID != nil {
|
||||
if authContext.EventStepInstanceID > 0 {
|
||||
callback.EventStepInstanceID.Valid = true
|
||||
callback.EventStepInstanceID.Int64 = int64(*input.EventStepInstanceID)
|
||||
callback.EventStepInstanceID.Int64 = int64(authContext.EventStepInstanceID)
|
||||
}
|
||||
statement, err := transaction.PrepareNamed(`
|
||||
INSERT INTO callback
|
||||
@@ -192,10 +196,20 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
|
||||
return response
|
||||
}
|
||||
for _, pc := range payloadCommands {
|
||||
if _, err := database.DB.Exec(`INSERT INTO loadedcommands
|
||||
(command_id, "version", callback_id, operator_id)
|
||||
VALUES ($1, $2, $3, $4)`,
|
||||
pc.CommandID, pc.Version, callback.ID, callback.OperatorID); err != nil {
|
||||
loadedCommand := databaseStructs.Loadedcommands{
|
||||
CommandID: pc.CommandID,
|
||||
Version: pc.Version,
|
||||
CallbackID: callback.ID,
|
||||
OperatorID: callback.OperatorID,
|
||||
}
|
||||
if authContext.APITokensID > 0 {
|
||||
loadedCommand.APITokensID.Valid = true
|
||||
loadedCommand.APITokensID.Int64 = int64(authContext.APITokensID)
|
||||
}
|
||||
if _, err := database.DB.NamedExec(`INSERT INTO loadedcommands
|
||||
(command_id, "version", callback_id, operator_id, apitokens_id)
|
||||
VALUES (:command_id, :version, :callback_id, :operator_id, :apitokens_id)`,
|
||||
loadedCommand); err != nil {
|
||||
logging.LogError(err, "Failed to mark command as loaded into callback", "payloadcommand", pc)
|
||||
}
|
||||
}
|
||||
@@ -214,17 +228,20 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
|
||||
return response
|
||||
}
|
||||
for _, pc2p := range payloadC2Profiles {
|
||||
if err := database.DB.Select(&payloadC2ProfileParameterInstances, `SELECT
|
||||
err := database.DB.Select(&payloadC2ProfileParameterInstances, `SELECT
|
||||
*
|
||||
FROM c2profileparametersinstance
|
||||
WHERE payload_id=$1 AND c2_profile_id=$2
|
||||
`, payload.ID, pc2p.C2profile.ID); err != nil {
|
||||
`, payload.ID, pc2p.C2profile.ID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get c2 profile parameter instances from payload in MythicRPCCallbackCreate")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if _, err := database.DB.Exec(`INSERT INTO callbackc2profiles
|
||||
}
|
||||
_, err = database.DB.Exec(`INSERT INTO callbackc2profiles
|
||||
(callback_id, c2_profile_id)
|
||||
VALUES ($1, $2)`, callback.ID, pc2p.C2profile.ID); err != nil {
|
||||
VALUES ($1, $2)`, callback.ID, pc2p.C2profile.ID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to associate c2 profile with callback")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -237,16 +254,20 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
|
||||
OperationID: callback.OperationID,
|
||||
Metadata: "",
|
||||
}
|
||||
if _, err := database.DB.NamedExec(`INSERT INTO callbackgraphedge
|
||||
(source_id, destination_id, c2_profile_id, operation_id, metadata)
|
||||
VALUES (:source_id, :destination_id, :c2_profile_id, :operation_id, :metadata)`, edge); err != nil {
|
||||
if authContext.APITokensID > 0 {
|
||||
edge.APITokensID.Valid = true
|
||||
edge.APITokensID.Int64 = int64(authContext.APITokensID)
|
||||
}
|
||||
_, err = database.DB.NamedExec(`INSERT INTO callbackgraphedge
|
||||
(source_id, destination_id, c2_profile_id, operation_id, metadata, apitokens_id)
|
||||
VALUES (:source_id, :destination_id, :c2_profile_id, :operation_id, :metadata, :apitokens_id)`, edge)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to create new callbackgraphedge for egress c2 profile in MythicRPCCallbackCreate")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
logging.LogInfo("Created new callbackgraph edge", "c2", input.C2ProfileName, "callback", callback.ID)
|
||||
callbackGraph.Add(callback, callback, pc2p.C2profile.Name, false)
|
||||
}
|
||||
logging.LogInfo("Created new callbackgraph edge", "c2", input.C2ProfileName, "callback", callback.ID)
|
||||
callbackGraph.Add(callback, callback, pc2p.C2profile.Name, false)
|
||||
}
|
||||
for _, c2paraminstance := range payloadC2ProfileParameterInstances {
|
||||
c2Instance := databaseStructs.C2profileparametersinstance{
|
||||
@@ -305,27 +326,29 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
|
||||
}
|
||||
|
||||
}
|
||||
addCommandAugmentsToCallback(callback.ID, payload.Os, payload.Payloadtype.Name, callback.OperatorID)
|
||||
addCommandAugmentsToCallback(callback.ID, payload.Os, payload.Payloadtype.Name, callback.OperatorID, authContext)
|
||||
operationsMsg := fmt.Sprintf("New Callback (%d) %s@%s with pid %d", callback.DisplayID, callback.User, callback.Host, callback.PID)
|
||||
go SendAllOperationsMessage(operationsMsg, callback.OperationID, "", database.MESSAGE_LEVEL_INFO, false)
|
||||
// prep data to send for log messages and webhook messages
|
||||
webhookData := NewCallbackWebhookData{
|
||||
User: callback.User,
|
||||
Host: callback.Host,
|
||||
IPs: callback.IP,
|
||||
Domain: callback.Domain,
|
||||
ExternalIP: callback.ExternalIp,
|
||||
ProcessName: callback.ProcessName,
|
||||
PID: callback.PID,
|
||||
Os: callback.Os,
|
||||
Architecture: callback.Architecture,
|
||||
AgentType: payload.Payloadtype.Name,
|
||||
Description: callback.Description,
|
||||
ExtraInfo: callback.ExtraInfo,
|
||||
SleepInfo: callback.SleepInfo,
|
||||
DisplayID: callback.DisplayID,
|
||||
ID: callback.ID,
|
||||
IntegrityLevel: callback.IntegrityLevel,
|
||||
User: callback.User,
|
||||
Host: callback.Host,
|
||||
IPs: callback.IP,
|
||||
Domain: callback.Domain,
|
||||
ExternalIP: callback.ExternalIp,
|
||||
ProcessName: callback.ProcessName,
|
||||
PID: callback.PID,
|
||||
Os: callback.Os,
|
||||
Architecture: callback.Architecture,
|
||||
AgentType: payload.Payloadtype.Name,
|
||||
Description: callback.Description,
|
||||
ExtraInfo: callback.ExtraInfo,
|
||||
SleepInfo: callback.SleepInfo,
|
||||
DisplayID: callback.DisplayID,
|
||||
ID: callback.ID,
|
||||
IntegrityLevel: callback.IntegrityLevel,
|
||||
Cwd: callback.Cwd,
|
||||
ImpersonationContext: callback.ImpersonationContext,
|
||||
}
|
||||
webhookMapData := map[string]interface{}{}
|
||||
err = mapstructure.Decode(webhookData, &webhookMapData)
|
||||
@@ -350,11 +373,11 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
|
||||
callback.Host, payload.ID, callback.OperationID); err != nil {
|
||||
logging.LogError(err, "Failed to register callback on host")
|
||||
}
|
||||
response.CallbackUUID = callback.AgentCallbackID
|
||||
response.AgentCallbackID = callback.AgentCallbackID
|
||||
response.CallbackID = callback.ID
|
||||
response.CallbackDisplayID = callback.DisplayID
|
||||
response.Success = true
|
||||
go sendPtOnNewCallback(callback.ID)
|
||||
go sendPtOnNewCallback(callback.ID, authContext)
|
||||
go func(triggerData databaseStructs.Callback) {
|
||||
EventingChannel <- EventNotification{
|
||||
Trigger: eventing.TriggerCallbackNew,
|
||||
@@ -364,14 +387,13 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
|
||||
PayloadID: callback.RegisteredPayloadID,
|
||||
}
|
||||
}(callback)
|
||||
|
||||
return response
|
||||
}
|
||||
func sendPtOnNewCallback(callbackId int) {
|
||||
func sendPtOnNewCallback(callbackId int, authContext RabbitMQAuthContext) {
|
||||
allCallbackData := GetOnNewCallbackConfigurationForContainer(callbackId)
|
||||
_ = RabbitMQConnection.SendPtOnNewCallback(allCallbackData)
|
||||
_ = RabbitMQConnection.SendPtOnNewCallback(allCallbackData, authContext)
|
||||
}
|
||||
func addCommandAugmentsToCallback(callbackID int, payloadOS string, payloadType string, operatorID int) {
|
||||
func addCommandAugmentsToCallback(callbackID int, payloadOS string, payloadType string, operatorID int, authContext RabbitMQAuthContext) {
|
||||
commandAugmentPayloadTypes := []databaseStructs.Payloadtype{}
|
||||
err := database.DB.Select(&commandAugmentPayloadTypes, `SELECT * FROM payloadtype
|
||||
WHERE agent_type=$1 AND deleted=false`, "command_augment")
|
||||
@@ -408,11 +430,20 @@ func addCommandAugmentsToCallback(callbackID int, payloadOS string, payloadType
|
||||
// only add augment commands where the command is builtin or suggested
|
||||
// this allows 'load' and 'unload' style commands to be created for more granular control
|
||||
if builtin || suggested {
|
||||
_, err = database.DB.Exec(`INSERT INTO loadedcommands
|
||||
(command_id, callback_id, operator_id, version)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT DO NOTHING`,
|
||||
commandAugmentCommands[j].ID, callbackID, operatorID, commandAugmentCommands[j].Version)
|
||||
loadedCommand := databaseStructs.Loadedcommands{
|
||||
CommandID: commandAugmentCommands[j].ID,
|
||||
CallbackID: callbackID,
|
||||
OperatorID: operatorID,
|
||||
Version: commandAugmentCommands[j].Version,
|
||||
}
|
||||
if authContext.APITokensID > 0 {
|
||||
loadedCommand.APITokensID.Valid = true
|
||||
loadedCommand.APITokensID.Int64 = int64(authContext.APITokensID)
|
||||
}
|
||||
_, err = database.DB.NamedExec(`INSERT INTO loadedcommands
|
||||
(command_id, callback_id, operator_id, version, apitokens_id)
|
||||
VALUES (:command_id, :callback_id, :operator_id, :version, :apitokens_id)
|
||||
ON CONFLICT DO NOTHING`, loadedCommand)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to add loaded command to callback")
|
||||
}
|
||||
@@ -425,11 +456,16 @@ func processMythicRPCCallbackCreate(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
@@ -29,6 +30,7 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_DECRYPT_BYTES,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_DECRYPT_BYTES,
|
||||
Handler: processMythicRPCCallbackDecryptBytes,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,14 +40,14 @@ func MythicRPCCallbackDecryptBytes(input MythicRPCCallbackDecryptBytesMessage) M
|
||||
Success: false,
|
||||
}
|
||||
|
||||
if cipherText, err := CallbackDecryptMessage(input); err != nil {
|
||||
cipherText, err := CallbackDecryptMessage(input)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
response.Message = cipherText
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
response.Message = cipherText
|
||||
return response
|
||||
}
|
||||
|
||||
func CallbackDecryptMessage(input MythicRPCCallbackDecryptBytesMessage) ([]byte, error) {
|
||||
@@ -148,11 +150,11 @@ func processMythicRPCCallbackDecryptBytes(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackDecryptBytesMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackDecryptBytes(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
return MythicRPCCallbackDecryptBytes(incomingMessage)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
|
||||
@@ -10,9 +12,7 @@ import (
|
||||
)
|
||||
|
||||
type MythicRPCCallbackDisplayToRealIdSearchMessage struct {
|
||||
CallbackDisplayID int `json:"callback_display_id"`
|
||||
OperationName *string `json:"operation_name"`
|
||||
OperationID *int `json:"operation_id"`
|
||||
CallbackDisplayID int `json:"callback_display_id"`
|
||||
}
|
||||
|
||||
// Every mythicRPC function call must return a response that includes the following two values
|
||||
@@ -28,63 +28,47 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_DISPLAY_TO_REAL_ID_SEARCH, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_DISPLAY_TO_REAL_ID_SEARCH, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCCallbackDisplayToRealIdSearch, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCCallbackDisplayToRealIdSearch(input MythicRPCCallbackDisplayToRealIdSearchMessage) MythicRPCCallbackDisplayToRealIdSearchMessageResponse {
|
||||
func MythicRPCCallbackDisplayToRealIdSearch(input MythicRPCCallbackDisplayToRealIdSearchMessage, authContext RabbitMQAuthContext) MythicRPCCallbackDisplayToRealIdSearchMessageResponse {
|
||||
response := MythicRPCCallbackDisplayToRealIdSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
searchString := ""
|
||||
if input.OperationName != nil {
|
||||
searchString = `SELECT
|
||||
callback.id
|
||||
FROM
|
||||
callback
|
||||
JOIN operation on callback.operation_id = operation.id
|
||||
WHERE callback.display_id=$1 AND operation.name=$2`
|
||||
callback := databaseStructs.Callback{}
|
||||
if err := database.DB.Get(&callback, searchString, input.CallbackDisplayID, *input.OperationName); err != nil {
|
||||
logging.LogError(err, "Failed to find task based on task id and operation name")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.CallbackID = callback.ID
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
} else if input.OperationID != nil {
|
||||
searchString = `SELECT
|
||||
callback.id
|
||||
FROM
|
||||
callback
|
||||
WHERE callback.display_id=$1 AND callback.operation_id=$2`
|
||||
callback := databaseStructs.Callback{}
|
||||
if err := database.DB.Get(&callback, searchString, input.CallbackDisplayID, *input.OperationID); err != nil {
|
||||
logging.LogError(err, "Failed to find task based on task id and operation id")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.CallbackID = callback.ID
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
} else {
|
||||
response.Error = "Must specify operation name or operation id"
|
||||
searchString = `SELECT
|
||||
callback.id
|
||||
FROM
|
||||
callback
|
||||
WHERE callback.display_id=$1 AND callback.operation_id=$2`
|
||||
callback := databaseStructs.Callback{}
|
||||
err := database.DB.Get(&callback, searchString, input.CallbackDisplayID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find task based on task id and operation id")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.CallbackID = callback.ID
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCCallbackDisplayToRealIdSearch(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCCallbackDisplayToRealIdSearchMessage{}
|
||||
responseMsg := MythicRPCCallbackDisplayToRealIdSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackDisplayToRealIdSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackDisplayToRealIdSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
@@ -23,6 +24,7 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_EDGE_REMOVE,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_EDGE_REMOVE,
|
||||
Handler: processMythicRPCCallbackEdgeRemove,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -45,11 +47,11 @@ func processMythicRPCCallbackEdgeRemove(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackEdgeRemoveMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackEdgeRemove(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
return MythicRPCCallbackEdgeRemove(incomingMessage)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -36,11 +37,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_EDGE_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_EDGE_SEARCH,
|
||||
Handler: processMythicRPCCallbackEdgeSearch,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CALLBACK_EDGE_SEARCH
|
||||
func MythicRPCCallbackEdgeSearch(input MythicRPCCallbackEdgeSearchMessage) MythicRPCCallbackEdgeSearchMessageResponse {
|
||||
func MythicRPCCallbackEdgeSearch(input MythicRPCCallbackEdgeSearchMessage, authContext RabbitMQAuthContext) MythicRPCCallbackEdgeSearchMessageResponse {
|
||||
response := MythicRPCCallbackEdgeSearchMessageResponse{
|
||||
Success: false,
|
||||
Results: []MythicRPCCallbackEdgeSearchMessageResult{},
|
||||
@@ -54,11 +56,12 @@ func MythicRPCCallbackEdgeSearch(input MythicRPCCallbackEdgeSearchMessage) Mythi
|
||||
JOIN callback d on callbackgraphedge.destination_id = d.id
|
||||
JOIN c2profile c2 on callbackgraphedge.c2_profile_id = c2.id
|
||||
WHERE (s.agent_callback_id=:agent_callback_id OR s.id=:id OR
|
||||
d.agent_callback_id=:agent_callback_id OR d.id=:id)`
|
||||
d.agent_callback_id=:agent_callback_id OR d.id=:id) AND callbackgraphedge.operation_id=:operation_id`
|
||||
|
||||
rows, err := database.DB.NamedQuery(searchString, databaseStructs.Callback{
|
||||
ID: input.AgentCallbackID,
|
||||
AgentCallbackID: input.AgentCallbackUUID,
|
||||
OperationID: authContext.OperationID,
|
||||
})
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to search callback edge information")
|
||||
@@ -107,11 +110,16 @@ func processMythicRPCCallbackEdgeSearch(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackEdgeSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackEdgeSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackEdgeSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
@@ -28,6 +29,7 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_ENCRYPT_BYTES,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_ENCRYPT_BYTES,
|
||||
Handler: processMythicRPCCallbackEncryptBytes,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -36,14 +38,14 @@ func MythicRPCCallbackEncryptBytes(input MythicRPCCallbackEncryptBytesMessage) M
|
||||
response := MythicRPCCallbackEncryptBytesMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if cipherText, err := CallbackEncryptMessage(input); err != nil {
|
||||
cipherText, err := CallbackEncryptMessage(input)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
response.Message = cipherText
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
response.Message = cipherText
|
||||
return response
|
||||
}
|
||||
func CallbackEncryptMessage(input MythicRPCCallbackEncryptBytesMessage) ([]byte, error) {
|
||||
cachedInfo, err := LookupEncryptionData(input.C2Profile, input.AgentCallbackID, false)
|
||||
@@ -105,11 +107,11 @@ func processMythicRPCCallbackEncryptBytes(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackEncryptBytesMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackEncryptBytes(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
return MythicRPCCallbackEncryptBytes(incomingMessage)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
@@ -27,6 +28,7 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_NEXT_CHECKIN_RANGE, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_NEXT_CHECKIN_RANGE, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCCallbackNextCheckinRange, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_READ},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -53,11 +55,11 @@ func processMythicRPCCallbackNextCheckinRange(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackNextCheckinRangeMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackNextCheckinRange(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
return MythicRPCCallbackNextCheckinRange(incomingMessage)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -26,11 +28,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_REMOVE_COMMAND,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_REMOVE_COMMAND,
|
||||
Handler: processMythicRPCCallbackRemoveCommand,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CALLBACK_REMOVE_COMMAND
|
||||
func MythicRPCCallbackRemoveCommand(input MythicRPCCallbackRemoveCommandMessage) MythicRPCCallbackRemoveCommandMessageResponse {
|
||||
func MythicRPCCallbackRemoveCommand(input MythicRPCCallbackRemoveCommandMessage, authContext RabbitMQAuthContext) MythicRPCCallbackRemoveCommandMessageResponse {
|
||||
response := MythicRPCCallbackRemoveCommandMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -45,7 +48,7 @@ func MythicRPCCallbackRemoveCommand(input MythicRPCCallbackRemoveCommandMessage)
|
||||
FROM task
|
||||
JOIN callback on task.callback_id = callback.id
|
||||
JOIN payload on callback.registered_payload_id = payload.id
|
||||
WHERE task.id=$1`, input.TaskID)
|
||||
WHERE task.id=$1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find task in MythicRPCCallbackAddCommand")
|
||||
response.Error = err.Error()
|
||||
@@ -59,7 +62,7 @@ func MythicRPCCallbackRemoveCommand(input MythicRPCCallbackRemoveCommandMessage)
|
||||
payload.payload_type_id "callback.payload.payload_type_id"
|
||||
FROM callback
|
||||
JOIN payload on callback.registered_payload_id = payload.id
|
||||
WHERE callback.agent_callback_id = $1`, input.AgentCallbackID)
|
||||
WHERE callback.agent_callback_id = $1 AND callback.operation_id=$2`, input.AgentCallbackID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find task in MythicRPCCallbackAddCommand")
|
||||
response.Error = err.Error()
|
||||
@@ -98,18 +101,14 @@ func MythicRPCCallbackRemoveCommand(input MythicRPCCallbackRemoveCommandMessage)
|
||||
return response
|
||||
}
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
if CallbackID == 0 {
|
||||
response.Error = "No callback supplied"
|
||||
return response
|
||||
}
|
||||
err := CallbackRemoveCommand(CallbackID, commandIDs)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to remove commands to callback")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
if CallbackID != 0 {
|
||||
err := CallbackRemoveCommand(CallbackID, commandIDs)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to remove commands to callback")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
@@ -131,11 +130,16 @@ func processMythicRPCCallbackRemoveCommand(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackRemoveCommandMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackRemoveCommand(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackRemoveCommand(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
@@ -15,8 +16,6 @@ import (
|
||||
)
|
||||
|
||||
type MythicRPCCallbackSearchMessage struct {
|
||||
AgentCallbackUUID string `json:"agent_callback_id"`
|
||||
CallbackID int `json:"callback_id"`
|
||||
SearchCallbackID *int `json:"search_callback_id"`
|
||||
SearchCallbackDisplayID *int `json:"search_callback_display_id"`
|
||||
SearchCallbackUUID *string `json:"search_callback_uuid"`
|
||||
@@ -78,27 +77,18 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_SEARCH,
|
||||
Handler: processMythicRPCCallbackSearch,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CALLBACK_SEARCH
|
||||
func MythicRPCCallbackSearch(input MythicRPCCallbackSearchMessage) MythicRPCCallbackSearchMessageResponse {
|
||||
func MythicRPCCallbackSearch(input MythicRPCCallbackSearchMessage, authContext RabbitMQAuthContext) MythicRPCCallbackSearchMessageResponse {
|
||||
response := MythicRPCCallbackSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
searchResults := databaseStructs.Callback{}
|
||||
callback := databaseStructs.Callback{}
|
||||
err := database.DB.Get(&callback, `SELECT
|
||||
operation_id
|
||||
FROM callback
|
||||
WHERE agent_callback_id=$1 OR id=$2`, input.AgentCallbackUUID, input.CallbackID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find callback UUID")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
targetCallback := databaseStructs.Callback{
|
||||
OperationID: callback.OperationID,
|
||||
OperationID: authContext.OperationID,
|
||||
}
|
||||
if input.SearchCallbackID != nil {
|
||||
targetCallback.ID = *input.SearchCallbackID
|
||||
@@ -207,7 +197,6 @@ func MythicRPCCallbackSearch(input MythicRPCCallbackSearchMessage) MythicRPCCall
|
||||
result.Timestamp = searchResults.Timestamp
|
||||
result.LastCheckin = searchResults.LastCheckin
|
||||
response.Results = append(response.Results, result)
|
||||
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
@@ -217,11 +206,16 @@ func processMythicRPCCallbackSearch(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/utils"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
@@ -52,11 +53,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACK_UPDATE,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACK_UPDATE,
|
||||
Handler: processMythicRPCCallbackUpdate,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CALLBACK_UPDATE
|
||||
func MythicRPCCallbackUpdate(input MythicRPCCallbackUpdateMessage) MythicRPCCallbackUpdateMessageResponse {
|
||||
func MythicRPCCallbackUpdate(input MythicRPCCallbackUpdateMessage, authContext RabbitMQAuthContext) MythicRPCCallbackUpdateMessageResponse {
|
||||
response := MythicRPCCallbackUpdateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -65,7 +67,7 @@ func MythicRPCCallbackUpdate(input MythicRPCCallbackUpdateMessage) MythicRPCCall
|
||||
if err := database.DB.Get(&callback, `SELECT
|
||||
*
|
||||
FROM callback
|
||||
WHERE agent_callback_id=$1`, input.AgentCallbackID); err != nil {
|
||||
WHERE agent_callback_id=$1 AND operation_id=$2`, input.AgentCallbackID, authContext.OperationID); err != nil {
|
||||
logging.LogError(err, "Failed to find callback UUID")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -74,7 +76,7 @@ func MythicRPCCallbackUpdate(input MythicRPCCallbackUpdateMessage) MythicRPCCall
|
||||
if err := database.DB.Get(&callback, `SELECT
|
||||
*
|
||||
FROM callback
|
||||
WHERE id=$1`, input.CallbackID); err != nil {
|
||||
WHERE id=$1 AND operation_id=$2`, input.CallbackID, authContext.OperationID); err != nil {
|
||||
logging.LogError(err, "Failed to find callback ID")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -84,7 +86,7 @@ func MythicRPCCallbackUpdate(input MythicRPCCallbackUpdateMessage) MythicRPCCall
|
||||
callback.*
|
||||
FROM callback
|
||||
JOIN task on task.callback_id = callback.id
|
||||
WHERE task.id=$1`, *input.TaskID); err != nil {
|
||||
WHERE task.id=$1 AND task.operation_id=$2`, *input.TaskID, authContext.OperationID); err != nil {
|
||||
logging.LogError(err, "failed to get callback information from task")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -207,11 +209,16 @@ func processMythicRPCCallbackUpdate(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackUpdateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackUpdate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackUpdate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -24,11 +26,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACKTOKEN_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACKTOKEN_CREATE,
|
||||
Handler: processMythicRPCCallbackTokenCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CALLBACKTOKEN_CREATE
|
||||
func MythicRPCCallbackTokenCreate(input MythicRPCCallbackTokenCreateMessage) MythicRPCCallbackTokenCreateMessageResponse {
|
||||
func MythicRPCCallbackTokenCreate(input MythicRPCCallbackTokenCreateMessage, authContext RabbitMQAuthContext) MythicRPCCallbackTokenCreateMessageResponse {
|
||||
response := MythicRPCCallbackTokenCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -40,7 +43,7 @@ func MythicRPCCallbackTokenCreate(input MythicRPCCallbackTokenCreateMessage) Myt
|
||||
input.CallbackTokens[i].Action = "add"
|
||||
}
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
task.id, task.status, task.completed, task.status_timestamp_processed, task.operator_id, task.operation_id,
|
||||
callback.host "callback.host",
|
||||
callback.user "callback.user",
|
||||
@@ -51,29 +54,36 @@ func MythicRPCCallbackTokenCreate(input MythicRPCCallbackTokenCreateMessage) Myt
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
JOIN payload ON callback.registered_payload_id = payload.id
|
||||
WHERE task.id = $1`, input.TaskID); err != nil {
|
||||
WHERE task.id = $1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch task")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err := handleAgentMessagePostResponseCallbackTokens(task, &input.CallbackTokens); err != nil {
|
||||
}
|
||||
err = handleAgentMessagePostResponseCallbackTokens(task, &input.CallbackTokens)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to create processes in MythicRPCProcessCreate")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCCallbackTokenCreate(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCCallbackTokenCreateMessage{}
|
||||
responseMsg := MythicRPCCallbackTokenCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackTokenCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackTokenCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -24,11 +26,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACKTOKEN_REMOVE,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACKTOKEN_REMOVE,
|
||||
Handler: processMythicRPCCallbackTokenRemove,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CALLBACKTOKEN_CREATE
|
||||
func MythicRPCCallbackTokenRemove(input MythicRPCCallbackTokenRemoveMessage) MythicRPCCallbackTokenRemoveMessageResponse {
|
||||
func MythicRPCCallbackTokenRemove(input MythicRPCCallbackTokenRemoveMessage, authContext RabbitMQAuthContext) MythicRPCCallbackTokenRemoveMessageResponse {
|
||||
response := MythicRPCCallbackTokenRemoveMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -40,7 +43,7 @@ func MythicRPCCallbackTokenRemove(input MythicRPCCallbackTokenRemoveMessage) Myt
|
||||
for i := 0; i < len(input.CallbackTokens); i++ {
|
||||
input.CallbackTokens[i].Action = "remove"
|
||||
}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
task.id, task.status, task.completed, task.status_timestamp_processed, task.operator_id, task.operation_id,
|
||||
callback.host "callback.host",
|
||||
callback.user "callback.user",
|
||||
@@ -51,29 +54,36 @@ func MythicRPCCallbackTokenRemove(input MythicRPCCallbackTokenRemoveMessage) Myt
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
JOIN payload ON callback.registered_payload_id = payload.id
|
||||
WHERE task.id = $1`, input.TaskID); err != nil {
|
||||
WHERE task.id = $1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch task")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err := handleAgentMessagePostResponseCallbackTokens(task, &input.CallbackTokens); err != nil {
|
||||
}
|
||||
err = handleAgentMessagePostResponseCallbackTokens(task, &input.CallbackTokens)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to create processes in MythicRPCProcessCreate")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCCallbackTokenRemove(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCCallbackTokenRemoveMessage{}
|
||||
responseMsg := MythicRPCCallbackTokenRemoveMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackTokenRemove(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackTokenRemove(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -26,11 +27,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CALLBACKTOKEN_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_CALLBACKTOKEN_SEARCH,
|
||||
Handler: processMythicRPCCallbackTokenSearch,
|
||||
Scopes: []string{mythicjwt.SCOPE_CALLBACK_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CALLBACKTOKEN_CREATE
|
||||
func MythicRPCCallbackTokenSearch(input MythicRPCCallbackTokenSearchMessage) MythicRPCCallbackTokenSearchMessageResponse {
|
||||
func MythicRPCCallbackTokenSearch(input MythicRPCCallbackTokenSearchMessage, authContext RabbitMQAuthContext) MythicRPCCallbackTokenSearchMessageResponse {
|
||||
response := MythicRPCCallbackTokenSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -40,7 +42,7 @@ func MythicRPCCallbackTokenSearch(input MythicRPCCallbackTokenSearchMessage) Myt
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
task.id, task.callback_id
|
||||
FROM task
|
||||
WHERE task.id = $1`, input.TaskID)
|
||||
WHERE task.id = $1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch task")
|
||||
response.Error = err.Error()
|
||||
@@ -48,13 +50,23 @@ func MythicRPCCallbackTokenSearch(input MythicRPCCallbackTokenSearchMessage) Myt
|
||||
}
|
||||
callbackID = task.CallbackID
|
||||
} else if input.CallbackID != nil {
|
||||
callbackID = *input.CallbackID
|
||||
callback := databaseStructs.Callback{}
|
||||
err := database.DB.Get(&callback, `SELECT
|
||||
callback.id
|
||||
FROM callback
|
||||
WHERE callback.id = $1 AND callback.operation_id=$2`, *input.CallbackID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch callback")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
callbackID = callback.ID
|
||||
} else if input.AgentCallbackID != nil {
|
||||
callback := databaseStructs.Callback{}
|
||||
err := database.DB.Get(&callback, `SELECT
|
||||
callback.id
|
||||
FROM callback
|
||||
WHERE callback.agent_callback_id = $1`, input.AgentCallbackID)
|
||||
WHERE callback.agent_callback_id = $1 AND callback.operation_id=$2`, *input.AgentCallbackID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch callback")
|
||||
response.Error = err.Error()
|
||||
@@ -108,11 +120,16 @@ func processMythicRPCCallbackTokenSearch(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCallbackTokenSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCallbackTokenSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCallbackTokenSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/its-a-feature/Mythic/utils"
|
||||
"reflect"
|
||||
|
||||
"github.com/its-a-feature/Mythic/utils"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -47,6 +48,7 @@ func init() {
|
||||
Queue: MYTHIC_RPC_COMMAND_SEARCH, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_COMMAND_SEARCH, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCCommandSearch, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -70,13 +72,14 @@ func MythicRPCCommandSearch(input MythicRPCCommandSearchMessage) MythicRPCComman
|
||||
}
|
||||
foundCommands := []MythicRPCCommandSearchCommandData{}
|
||||
commands := []databaseStructs.Command{}
|
||||
if err := database.DB.Select(&commands, `SELECT
|
||||
err := database.DB.Select(&commands, `SELECT
|
||||
command.*,
|
||||
payloadtype.name "payloadtype.name"
|
||||
FROM
|
||||
command
|
||||
JOIN payloadtype on command.payload_type_id = payloadtype.id
|
||||
WHERE payloadtype.name=$1 AND command.deleted=false`, input.SearchPayloadTypeName); err != nil {
|
||||
WHERE payloadtype.name=$1 AND command.deleted=false`, input.SearchPayloadTypeName)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to search for commands")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -95,7 +98,8 @@ func MythicRPCCommandSearch(input MythicRPCCommandSearchMessage) MythicRPCComman
|
||||
uiFeatures := command.SupportedUiFeatures.StructValue()
|
||||
stringUIFeatures := interfaceArrayToStringArray(uiFeatures)
|
||||
attributes := map[string]interface{}{}
|
||||
if err := command.Attributes.Unmarshal(&attributes); err != nil {
|
||||
err = command.Attributes.Unmarshal(&attributes)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get attributes from command")
|
||||
response.Error = "Failed to get attributes from command"
|
||||
return response
|
||||
@@ -198,11 +202,11 @@ func processMythicRPCCommandSearch(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCCommandSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCommandSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
return MythicRPCCommandSearch(incomingMessage)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -24,11 +26,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CREDENTIAL_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_CREDENTIAL_CREATE,
|
||||
Handler: processMythicRPCCredentialCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_CREDENTIAL_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CREDENTIAL_CREATE
|
||||
func MythicRPCCredentialCreate(input MythicRPCCredentialCreateMessage) MythicRPCCredentialCreateMessageResponse {
|
||||
func MythicRPCCredentialCreate(input MythicRPCCredentialCreateMessage, authContext RabbitMQAuthContext) MythicRPCCredentialCreateMessageResponse {
|
||||
response := MythicRPCCredentialCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -37,8 +40,9 @@ func MythicRPCCredentialCreate(input MythicRPCCredentialCreateMessage) MythicRPC
|
||||
return response
|
||||
}
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
task.id, task.status, task.completed, task.status_timestamp_processed, task.operator_id, task.operation_id,
|
||||
task.apitokens_id, task.eventstepinstance_id,
|
||||
callback.host "callback.host",
|
||||
callback.user "callback.user",
|
||||
callback.id "callback.id",
|
||||
@@ -48,29 +52,36 @@ func MythicRPCCredentialCreate(input MythicRPCCredentialCreateMessage) MythicRPC
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
JOIN payload ON callback.registered_payload_id = payload.id
|
||||
WHERE task.id = $1`, input.TaskID); err != nil {
|
||||
WHERE task.id = $1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch task")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err := handleAgentMessagePostResponseCredentials(task, &input.Credentials); err != nil {
|
||||
}
|
||||
err = handleAgentMessagePostResponseCredentials(task, &input.Credentials)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to create credentials in MythicRPCCredentialCreate")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCCredentialCreate(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCCredentialCreateMessage{}
|
||||
responseMsg := MythicRPCCredentialCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCredentialCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCredentialCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
type MythicRPCCredentialSearchMessage struct {
|
||||
TaskID int `json:"task_id"` //required
|
||||
SearchCredentials MythicRPCCredentialSearchCredentialData `json:"credentials"`
|
||||
}
|
||||
type MythicRPCCredentialSearchMessageResponse struct {
|
||||
@@ -34,86 +34,82 @@ func init() {
|
||||
Queue: MYTHIC_RPC_CREDENTIAL_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_CREDENTIAL_SEARCH,
|
||||
Handler: processMythicRPCCredentialSearch,
|
||||
Scopes: []string{mythicjwt.SCOPE_CREDENTIAL_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_CREDENTIAL_SEARCH
|
||||
func MythicRPCCredentialSearch(input MythicRPCCredentialSearchMessage) MythicRPCCredentialSearchMessageResponse {
|
||||
func MythicRPCCredentialSearch(input MythicRPCCredentialSearchMessage, authContext RabbitMQAuthContext) MythicRPCCredentialSearchMessageResponse {
|
||||
response := MythicRPCCredentialSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
params := []interface{}{}
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
task.id,
|
||||
task.operation_id
|
||||
FROM task
|
||||
WHERE task.id=$1`, input.TaskID); err != nil {
|
||||
credentials := []databaseStructs.Credential{}
|
||||
params = append(params, authContext.OperationID)
|
||||
searchString := fmt.Sprintf(`SELECT * FROM credential WHERE operation_id=$%d `, len(params))
|
||||
if input.SearchCredentials.Type != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Type+"%")
|
||||
searchString += fmt.Sprintf("AND \"type\" ILIKE $%d ", len(params))
|
||||
}
|
||||
if input.SearchCredentials.Credential != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Credential+"%")
|
||||
searchString += fmt.Sprintf("AND credential LIKE $%d ", len(params))
|
||||
}
|
||||
if input.SearchCredentials.Account != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Account+"%")
|
||||
searchString += fmt.Sprintf("AND account ILIKE $%d ", len(params))
|
||||
}
|
||||
if input.SearchCredentials.Realm != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Realm+"%")
|
||||
searchString += fmt.Sprintf("AND realm ILIKE $%d ", len(params))
|
||||
}
|
||||
if input.SearchCredentials.Comment != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Comment+"%")
|
||||
searchString += fmt.Sprintf("AND comment ILIKE $%d ", len(params))
|
||||
}
|
||||
if input.SearchCredentials.Metadata != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Metadata+"%")
|
||||
searchString += fmt.Sprintf("AND metadata ILIKE $%d ", len(params))
|
||||
}
|
||||
searchString += " ORDER BY id DESC"
|
||||
err := database.DB.Select(&credentials, searchString, params...)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to search for credentials")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
credentials := []databaseStructs.Credential{}
|
||||
params = append(params, task.OperationID)
|
||||
searchString := fmt.Sprintf(`SELECT * FROM credential WHERE operation_id=$%d `, len(params))
|
||||
|
||||
if input.SearchCredentials.Type != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Type+"%")
|
||||
searchString += fmt.Sprintf("AND \"type\" ILIKE $%d ", len(params))
|
||||
}
|
||||
if input.SearchCredentials.Credential != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Credential+"%")
|
||||
searchString += fmt.Sprintf("AND credential LIKE $%d ", len(params))
|
||||
}
|
||||
if input.SearchCredentials.Account != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Account+"%")
|
||||
searchString += fmt.Sprintf("AND account ILIKE $%d ", len(params))
|
||||
}
|
||||
if input.SearchCredentials.Realm != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Realm+"%")
|
||||
searchString += fmt.Sprintf("AND realm ILIKE $%d ", len(params))
|
||||
}
|
||||
if input.SearchCredentials.Comment != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Comment+"%")
|
||||
searchString += fmt.Sprintf("AND comment ILIKE $%d ", len(params))
|
||||
}
|
||||
if input.SearchCredentials.Metadata != nil {
|
||||
params = append(params, "%"+*input.SearchCredentials.Metadata+"%")
|
||||
searchString += fmt.Sprintf("AND metadata ILIKE $%d ", len(params))
|
||||
}
|
||||
searchString += " ORDER BY id DESC"
|
||||
if err := database.DB.Select(&credentials, searchString, params...); err != nil {
|
||||
logging.LogError(err, "Failed to search for credentials")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
returnedCredentials := []MythicRPCCredentialSearchCredentialData{}
|
||||
if marshalledBytes, err := json.Marshal(credentials); err != nil {
|
||||
logging.LogError(err, "Failed to marshal credential result")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err := json.Unmarshal(marshalledBytes, &returnedCredentials); err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal credential results")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
response.Credentials = returnedCredentials
|
||||
return response
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
returnedCredentials := []MythicRPCCredentialSearchCredentialData{}
|
||||
marshalledBytes, err := json.Marshal(credentials)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to marshal credential result")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
err = json.Unmarshal(marshalledBytes, &returnedCredentials)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal credential results")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
response.Credentials = returnedCredentials
|
||||
return response
|
||||
}
|
||||
func processMythicRPCCredentialSearch(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCCredentialSearchMessage{}
|
||||
responseMsg := MythicRPCCredentialSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCCredentialSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCredentialSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -12,8 +13,6 @@ import (
|
||||
)
|
||||
|
||||
type MythicRPCCustomBrowserSearchMessage struct {
|
||||
TaskID *int `json:"task_id"`
|
||||
OperationID *int `json:"operation_id"`
|
||||
GetAllMatchingChildren bool `json:"all_matching_children"`
|
||||
SearchCustomBrowser MythicRPCCustomBrowserSearchData `json:"custombrowser"`
|
||||
}
|
||||
@@ -44,36 +43,20 @@ type MythicRPCCustomBrowserSearchDataResponse struct {
|
||||
func init() {
|
||||
RabbitMQConnection.AddRPCQueue(RPCQueueStruct{
|
||||
Exchange: MYTHIC_EXCHANGE,
|
||||
Queue: CUSTOMBROWSER_SEARCH,
|
||||
RoutingKey: CUSTOMBROWSER_SEARCH,
|
||||
Queue: MYTHIC_RPC_CUSTOMBROWSER_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_CUSTOMBROWSER_SEARCH,
|
||||
Handler: processMythicRPCCustomBrowserSearch,
|
||||
Scopes: []string{mythicjwt.SCOPE_BROWSER_READ},
|
||||
})
|
||||
}
|
||||
|
||||
func MythicRPCCustomBrowserSearch(input MythicRPCCustomBrowserSearchMessage) MythicRPCCustomBrowserSearchMessageResponse {
|
||||
func MythicRPCCustomBrowserSearch(input MythicRPCCustomBrowserSearchMessage, authContext RabbitMQAuthContext) MythicRPCCustomBrowserSearchMessageResponse {
|
||||
response := MythicRPCCustomBrowserSearchMessageResponse{
|
||||
Success: false,
|
||||
CustomBrowserEntries: []MythicRPCCustomBrowserSearchDataResponse{},
|
||||
}
|
||||
paramDict := make(map[string]interface{})
|
||||
if input.TaskID != nil {
|
||||
task := databaseStructs.Task{}
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
task.id, task.operation_id
|
||||
FROM task
|
||||
WHERE task.id=$1`, *input.TaskID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
paramDict["operation_id"] = task.OperationID
|
||||
} else if input.OperationID != nil {
|
||||
paramDict["operation_id"] = *input.OperationID
|
||||
} else {
|
||||
logging.LogError(nil, "must provide task_id or operation_id")
|
||||
response.Error = "must provide task_id or operation_id"
|
||||
return response
|
||||
}
|
||||
paramDict["operation_id"] = authContext.OperationID
|
||||
paramDict["tree_type"] = input.SearchCustomBrowser.TreeType
|
||||
searchString := `SELECT
|
||||
mythictree.id, mythictree.task_id, mythictree.timestamp, mythictree.operation_id,
|
||||
@@ -175,5 +158,10 @@ func processMythicRPCCustomBrowserSearch(msg amqp.Delivery) interface{} {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCustomBrowserSearch(incomingMessage)
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCCustomBrowserSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
func MythicRPCDirectFileTokenCreate(input MythicRPCDirectFileTokenCreateMessage) MythicRPCDirectFileTokenCreateMessageResponse {
|
||||
func MythicRPCDirectFileTokenCreate(input MythicRPCDirectFileTokenCreateMessage, authContext RabbitMQAuthContext) MythicRPCDirectFileTokenCreateMessageResponse {
|
||||
response := MythicRPCDirectFileTokenCreateMessageResponse{Success: false}
|
||||
if input.FileUUID == "" {
|
||||
response.Error = "file_uuid is required"
|
||||
@@ -42,34 +42,26 @@ func MythicRPCDirectFileTokenCreate(input MythicRPCDirectFileTokenCreateMessage)
|
||||
}
|
||||
file := databaseStructs.Filemeta{}
|
||||
err := database.DB.Get(&file, `SELECT
|
||||
operator_id, operation_id, eventstepinstance_id, apitokens_id
|
||||
id
|
||||
FROM filemeta
|
||||
WHERE agent_file_id=$1`,
|
||||
input.FileUUID)
|
||||
WHERE agent_file_id=$1 AND operation_id=$2`,
|
||||
input.FileUUID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
user := databaseStructs.Operator{
|
||||
ID: file.OperatorID,
|
||||
ID: authContext.OperatorID,
|
||||
CurrentOperationID: sql.NullInt64{
|
||||
Valid: true,
|
||||
Int64: int64(file.OperationID),
|
||||
Int64: int64(authContext.OperationID),
|
||||
},
|
||||
}
|
||||
eventstepInstanceID := 0
|
||||
if file.EventStepInstanceID.Valid {
|
||||
eventstepInstanceID = int(file.EventStepInstanceID.Int64)
|
||||
}
|
||||
scope := mythicjwt.SCOPE_FILE_READ
|
||||
if input.Action == "upload" {
|
||||
if input.Action == "upload" || input.Action == "both" {
|
||||
scope = mythicjwt.SCOPE_FILE_WRITE
|
||||
}
|
||||
apitokenID := 0
|
||||
if file.APITokensID.Valid {
|
||||
apitokenID = int(file.APITokensID.Int64)
|
||||
}
|
||||
token, _, err := mythicjwt.GenerateScopedJWT(user, []string{scope}, input.FileUUID, directFileScopedTokenTTL, eventstepInstanceID, apitokenID)
|
||||
token, _, err := mythicjwt.GenerateScopedJWT(user, []string{scope}, input.FileUUID, directFileScopedTokenTTL, authContext.EventStepInstanceID, authContext.APITokensID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -82,11 +74,16 @@ func MythicRPCDirectFileTokenCreate(input MythicRPCDirectFileTokenCreateMessage)
|
||||
func processMythicRPCDirectFileTokenCreate(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCDirectFileTokenCreateMessage{}
|
||||
responseMsg := MythicRPCDirectFileTokenCreateMessageResponse{Success: false}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCDirectFileTokenCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCDirectFileTokenCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -20,8 +21,6 @@ type MythicRPCFileCreateMessage struct {
|
||||
TaskID int `json:"task_id"`
|
||||
PayloadUUID string `json:"payload_uuid"`
|
||||
AgentCallbackID string `json:"agent_callback_id"`
|
||||
OperationID int `json:"operation_id"`
|
||||
OperatorID int `json:"operator_id"`
|
||||
FileContents []byte `json:"file_contents"`
|
||||
DeleteAfterFetch bool `json:"delete_after_fetch"`
|
||||
Filename string `json:"filename"`
|
||||
@@ -43,13 +42,14 @@ func init() {
|
||||
Queue: MYTHIC_RPC_FILE_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_FILE_CREATE,
|
||||
Handler: processMythicRPCFileCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_FILE_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_FILE_CREATE
|
||||
//
|
||||
// Creates a FileMeta object for a specific task in Mythic's database and writes contents to disk with a random UUID filename.
|
||||
func MythicRPCFileCreate(input MythicRPCFileCreateMessage) MythicRPCFileCreateMessageResponse {
|
||||
func MythicRPCFileCreate(input MythicRPCFileCreateMessage, authContext RabbitMQAuthContext) MythicRPCFileCreateMessageResponse {
|
||||
response := MythicRPCFileCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -93,18 +93,18 @@ func MythicRPCFileCreate(input MythicRPCFileCreateMessage) MythicRPCFileCreateMe
|
||||
if len(input.Filename) != 0 {
|
||||
fileData.Filename = []byte(input.Filename)
|
||||
}
|
||||
|
||||
fileData.OperationID = authContext.OperationID
|
||||
fileData.OperatorID = authContext.OperatorID
|
||||
if input.TaskID > 0 {
|
||||
task := databaseStructs.Task{}
|
||||
err = database.DB.Get(&task, `SELECT
|
||||
task.operator_id, task.display_id,
|
||||
callback.operation_id "callback.operation_id",
|
||||
task.display_id,
|
||||
callback.host "callback.host"
|
||||
FROM
|
||||
task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
WHERE
|
||||
task.id=$1`, input.TaskID)
|
||||
task.id=$1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to fetch task")
|
||||
response.Error = "Must supply a valid task ID"
|
||||
@@ -117,20 +117,17 @@ func MythicRPCFileCreate(input MythicRPCFileCreateMessage) MythicRPCFileCreateMe
|
||||
}
|
||||
fileData.TaskID.Int64 = int64(input.TaskID)
|
||||
fileData.TaskID.Valid = true
|
||||
fileData.OperationID = task.Callback.OperationID
|
||||
fileData.OperatorID = task.OperatorID
|
||||
if input.Comment == "" {
|
||||
fileData.Comment = fmt.Sprintf("Created from task %d", task.DisplayID)
|
||||
}
|
||||
} else if input.PayloadUUID != "" {
|
||||
payload := databaseStructs.Payload{}
|
||||
err = database.DB.Get(&payload, `SELECT
|
||||
payload.operator_id,
|
||||
payload.operation_id
|
||||
FROM
|
||||
payload
|
||||
WHERE
|
||||
payload.uuid=$1`, input.PayloadUUID)
|
||||
payload.uuid=$1 AND payload.operation_id=$2`, input.PayloadUUID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to fetch payload uuid")
|
||||
response.Error = "Must supply a valid payload UUID"
|
||||
@@ -141,22 +138,18 @@ func MythicRPCFileCreate(input MythicRPCFileCreateMessage) MythicRPCFileCreateMe
|
||||
} else {
|
||||
fileData.Host = strings.ToUpper(input.TargetHostName)
|
||||
}
|
||||
fileData.OperationID = payload.OperationID
|
||||
fileData.OperatorID = payload.OperatorID
|
||||
if input.Comment == "" {
|
||||
fileData.Comment = fmt.Sprintf("Created from payload %s", input.PayloadUUID)
|
||||
}
|
||||
} else if input.AgentCallbackID != "" {
|
||||
callback := databaseStructs.Callback{}
|
||||
err = database.DB.Get(&callback, `SELECT
|
||||
callback.operator_id,
|
||||
callback.operation_id,
|
||||
callback.display_id,
|
||||
callback.host
|
||||
FROM
|
||||
callback
|
||||
WHERE
|
||||
callback.agent_callback_id=$1`, input.AgentCallbackID)
|
||||
callback.agent_callback_id=$1 AND callback.operation_id=$2`, input.AgentCallbackID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to find agent callback id")
|
||||
response.Error = "Must supply a valid agent callback id"
|
||||
@@ -167,52 +160,54 @@ func MythicRPCFileCreate(input MythicRPCFileCreateMessage) MythicRPCFileCreateMe
|
||||
} else {
|
||||
fileData.Host = strings.ToUpper(input.TargetHostName)
|
||||
}
|
||||
fileData.OperationID = callback.OperationID
|
||||
fileData.OperatorID = callback.OperatorID
|
||||
if input.Comment == "" {
|
||||
fileData.Comment = fmt.Sprintf("Created from callback %d", callback.DisplayID)
|
||||
}
|
||||
} else if input.OperationID > 0 {
|
||||
fileData.OperationID = input.OperationID
|
||||
if input.OperatorID > 0 {
|
||||
fileData.OperatorID = input.OperatorID
|
||||
} else {
|
||||
response.Error = "Must supply a valid OperatorID if you're also supplying an OperationID"
|
||||
return response
|
||||
}
|
||||
} else {
|
||||
response.Error = "Must supply a task ID, payload UUID, or agent callback id. Alternatively, you can supply an OperationID and OperatorID"
|
||||
return response
|
||||
}
|
||||
if statement, err := database.DB.PrepareNamed(`INSERT INTO filemeta
|
||||
(filename,total_chunks,chunks_received,chunk_size,path,operation_id,complete,comment,operator_id,delete_after_fetch,md5,sha1,agent_file_id,full_remote_path,task_id,is_screenshot,is_download_from_agent,host,size)
|
||||
VALUES (:filename, :total_chunks, :chunks_received, :chunk_size, :path, :operation_id, :complete, :comment, :operator_id, :delete_after_fetch, :md5, :sha1, :agent_file_id, :full_remote_path, :task_id, :is_screenshot, :is_download_from_agent, :host, :size)
|
||||
RETURNING id`); err != nil {
|
||||
if authContext.APITokensID > 0 {
|
||||
fileData.APITokensID.Valid = true
|
||||
fileData.APITokensID.Int64 = int64(authContext.APITokensID)
|
||||
}
|
||||
if authContext.EventStepInstanceID > 0 {
|
||||
fileData.EventStepInstanceID.Valid = true
|
||||
fileData.EventStepInstanceID.Int64 = int64(authContext.EventStepInstanceID)
|
||||
}
|
||||
statement, err := database.DB.PrepareNamed(`INSERT INTO filemeta
|
||||
(filename,total_chunks,chunks_received,chunk_size,path,operation_id,complete,comment,operator_id,delete_after_fetch,md5,sha1,agent_file_id,full_remote_path,task_id,is_screenshot,is_download_from_agent,host,size,apitokens_id,eventstepinstance_id)
|
||||
VALUES (:filename, :total_chunks, :chunks_received, :chunk_size, :path, :operation_id, :complete, :comment, :operator_id, :delete_after_fetch, :md5, :sha1, :agent_file_id, :full_remote_path, :task_id, :is_screenshot, :is_download_from_agent, :host, :size, :apitokens_id, :eventstepinstance_id)
|
||||
RETURNING id`)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to save file metadata to database")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err = statement.Get(&fileData.ID, fileData); err != nil {
|
||||
}
|
||||
err = statement.Get(&fileData.ID, fileData)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to save file to database")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
logging.LogDebug("creating new file", "filedata", fileData)
|
||||
response.Success = true
|
||||
response.AgentFileId = fileData.AgentFileID
|
||||
go EmitFileLog(fileData.ID)
|
||||
return response
|
||||
}
|
||||
logging.LogDebug("creating new file", "filedata", fileData)
|
||||
response.Success = true
|
||||
response.AgentFileId = fileData.AgentFileID
|
||||
go EmitFileLog(fileData.ID)
|
||||
return response
|
||||
}
|
||||
func processMythicRPCFileCreate(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCFileCreateMessage{}
|
||||
responseMsg := MythicRPCFileCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCFileCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCFileCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -27,50 +28,62 @@ func init() {
|
||||
Queue: MYTHIC_RPC_FILE_GET_CONTENT, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_FILE_GET_CONTENT, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCFileGetContent, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_FILE_READ},
|
||||
})
|
||||
}
|
||||
|
||||
//MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCFileGetContent(input MythicRPCFileGetContentMessage) MythicRPCFileGetContentMessageResponse {
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCFileGetContent(input MythicRPCFileGetContentMessage, authContext RabbitMQAuthContext) MythicRPCFileGetContentMessageResponse {
|
||||
response := MythicRPCFileGetContentMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
file := databaseStructs.Filemeta{
|
||||
AgentFileID: input.AgentFileID,
|
||||
}
|
||||
if err := database.DB.Get(&file, `SELECT id, path FROM filemeta WHERE agent_file_id=$1`, input.AgentFileID); err != nil {
|
||||
err := database.DB.Get(&file, `SELECT
|
||||
id, path
|
||||
FROM filemeta
|
||||
WHERE agent_file_id=$1 AND operation_id=$2`, input.AgentFileID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
|
||||
if diskFile, err := os.OpenFile(file.Path, os.O_RDONLY, 0644); err != nil {
|
||||
diskFile, err := os.OpenFile(file.Path, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if fileInfo, err := diskFile.Stat(); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
fileContents := make([]byte, fileInfo.Size())
|
||||
if _, err := diskFile.Read(fileContents); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Content = fileContents
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
}
|
||||
fileInfo, err := diskFile.Stat()
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
fileContents := make([]byte, fileInfo.Size())
|
||||
_, err = diskFile.Read(fileContents)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Content = fileContents
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCFileGetContent(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCFileGetContentMessage{}
|
||||
responseMsg := MythicRPCFileGetContentMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCFileGetContent(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCFileGetContent(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -16,7 +17,6 @@ type MythicRPCFileSearchMessage struct {
|
||||
TaskID int `json:"task_id"`
|
||||
CallbackID int `json:"callback_id"`
|
||||
Filename string `json:"filename"`
|
||||
OperationId int `json:"operation_id"`
|
||||
LimitByCallback bool `json:"limit_by_callback"`
|
||||
MaxResults int `json:"max_results"`
|
||||
Comment string `json:"comment"`
|
||||
@@ -56,22 +56,22 @@ func init() {
|
||||
Queue: MYTHIC_RPC_FILE_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_FILE_SEARCH,
|
||||
Handler: processMythicRPCFileSearch,
|
||||
Scopes: []string{mythicjwt.SCOPE_FILE_READ},
|
||||
})
|
||||
}
|
||||
|
||||
func MythicRPCFileSearch(input MythicRPCFileSearchMessage) MythicRPCFileSearchMessageResponse {
|
||||
func MythicRPCFileSearch(input MythicRPCFileSearchMessage, authContext RabbitMQAuthContext) MythicRPCFileSearchMessageResponse {
|
||||
response := MythicRPCFileSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
var callbackId int
|
||||
var operationId int
|
||||
callbackId := 0
|
||||
if input.AgentFileID != "" {
|
||||
// the search is for a specific fileID, so just fetch it and return
|
||||
fileMeta := databaseStructs.Filemeta{}
|
||||
err := database.DB.Get(&fileMeta, `SELECT
|
||||
filemeta.*
|
||||
FROM filemeta
|
||||
WHERE filemeta.agent_file_id=$1`, input.AgentFileID)
|
||||
WHERE filemeta.agent_file_id=$1 AND filemeta.operation_id=$2`, input.AgentFileID, authContext.OperationID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
payload := databaseStructs.Payload{}
|
||||
err = database.DB.Get(&payload, `SELECT
|
||||
@@ -81,12 +81,13 @@ func MythicRPCFileSearch(input MythicRPCFileSearchMessage) MythicRPCFileSearchMe
|
||||
filemeta.operation_id "filemeta.operation_id"
|
||||
FROM payload
|
||||
JOIN filemeta ON payload.file_id = filemeta.id
|
||||
WHERE payload.uuid=$1`, input.AgentFileID)
|
||||
WHERE payload.uuid=$1 AND payload.operation_id=$2`, input.AgentFileID, authContext.OperationID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
logging.LogError(err, "Failed to get file data from the database, it's not a payload uuid or file uuid")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err != nil {
|
||||
}
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get file data from database")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -101,33 +102,31 @@ func MythicRPCFileSearch(input MythicRPCFileSearchMessage) MythicRPCFileSearchMe
|
||||
response.Files = append(response.Files, convertFileMetaToFileData(fileMeta))
|
||||
return response
|
||||
|
||||
} else if input.CallbackID > 0 {
|
||||
}
|
||||
if input.CallbackID > 0 {
|
||||
callback := databaseStructs.Callback{ID: input.CallbackID}
|
||||
if err := database.DB.Get(&callback, `SELECT operation_id, id FROM callback WHERE id=$1`, callback.ID); err != nil {
|
||||
err := database.DB.Get(&callback, `
|
||||
SELECT operation_id, id
|
||||
FROM callback
|
||||
WHERE id=$1 AND operation_id=$2`, callback.ID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get operation in MythicRPCFileSearch")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
operationId = callback.OperationID
|
||||
callbackId = input.CallbackID
|
||||
}
|
||||
callbackId = input.CallbackID
|
||||
} else if input.TaskID > 0 {
|
||||
task := databaseStructs.Task{ID: input.TaskID}
|
||||
if err := database.DB.Get(&task, `SELECT operation_id, callback_id FROM task WHERE id=$1`, task.ID); err != nil {
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
operation_id, callback_id
|
||||
FROM task
|
||||
WHERE id=$1 AND operation_id=$2`, task.ID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get task from Mythic")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
operationId = task.OperationID
|
||||
callbackId = task.CallbackID
|
||||
}
|
||||
} else if input.OperationId == 0 {
|
||||
response.Success = false
|
||||
response.Error = "Must supply TaskID, CallbackID or OperationID if not searching for a specific file by AgentFileID"
|
||||
return response
|
||||
} else {
|
||||
operationId = input.OperationId
|
||||
callbackId = 0
|
||||
callbackId = task.CallbackID
|
||||
}
|
||||
files := []databaseStructs.Filemeta{}
|
||||
comment := "%%"
|
||||
@@ -145,7 +144,7 @@ func MythicRPCFileSearch(input MythicRPCFileSearchMessage) MythicRPCFileSearchMe
|
||||
is_screenshot=$5 AND deleted=false and filemeta.operation_id=$6 and filemeta.copy_of_file_id is NULL
|
||||
ORDER BY id DESC`
|
||||
searchParameters := []interface{}{
|
||||
comment, filename, input.IsPayload, input.IsDownloadFromAgent, input.IsScreenshot, operationId,
|
||||
comment, filename, input.IsPayload, input.IsDownloadFromAgent, input.IsScreenshot, authContext.OperationID,
|
||||
}
|
||||
if input.MaxResults > 0 && !input.LimitByCallback {
|
||||
searchString += ` LIMIT $7`
|
||||
@@ -164,7 +163,10 @@ func MythicRPCFileSearch(input MythicRPCFileSearchMessage) MythicRPCFileSearchMe
|
||||
if input.LimitByCallback {
|
||||
if file.TaskID.Valid {
|
||||
var fileCallbackID int
|
||||
if err := database.DB.Get(&fileCallbackID, `SELECT callback_id FROM task WHERE id=$1`, file.TaskID.Int64); err != nil {
|
||||
if err := database.DB.Get(&fileCallbackID, `
|
||||
SELECT callback_id
|
||||
FROM task
|
||||
WHERE id=$1 AND operation_id=$2`, file.TaskID.Int64, authContext.OperationID); err != nil {
|
||||
logging.LogError(err, "Failed to get the task information for callback")
|
||||
} else if fileCallbackID == callbackId {
|
||||
//logging.LogInfo("found matching callback_id", "filename", string(file.Filename), "maxResults", input.MaxResults)
|
||||
@@ -233,11 +235,16 @@ func processMythicRPCFileSearch(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCFileSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCFileSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCFileSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -35,19 +36,23 @@ func init() {
|
||||
Queue: MYTHIC_RPC_FILE_UPDATE, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_FILE_UPDATE, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCFileUpdate, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_FILE_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCFileUpdate(input MythicRPCFileUpdateMessage) MythicRPCFileUpdateMessageResponse {
|
||||
func MythicRPCFileUpdate(input MythicRPCFileUpdateMessage, authContext RabbitMQAuthContext) MythicRPCFileUpdateMessageResponse {
|
||||
response := MythicRPCFileUpdateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
file := databaseStructs.Filemeta{
|
||||
AgentFileID: input.AgentFileID,
|
||||
}
|
||||
if err := database.DB.Get(&file, `SELECT id, "comment", filename, "path", delete_after_fetch
|
||||
FROM filemeta WHERE agent_file_id=$1`, input.AgentFileID); err != nil {
|
||||
err := database.DB.Get(&file, `
|
||||
SELECT id, "comment", filename, "path", delete_after_fetch
|
||||
FROM filemeta
|
||||
WHERE agent_file_id=$1 AND operation_id=$2`, input.AgentFileID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
@@ -61,95 +66,107 @@ func MythicRPCFileUpdate(input MythicRPCFileUpdateMessage) MythicRPCFileUpdateMe
|
||||
file.DeleteAfterFetch = *input.DeleteAfterFetch
|
||||
}
|
||||
if input.AppendContents != nil {
|
||||
if diskFile, err := os.OpenFile(file.Path, os.O_APPEND|os.O_RDWR, 0644); err != nil {
|
||||
diskFile, err := os.OpenFile(file.Path, os.O_APPEND|os.O_RDWR, 0644)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if _, err = diskFile.Write(*input.AppendContents); err != nil {
|
||||
}
|
||||
_, err = diskFile.Write(*input.AppendContents)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err = diskFile.Close(); err != nil {
|
||||
}
|
||||
err = diskFile.Close()
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if diskFile, err = os.OpenFile(file.Path, os.O_RDONLY, 0644); err != nil {
|
||||
}
|
||||
diskFile, err = os.OpenFile(file.Path, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if fileInfo, err := diskFile.Stat(); err != nil {
|
||||
}
|
||||
fileInfo, err := diskFile.Stat()
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
fileContents := make([]byte, fileInfo.Size())
|
||||
if _, err := diskFile.Read(fileContents); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
// calculate new MD5/SHA1 hashes
|
||||
sha1Sum := sha1.Sum(fileContents)
|
||||
file.Sha1 = fmt.Sprintf("%x", sha1Sum)
|
||||
md5Sum := md5.Sum(fileContents)
|
||||
file.Md5 = fmt.Sprintf("%x", md5Sum)
|
||||
if _, err := database.DB.NamedExec(`UPDATE filemeta SET
|
||||
}
|
||||
fileContents := make([]byte, fileInfo.Size())
|
||||
_, err = diskFile.Read(fileContents)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
// calculate new MD5/SHA1 hashes
|
||||
sha1Sum := sha1.Sum(fileContents)
|
||||
file.Sha1 = fmt.Sprintf("%x", sha1Sum)
|
||||
md5Sum := md5.Sum(fileContents)
|
||||
file.Md5 = fmt.Sprintf("%x", md5Sum)
|
||||
_, err = database.DB.NamedExec(`UPDATE filemeta SET
|
||||
"comment"=:comment, filename=:filename, sha1=:sha1, md5=:md5, delete_after_fetch=:delete_after_fetch
|
||||
WHERE id=:id`, file); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
go EmitFileLog(file.ID)
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
WHERE id=:id`, file)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
|
||||
} else if input.ReplaceContents != nil {
|
||||
if diskFile, err := os.OpenFile(file.Path, os.O_TRUNC|os.O_RDWR, 0644); err != nil {
|
||||
response.Success = true
|
||||
go EmitFileLog(file.ID)
|
||||
return response
|
||||
}
|
||||
if input.ReplaceContents != nil {
|
||||
diskFile, err := os.OpenFile(file.Path, os.O_TRUNC|os.O_RDWR, 0644)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if _, err = diskFile.Write(*input.ReplaceContents); err != nil {
|
||||
}
|
||||
_, err = diskFile.Write(*input.ReplaceContents)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err = diskFile.Close(); err != nil {
|
||||
}
|
||||
err = diskFile.Close()
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
// calculate new MD5/SHA1 hashes
|
||||
sha1Sum := sha1.Sum(*input.ReplaceContents)
|
||||
file.Sha1 = fmt.Sprintf("%x", sha1Sum)
|
||||
md5Sum := md5.Sum(*input.ReplaceContents)
|
||||
file.Md5 = fmt.Sprintf("%x", md5Sum)
|
||||
if _, err := database.DB.NamedExec(`UPDATE filemeta SET
|
||||
}
|
||||
// calculate new MD5/SHA1 hashes
|
||||
sha1Sum := sha1.Sum(*input.ReplaceContents)
|
||||
file.Sha1 = fmt.Sprintf("%x", sha1Sum)
|
||||
md5Sum := md5.Sum(*input.ReplaceContents)
|
||||
file.Md5 = fmt.Sprintf("%x", md5Sum)
|
||||
_, err = database.DB.NamedExec(`UPDATE filemeta SET
|
||||
comment=:comment, filename=:filename, sha1=:sha1, md5=:md5, delete_after_fetch=:delete_after_fetch
|
||||
WHERE id=:id`, file); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
}
|
||||
} else if input.Delete {
|
||||
if err := os.Remove(file.Path); err != nil {
|
||||
WHERE id=:id`, file)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
if _, err := database.DB.NamedExec(`UPDATE filemeta SET
|
||||
"comment"=:comment, filename=:filename, deleted=true, delete_after_fetch=:delete_after_fetch
|
||||
WHERE id=:id`, file); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, err := database.DB.NamedExec(`UPDATE filemeta SET
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
if input.Delete {
|
||||
err = os.Remove(file.Path)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
_, err = database.DB.NamedExec(`UPDATE filemeta SET
|
||||
"comment"=:comment, filename=:filename, deleted=true, delete_after_fetch=:delete_after_fetch
|
||||
WHERE id=:id`, file)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
_, err = database.DB.NamedExec(`UPDATE filemeta SET
|
||||
"comment"=:comment, filename=:filename, delete_after_fetch=:delete_after_fetch
|
||||
WHERE id=:id
|
||||
`, file); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
`, file)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
@@ -159,11 +176,16 @@ func processMythicRPCFileUpdate(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCFileUpdateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct", "body", msg.Body)
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCFileUpdate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCFileUpdate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -25,11 +26,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_FILEBROWSER_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_FILEBROWSER_CREATE,
|
||||
Handler: processMythicRPCFileBrowserCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_BROWSER_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_FILEBROWSER_CREATE
|
||||
func MythicRPCFileBrowserCreate(input MythicRPCFileBrowserCreateMessage) MythicRPCFileBrowserCreateMessageResponse {
|
||||
func MythicRPCFileBrowserCreate(input MythicRPCFileBrowserCreateMessage, authContext RabbitMQAuthContext) MythicRPCFileBrowserCreateMessageResponse {
|
||||
response := MythicRPCFileBrowserCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -46,7 +48,7 @@ func MythicRPCFileBrowserCreate(input MythicRPCFileBrowserCreateMessage) MythicR
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
JOIN payload ON callback.registered_payload_id = payload.id
|
||||
WHERE task.id = $1`, input.TaskID)
|
||||
WHERE task.id = $1 AND task.operation_id=$2`, input.TaskID, authContext)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch task")
|
||||
response.Error = err.Error()
|
||||
@@ -78,5 +80,10 @@ func processMythicRPCFileBrowserCreate(msg amqp.Delivery) interface{} {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCFileBrowserCreate(incomingMessage)
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCFileBrowserCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
"github.com/its-a-feature/Mythic/utils"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
@@ -22,6 +23,7 @@ func init() {
|
||||
Queue: MYTHIC_RPC_FILEBROWSER_PARSE_PATH,
|
||||
RoutingKey: MYTHIC_RPC_FILEBROWSER_PARSE_PATH,
|
||||
Handler: processMythicRPCFileBrowserParsePath,
|
||||
Scopes: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,11 +46,11 @@ func processMythicRPCFileBrowserParsePath(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCFileBrowserParsePathMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCFileBrowserParsePath(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
return MythicRPCFileBrowserParsePath(incomingMessage)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -24,16 +26,17 @@ func init() {
|
||||
Queue: MYTHIC_RPC_FILEBROWSER_REMOVE,
|
||||
RoutingKey: MYTHIC_RPC_FILEBROWSER_REMOVE,
|
||||
Handler: processMythicRPCFileBrowserRemove,
|
||||
Scopes: []string{mythicjwt.SCOPE_BROWSER_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_FILEBROWSER_REMOVE
|
||||
func MythicRPCFileBrowserRemove(input MythicRPCFileBrowserRemoveMessage) MythicRPCFileBrowserRemoveMessageResponse {
|
||||
func MythicRPCFileBrowserRemove(input MythicRPCFileBrowserRemoveMessage, authContext RabbitMQAuthContext) MythicRPCFileBrowserRemoveMessageResponse {
|
||||
response := MythicRPCFileBrowserRemoveMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
task.id, task.status, task.completed, task.status_timestamp_processed, task.operator_id, task.operation_id,
|
||||
callback.host "callback.host",
|
||||
callback.user "callback.user",
|
||||
@@ -44,29 +47,36 @@ func MythicRPCFileBrowserRemove(input MythicRPCFileBrowserRemoveMessage) MythicR
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
JOIN payload ON callback.registered_payload_id = payload.id
|
||||
WHERE task.id = $1`, input.TaskID); err != nil {
|
||||
WHERE task.id = $1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch task")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err := handleAgentMessagePostResponseRemovedFiles(task, &input.RemovedFiles); err != nil {
|
||||
}
|
||||
err = handleAgentMessagePostResponseRemovedFiles(task, &input.RemovedFiles)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to create processes in MythicRPCProcessCreate")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCFileBrowserRemove(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCFileBrowserRemoveMessage{}
|
||||
responseMsg := MythicRPCFileBrowserRemoveMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCFileBrowserRemove(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCFileBrowserRemove(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
|
||||
@@ -29,11 +30,17 @@ func init() {
|
||||
Queue: MYTHIC_RPC_HANDLE_AGENT_JSON,
|
||||
RoutingKey: MYTHIC_RPC_HANDLE_AGENT_JSON,
|
||||
Handler: processMythicRPCHandleAgentJson,
|
||||
Scopes: []string{
|
||||
mythicjwt.SCOPE_CALLBACK_WRITE,
|
||||
mythicjwt.SCOPE_RESPONSE_WRITE,
|
||||
mythicjwt.SCOPE_FILE_WRITE,
|
||||
mythicjwt.SCOPE_CREDENTIAL_WRITE,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_HANDLE_AGENT_JSON
|
||||
func MythicRPCHandleAgentJson(input MythicRPCHandleAgentJsonMessage) MythicRPCHandleAgentJsonMessageResponse {
|
||||
func MythicRPCHandleAgentJson(input MythicRPCHandleAgentJsonMessage, authContext RabbitMQAuthContext) MythicRPCHandleAgentJsonMessageResponse {
|
||||
response := MythicRPCHandleAgentJsonMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -42,7 +49,7 @@ func MythicRPCHandleAgentJson(input MythicRPCHandleAgentJsonMessage) MythicRPCHa
|
||||
err := database.DB.Get(&callback, `SELECT
|
||||
operation_id, id, display_id, agent_callback_id
|
||||
FROM callback
|
||||
WHERE id=$1`, *input.CallbackID)
|
||||
WHERE id=$1 AND operation_id=$2`, *input.CallbackID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = err.Error()
|
||||
@@ -52,7 +59,7 @@ func MythicRPCHandleAgentJson(input MythicRPCHandleAgentJsonMessage) MythicRPCHa
|
||||
err := database.DB.Get(&callback, `SELECT
|
||||
operation_id, id, display_id, agent_callback_id
|
||||
FROM callback
|
||||
WHERE agent_callback_id=$1`, *input.AgentCallbackID)
|
||||
WHERE agent_callback_id=$1 AND operation_id=$2`, *input.AgentCallbackID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = err.Error()
|
||||
@@ -96,11 +103,16 @@ func processMythicRPCHandleAgentJson(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCHandleAgentJsonMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCHandleAgentJson(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCHandleAgentJson(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -24,11 +26,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_KEYLOG_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_KEYLOG_CREATE,
|
||||
Handler: processMythicRPCKeylogCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_RESPONSE_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_PROCESS_CREATE
|
||||
func MythicRPCKeylogCreate(input MythicRPCKeylogCreateMessage) MythicRPCKeylogCreateMessageResponse {
|
||||
func MythicRPCKeylogCreate(input MythicRPCKeylogCreateMessage, authContext RabbitMQAuthContext) MythicRPCKeylogCreateMessageResponse {
|
||||
response := MythicRPCKeylogCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -37,8 +40,9 @@ func MythicRPCKeylogCreate(input MythicRPCKeylogCreateMessage) MythicRPCKeylogCr
|
||||
return response
|
||||
}
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
task.id, task.status, task.completed, task.status_timestamp_processed, task.operator_id, task.operation_id,
|
||||
task.apitokens_id, task.eventstepinstance_id,
|
||||
callback.host "callback.host",
|
||||
callback.user "callback.user",
|
||||
callback.id "callback.id",
|
||||
@@ -48,29 +52,36 @@ func MythicRPCKeylogCreate(input MythicRPCKeylogCreateMessage) MythicRPCKeylogCr
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
JOIN payload ON callback.registered_payload_id = payload.id
|
||||
WHERE task.id = $1`, input.TaskID); err != nil {
|
||||
WHERE task.id = $1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch task")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err := handleAgentMessagePostResponseKeylogs(task, &input.Keylogs); err != nil {
|
||||
}
|
||||
err = handleAgentMessagePostResponseKeylogs(task, &input.Keylogs)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to create processes in MythicRPCProcessCreate")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCKeylogCreate(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCKeylogCreateMessage{}
|
||||
responseMsg := MythicRPCKeylogCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCKeylogCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCKeylogCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
@@ -11,7 +13,6 @@ import (
|
||||
)
|
||||
|
||||
type MythicRPCKeylogSearchMessage struct {
|
||||
TaskID int `json:"task_id"` //required
|
||||
SearchKeylogs MythicRPCKeylogSearchKeylogData `json:"keylogs"`
|
||||
}
|
||||
type MythicRPCKeylogSearchMessageResponse struct {
|
||||
@@ -31,69 +32,62 @@ func init() {
|
||||
Queue: MYTHIC_RPC_KEYLOG_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_KEYLOG_SEARCH,
|
||||
Handler: processMythicRPCKeylogSearch,
|
||||
Scopes: []string{mythicjwt.SCOPE_RESPONSE_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_KEYLOG_SEARCH
|
||||
func MythicRPCKeylogSearch(input MythicRPCKeylogSearchMessage) MythicRPCKeylogSearchMessageResponse {
|
||||
func MythicRPCKeylogSearch(input MythicRPCKeylogSearchMessage, authContext RabbitMQAuthContext) MythicRPCKeylogSearchMessageResponse {
|
||||
response := MythicRPCKeylogSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
paramDict := make(map[string]interface{})
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
task.id,
|
||||
callback.operation_id "callback.operation_id",
|
||||
callback.host "callback.host"
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
WHERE task.id=$1`, input.TaskID); err != nil {
|
||||
keylogs := []databaseStructs.Keylog{}
|
||||
paramDict["operation_id"] = authContext.OperationID
|
||||
searchString := `SELECT * FROM keylog WHERE operation_id=:operation_id `
|
||||
if input.SearchKeylogs.User != nil {
|
||||
paramDict["user"] = "%" + *input.SearchKeylogs.User + "%"
|
||||
searchString += "AND user ILIKE :user "
|
||||
}
|
||||
if input.SearchKeylogs.WindowTitle != nil {
|
||||
paramDict["window_title"] = "%" + *input.SearchKeylogs.WindowTitle + "%"
|
||||
searchString += "AND window ILIKE :window_title "
|
||||
}
|
||||
if input.SearchKeylogs.Keystrokes != nil {
|
||||
paramDict["keystrokes"] = "%" + string(*input.SearchKeylogs.Keystrokes) + "%"
|
||||
searchString += "AND keystrokes LIKE :keystrokes "
|
||||
}
|
||||
searchString += " ORDER BY id DESC"
|
||||
err := database.DB.Select(&keylogs, searchString, paramDict)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
keylogs := []databaseStructs.Keylog{}
|
||||
paramDict["operation_id"] = task.Callback.OperationID
|
||||
searchString := `SELECT * FROM keylog WHERE operation_id=:operation_id `
|
||||
if input.SearchKeylogs.User != nil {
|
||||
paramDict["user"] = "%" + *input.SearchKeylogs.User + "%"
|
||||
searchString += "AND user ILIKE :user "
|
||||
}
|
||||
if input.SearchKeylogs.WindowTitle != nil {
|
||||
paramDict["window_title"] = "%" + *input.SearchKeylogs.WindowTitle + "%"
|
||||
searchString += "AND window ILIKE :window_title "
|
||||
}
|
||||
if input.SearchKeylogs.Keystrokes != nil {
|
||||
paramDict["keystrokes"] = "%" + string(*input.SearchKeylogs.Keystrokes) + "%"
|
||||
searchString += "AND keystrokes LIKE :keystrokes "
|
||||
}
|
||||
searchString += " ORDER BY id DESC"
|
||||
if err := database.DB.Select(&keylogs, searchString, paramDict); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
returnedProcesses := []MythicRPCKeylogSearchKeylogData{}
|
||||
if err := mapstructure.Decode(keylogs, &returnedProcesses); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
response.Keylogs = returnedProcesses
|
||||
return response
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
returnedProcesses := []MythicRPCKeylogSearchKeylogData{}
|
||||
err = mapstructure.Decode(keylogs, &returnedProcesses)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
response.Keylogs = returnedProcesses
|
||||
return response
|
||||
}
|
||||
func processMythicRPCKeylogSearch(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCKeylogSearchMessage{}
|
||||
responseMsg := MythicRPCKeylogSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCKeylogSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCKeylogSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,19 +3,13 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
|
||||
type MythicRPCOperationEventLogCreateMessage struct {
|
||||
// three optional ways to specify the operation
|
||||
TaskID *int `json:"task_id"`
|
||||
CallbackID *int `json:"callback_id"`
|
||||
AgentCallbackID *string `json:"callback_agent_id"`
|
||||
OperationID *int `json:"operation_id"`
|
||||
// the data to store
|
||||
Message string `json:"message"`
|
||||
MessageLevel database.MESSAGE_TYPE `json:"level"` //info or warning
|
||||
@@ -32,50 +26,20 @@ func init() {
|
||||
Queue: MYTHIC_RPC_EVENTLOG_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_EVENTLOG_CREATE,
|
||||
Handler: processMythicRPCOperationEventLogCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_EVENTLOG_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_OPERATIONEVENTLOG_CREATE
|
||||
func MythicRPCOperationEventLogCreate(input MythicRPCOperationEventLogCreateMessage) MythicRPCOperationEventLogCreateMessageResponse {
|
||||
func MythicRPCOperationEventLogCreate(input MythicRPCOperationEventLogCreateMessage, authContext RabbitMQAuthContext) MythicRPCOperationEventLogCreateMessageResponse {
|
||||
response := MythicRPCOperationEventLogCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
operationId := 0
|
||||
if input.TaskID != nil {
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT operation_id FROM task WHERE id=$1`, input.TaskID); err != nil {
|
||||
logging.LogError(err, "Failed to find task for creating operation event log")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
operationId = task.OperationID
|
||||
}
|
||||
} else if input.CallbackID != nil {
|
||||
callback := databaseStructs.Callback{}
|
||||
if err := database.DB.Get(&callback, `SELECT operation_id FROM callback WHERE id=$1`, input.CallbackID); err != nil {
|
||||
logging.LogError(err, "Failed to find callback for creating operation event log")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
operationId = callback.OperationID
|
||||
}
|
||||
} else if input.AgentCallbackID != nil {
|
||||
callback := databaseStructs.Callback{}
|
||||
if err := database.DB.Get(&callback, `SELECT operation_id FROM callback WHERE agent_callback_id=$1`, input.AgentCallbackID); err != nil {
|
||||
logging.LogError(err, "Failed to find callback for creating operation event log")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
operationId = callback.OperationID
|
||||
}
|
||||
} else if input.OperationID != nil {
|
||||
operationId = *input.OperationID
|
||||
}
|
||||
if input.MessageLevel == "warning" {
|
||||
input.MessageLevel = database.MESSAGE_LEVEL_INFO
|
||||
input.Warning = true
|
||||
}
|
||||
SendAllOperationsMessage(input.Message, operationId, "", input.MessageLevel, input.Warning)
|
||||
SendAllOperationsMessageWithAuth(input.Message, authContext.OperationID, "", input.MessageLevel, input.Warning, authContext)
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
@@ -84,11 +48,16 @@ func processMythicRPCOperationEventLogCreate(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCOperationEventLogCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCOperationEventLogCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCOperationEventLogCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -25,32 +27,33 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PAYLOAD_ADD_COMMAND,
|
||||
RoutingKey: MYTHIC_RPC_PAYLOAD_ADD_COMMAND,
|
||||
Handler: processMythicRPCPayloadAddCommand,
|
||||
Scopes: []string{mythicjwt.SCOPE_PAYLOAD_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_PAYLOAD_ADD_COMMAND
|
||||
func MythicRPCPayloadAddCommand(input MythicRPCPayloadAddCommandMessage) MythicRPCPayloadAddCommandMessageResponse {
|
||||
func MythicRPCPayloadAddCommand(input MythicRPCPayloadAddCommandMessage, authContext RabbitMQAuthContext) MythicRPCPayloadAddCommandMessageResponse {
|
||||
response := MythicRPCPayloadAddCommandMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT payload.id, payload_type_id
|
||||
err := database.DB.Get(&payload, `SELECT
|
||||
payload.id, payload_type_id
|
||||
FROM payload
|
||||
WHERE uuid=$1`, input.PayloadUUID); err != nil {
|
||||
WHERE uuid=$1 AND operation_id=$2`, input.PayloadUUID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch payload in MythicRPCPayloadAddCommand")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
if err := PayloadAddCommand(payload.ID, payload.PayloadTypeID, input.Commands); err != nil {
|
||||
logging.LogError(err, "Failed to add commands to callback")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
err = PayloadAddCommand(payload.ID, payload.PayloadTypeID, input.Commands)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to add commands to callback")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func PayloadAddCommand(PayloadID int, payloadtypeID int, commands []string) error {
|
||||
for _, command := range commands {
|
||||
@@ -97,11 +100,16 @@ func processMythicRPCPayloadAddCommand(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCPayloadAddCommandMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCPayloadAddCommand(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCPayloadAddCommand(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -29,27 +30,31 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PAYLOAD_CREATE_FROM_SCRATCH, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_PAYLOAD_CREATE_FROM_SCRATCH, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCPayloadCreateFromScratch, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_PAYLOAD_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCPayloadCreateFromScratch(input MythicRPCPayloadCreateFromScratchMessage) MythicRPCPayloadCreateFromScratchMessageResponse {
|
||||
func MythicRPCPayloadCreateFromScratch(input MythicRPCPayloadCreateFromScratchMessage, authContext RabbitMQAuthContext) MythicRPCPayloadCreateFromScratchMessageResponse {
|
||||
response := MythicRPCPayloadCreateFromScratchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
operator_id, operation_id
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
operator_id, operation_id, apitokens_id, eventstepinstance_id
|
||||
FROM task
|
||||
WHERE id=$1`, input.TaskID); err != nil {
|
||||
WHERE id=$1 AND operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get operator_id from task when generating payload")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
input.PayloadConfiguration.APITokensID = authContext.APITokensID
|
||||
input.PayloadConfiguration.EventStepInstance = authContext.EventStepInstanceID
|
||||
newUUID, newID, err := RegisterNewPayload(input.PayloadConfiguration, &databaseStructs.Operatoroperation{
|
||||
CurrentOperation: databaseStructs.Operation{ID: task.OperationID},
|
||||
CurrentOperator: databaseStructs.Operator{ID: task.OperatorID},
|
||||
})
|
||||
}, authContext)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -78,11 +83,16 @@ func processMythicRPCPayloadCreateFromScratch(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCPayloadCreateFromScratchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCPayloadCreateFromScratch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCPayloadCreateFromScratch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
mythicCrypto "github.com/its-a-feature/Mythic/crypto"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
@@ -38,18 +39,19 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PAYLOAD_CREATE_FROM_UUID, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_PAYLOAD_CREATE_FROM_UUID, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCPayloadCreateFromUUID, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_PAYLOAD_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCPayloadCreateFromUUID(input MythicRPCPayloadCreateFromUUIDMessage) MythicRPCPayloadCreateFromUUIDMessageResponse {
|
||||
func MythicRPCPayloadCreateFromUUID(input MythicRPCPayloadCreateFromUUIDMessage, authContext RabbitMQAuthContext) MythicRPCPayloadCreateFromUUIDMessageResponse {
|
||||
response := MythicRPCPayloadCreateFromUUIDMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
// first get all of the payload information
|
||||
payloadConfiguration := PayloadConfiguration{}
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT
|
||||
err := database.DB.Get(&payload, `SELECT
|
||||
payload.id, payload.description, payload.uuid, payload.os, payload.wrapped_payload_id, payload.operation_id,
|
||||
payloadtype.name "payloadtype.name",
|
||||
filemeta.filename "filemeta.filename"
|
||||
@@ -58,80 +60,96 @@ func MythicRPCPayloadCreateFromUUID(input MythicRPCPayloadCreateFromUUIDMessage)
|
||||
JOIN payloadtype ON payload.payload_type_id = payloadtype.id
|
||||
JOIN filemeta ON payload.file_id = filemeta.id
|
||||
WHERE
|
||||
payload.uuid=$1`, input.PayloadUUID); err != nil {
|
||||
payload.uuid=$1 AND payload.operation_id=$2`, input.PayloadUUID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get payload")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
if input.NewDescription != nil {
|
||||
payloadConfiguration.Description = *input.NewDescription
|
||||
} else {
|
||||
if input.NewDescription != nil {
|
||||
payloadConfiguration.Description = *input.NewDescription
|
||||
} else {
|
||||
payloadConfiguration.Description = payload.Description
|
||||
}
|
||||
if input.NewFilename != nil {
|
||||
payloadConfiguration.Filename = *input.NewFilename
|
||||
} else {
|
||||
payloadConfiguration.Filename = string(payload.Filemeta.Filename)
|
||||
}
|
||||
payloadConfiguration.SelectedOS = payload.Os
|
||||
payloadConfiguration.PayloadType = payload.Payloadtype.Name
|
||||
payloadConfiguration.C2Profiles = GetPayloadC2ProfileInformation(payload)
|
||||
payloadConfiguration.BuildParameters = GetBuildParameterInformation(payload.ID)
|
||||
payloadConfiguration.Commands = GetPayloadCommandInformation(payload)
|
||||
if payload.WrappedPayloadID.Valid {
|
||||
// get the associated UUID for the wrapped payload
|
||||
wrappedPayload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&wrappedPayload, `SELECT uuid FROM payload WHERE id=$1`, payload.WrappedPayloadID.Int64); err != nil {
|
||||
logging.LogError(err, "Failed to fetch wrapped payload information")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
payloadConfiguration.WrappedPayloadUUID = wrappedPayload.UuID
|
||||
}
|
||||
}
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT operator_id FROM task WHERE id=$1`, input.TaskID); err != nil {
|
||||
logging.LogError(err, "Failed to get operator_id from task when generating payload")
|
||||
payloadConfiguration.Description = payload.Description
|
||||
}
|
||||
if input.NewFilename != nil {
|
||||
payloadConfiguration.Filename = *input.NewFilename
|
||||
} else {
|
||||
payloadConfiguration.Filename = string(payload.Filemeta.Filename)
|
||||
}
|
||||
payloadConfiguration.SelectedOS = payload.Os
|
||||
payloadConfiguration.PayloadType = payload.Payloadtype.Name
|
||||
payloadConfiguration.C2Profiles = GetPayloadC2ProfileInformation(payload)
|
||||
payloadConfiguration.BuildParameters = GetBuildParameterInformation(payload.ID)
|
||||
payloadConfiguration.Commands = GetPayloadCommandInformation(payload)
|
||||
if payload.WrappedPayloadID.Valid {
|
||||
// get the associated UUID for the wrapped payload
|
||||
wrappedPayload := databaseStructs.Payload{}
|
||||
err = database.DB.Get(&wrappedPayload, `SELECT
|
||||
uuid
|
||||
FROM payload
|
||||
WHERE id=$1 AND operation_id=$2`, payload.WrappedPayloadID.Int64, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch wrapped payload information")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
if newUUID, newID, err := RegisterNewPayload(payloadConfiguration, &databaseStructs.Operatoroperation{
|
||||
CurrentOperation: databaseStructs.Operation{ID: payload.OperationID},
|
||||
CurrentOperator: databaseStructs.Operator{ID: task.OperatorID},
|
||||
}); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if _, err := database.DB.Exec(`UPDATE payload SET auto_generated=true, task_id=$2 WHERE id=$1`, newID, input.TaskID); err != nil {
|
||||
logging.LogError(err, "failed to update payload auto_generated status")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
if input.RemoteHost != nil {
|
||||
if _, err := database.DB.Exec(`INSERT INTO payloadonhost
|
||||
payloadConfiguration.WrappedPayloadUUID = wrappedPayload.UuID
|
||||
}
|
||||
task := databaseStructs.Task{}
|
||||
err = database.DB.Get(&task, `SELECT
|
||||
operator_id, apitokens_id, eventstepinstance_id
|
||||
FROM task
|
||||
WHERE id=$1 AND operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get operator_id from task when generating payload")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
payloadConfiguration.APITokensID = authContext.APITokensID
|
||||
payloadConfiguration.EventStepInstance = authContext.EventStepInstanceID
|
||||
newUUID, newID, err := RegisterNewPayload(payloadConfiguration, &databaseStructs.Operatoroperation{
|
||||
CurrentOperation: databaseStructs.Operation{ID: payload.OperationID},
|
||||
CurrentOperator: databaseStructs.Operator{ID: task.OperatorID},
|
||||
}, authContext)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
_, err = database.DB.Exec(`UPDATE payload SET auto_generated=true, task_id=$2 WHERE id=$1`, newID, input.TaskID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to update payload auto_generated status")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
if input.RemoteHost != nil {
|
||||
if _, err := database.DB.Exec(`INSERT INTO payloadonhost
|
||||
(host, payload_id, operation_id, task_id)
|
||||
VALUES
|
||||
($1, $2, $3, $4)`, strings.ToUpper(*input.RemoteHost), newID, payload.OperationID, input.TaskID); err != nil {
|
||||
logging.LogError(err, "Failed to register payload on host")
|
||||
}
|
||||
}
|
||||
response.NewPayloadUUID = newUUID
|
||||
response.Success = true
|
||||
return response
|
||||
logging.LogError(err, "Failed to register payload on host")
|
||||
}
|
||||
}
|
||||
response.NewPayloadUUID = newUUID
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCPayloadCreateFromUUID(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCPayloadCreateFromUUIDMessage{}
|
||||
responseMsg := MythicRPCPayloadCreateFromUUIDMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCPayloadCreateFromUUID(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCPayloadCreateFromUUID(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
func associateBuildParametersWithPayload(databasePayload databaseStructs.Payload, buildParameters *[]PayloadConfigurationBuildParameter) (map[string]interface{}, error) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -27,50 +28,60 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PAYLOAD_GET_PAYLOAD_CONTENT, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_PAYLOAD_GET_PAYLOAD_CONTENT, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCPayloadGetContent, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_PAYLOAD_READ},
|
||||
})
|
||||
}
|
||||
|
||||
//MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCPayloadGetContent(input MythicRPCPayloadGetContentMessage) MythicRPCPayloadGetContentMessageResponse {
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCPayloadGetContent(input MythicRPCPayloadGetContentMessage, authContext RabbitMQAuthContext) MythicRPCPayloadGetContentMessageResponse {
|
||||
response := MythicRPCPayloadGetContentMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT
|
||||
err := database.DB.Get(&payload, `SELECT
|
||||
filemeta.path "filemeta.path"
|
||||
FROM payload
|
||||
JOIN filemeta ON payload.file_id = filemeta.id
|
||||
WHERE payload.uuid = $1`, input.PayloadUUID); err != nil {
|
||||
WHERE payload.uuid = $1 AND payload.operation_id=$2`, input.PayloadUUID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if diskFile, err := os.OpenFile(payload.Filemeta.Path, os.O_RDONLY, 0644); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if fileInfo, err := diskFile.Stat(); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
fileContents := make([]byte, fileInfo.Size())
|
||||
if _, err := diskFile.Read(fileContents); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Content = fileContents
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
}
|
||||
diskFile, err := os.OpenFile(payload.Filemeta.Path, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
fileInfo, err := diskFile.Stat()
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
fileContents := make([]byte, fileInfo.Size())
|
||||
_, err = diskFile.Read(fileContents)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Content = fileContents
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCPayloadGetContent(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCPayloadGetContentMessage{}
|
||||
responseMsg := MythicRPCPayloadGetContentMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCPayloadGetContent(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCPayloadGetContent(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -25,31 +26,33 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PAYLOAD_REMOVE_COMMAND,
|
||||
RoutingKey: MYTHIC_RPC_PAYLOAD_REMOVE_COMMAND,
|
||||
Handler: processMythicRPCPayloadRemoveCommand,
|
||||
Scopes: []string{mythicjwt.SCOPE_PAYLOAD_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_PAYLOAD_REMOVE_COMMAND
|
||||
func MythicRPCPayloadRemoveCommand(input MythicRPCPayloadRemoveCommandMessage) MythicRPCPayloadRemoveCommandMessageResponse {
|
||||
func MythicRPCPayloadRemoveCommand(input MythicRPCPayloadRemoveCommandMessage, authContext RabbitMQAuthContext) MythicRPCPayloadRemoveCommandMessageResponse {
|
||||
response := MythicRPCPayloadRemoveCommandMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT payload.id, payload.payload_type_id
|
||||
err := database.DB.Get(&payload, `SELECT
|
||||
payload.id, payload.payload_type_id
|
||||
FROM payload
|
||||
WHERE uuid=$1`, input.PayloadUUID); err != nil {
|
||||
WHERE uuid=$1 AND operation_id=$2`, input.PayloadUUID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch callback in MythicRPCPayloadRemoveCommand")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
if err := PayloadRemoveCommand(payload.ID, payload.PayloadTypeID, input.Commands); err != nil {
|
||||
logging.LogError(err, "Failed to remove commands in payload")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
}
|
||||
err = PayloadRemoveCommand(payload.ID, payload.PayloadTypeID, input.Commands)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to remove commands in payload")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func PayloadRemoveCommand(PayloadID int, payloadtypeID int, commands []string) error {
|
||||
for _, command := range commands {
|
||||
@@ -88,11 +91,16 @@ func processMythicRPCPayloadRemoveCommand(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCPayloadRemoveCommandMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCPayloadRemoveCommand(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCPayloadRemoveCommand(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
type MythicRPCPayloadSearchMessage struct {
|
||||
CallbackID int `json:"callback_id"`
|
||||
PayloadUUID string `json:"uuid"`
|
||||
Description string `json:"description"`
|
||||
Filename string `json:"filename"`
|
||||
@@ -38,51 +38,31 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PAYLOAD_SEARCH, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_PAYLOAD_SEARCH, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCPayloadSearch, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_PAYLOAD_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCPayloadSearch(input MythicRPCPayloadSearchMessage) MythicRPCPayloadSearchMessageResponse {
|
||||
func MythicRPCPayloadSearch(input MythicRPCPayloadSearchMessage, authContext RabbitMQAuthContext) MythicRPCPayloadSearchMessageResponse {
|
||||
response := MythicRPCPayloadSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
searching := true
|
||||
if input.CallbackID == 0 && input.Description == "" && input.Filename == "" && len(input.PayloadTypes) == 0 && len(input.BuildParameters) == 0 {
|
||||
if input.Description == "" && input.Filename == "" && len(input.PayloadTypes) == 0 && len(input.BuildParameters) == 0 {
|
||||
searching = false
|
||||
}
|
||||
if input.PayloadUUID != "" && !searching {
|
||||
if config, err := getPayloadConfigFromUUID(input.PayloadUUID); err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.PayloadConfigurations = append(response.PayloadConfigurations, config)
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
}
|
||||
// search payloads based on the supplied information
|
||||
operationId := 0
|
||||
payloads := []databaseStructs.Payload{}
|
||||
if input.CallbackID > 0 {
|
||||
callback := databaseStructs.Callback{}
|
||||
err := database.DB.Get(&callback, `SELECT operation_id FROM callback WHERE id=$1`, input.CallbackID)
|
||||
config, err := getPayloadConfigFromUUID(input.PayloadUUID, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
operationId = callback.OperationID
|
||||
} else if input.PayloadUUID != "" {
|
||||
payload := databaseStructs.Payload{}
|
||||
err := database.DB.Get(&payload, `SELECT operation_id FROM payload WHERE uuid=$1`, input.PayloadUUID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
operationId = payload.OperationID
|
||||
} else {
|
||||
response.Error = "Must supply PayloadUUID or CallbackID for searching"
|
||||
response.PayloadConfigurations = append(response.PayloadConfigurations, config)
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
// search payloads based on the supplied information
|
||||
payloads := []databaseStructs.Payload{}
|
||||
|
||||
err := database.DB.Select(&payloads, `SELECT
|
||||
payload.uuid, payload.auto_generated, payload.id,
|
||||
@@ -92,7 +72,7 @@ func MythicRPCPayloadSearch(input MythicRPCPayloadSearchMessage) MythicRPCPayloa
|
||||
JOIN payloadtype ON payload.payload_type_id = payloadtype.id
|
||||
WHERE payload.operation_id=$1 AND payload.deleted=false AND payload.build_phase='success'
|
||||
ORDER BY payload.id DESC
|
||||
`, operationId)
|
||||
`, authContext.OperationID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -101,9 +81,11 @@ func MythicRPCPayloadSearch(input MythicRPCPayloadSearchMessage) MythicRPCPayloa
|
||||
for _, payload := range payloads {
|
||||
if payload.AutoGenerated && !input.IncludeAutoGeneratedPayloads {
|
||||
continue
|
||||
} else if len(input.PayloadTypes) > 0 && !utils.SliceContains(input.PayloadTypes, payload.Payloadtype.Name) {
|
||||
}
|
||||
if len(input.PayloadTypes) > 0 && !utils.SliceContains(input.PayloadTypes, payload.Payloadtype.Name) {
|
||||
continue
|
||||
} else if len(input.BuildParameters) > 0 {
|
||||
}
|
||||
if len(input.BuildParameters) > 0 {
|
||||
allBuildParametersAreGood := true
|
||||
for _, buildRequirement := range input.BuildParameters {
|
||||
if buildRequirement.PayloadType == payload.Payloadtype.Name {
|
||||
@@ -128,21 +110,21 @@ func MythicRPCPayloadSearch(input MythicRPCPayloadSearchMessage) MythicRPCPayloa
|
||||
}
|
||||
}
|
||||
if allBuildParametersAreGood {
|
||||
if finalPayload, err := getPayloadConfigFromUUID(payload.UuID); err != nil {
|
||||
finalPayload, err := getPayloadConfigFromUUID(payload.UuID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get configuration for payload")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
if input.Description != "" && finalPayload.Description != input.Description {
|
||||
continue
|
||||
}
|
||||
if input.Filename != "" && finalPayload.Filename != input.Filename {
|
||||
continue
|
||||
}
|
||||
finalPayloads = append(finalPayloads, finalPayload)
|
||||
}
|
||||
if input.Description != "" && finalPayload.Description != input.Description {
|
||||
continue
|
||||
}
|
||||
if input.Filename != "" && finalPayload.Filename != input.Filename {
|
||||
continue
|
||||
}
|
||||
finalPayloads = append(finalPayloads, finalPayload)
|
||||
}
|
||||
} else if finalPayload, err := getPayloadConfigFromUUID(payload.UuID); err != nil {
|
||||
} else if finalPayload, err := getPayloadConfigFromUUID(payload.UuID, authContext.OperationID); err != nil {
|
||||
logging.LogError(err, "Failed to get configuration for payload")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
@@ -157,7 +139,6 @@ func MythicRPCPayloadSearch(input MythicRPCPayloadSearchMessage) MythicRPCPayloa
|
||||
}
|
||||
}
|
||||
response.PayloadConfigurations = finalPayloads
|
||||
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
@@ -166,19 +147,24 @@ func processMythicRPCPayloadSearch(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCPayloadSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCPayloadSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCPayloadSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
func getPayloadConfigFromUUID(payloadUUID string) (PayloadConfiguration, error) {
|
||||
func getPayloadConfigFromUUID(payloadUUID string, operationID int) (PayloadConfiguration, error) {
|
||||
payloadConfiguration := PayloadConfiguration{}
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT
|
||||
err := database.DB.Get(&payload, `SELECT
|
||||
payload.id, payload.description, payload.uuid, payload.os, payload.wrapped_payload_id, payload.build_phase,
|
||||
payloadtype.name "payloadtype.name",
|
||||
filemeta.filename "filemeta.filename",
|
||||
@@ -188,29 +174,33 @@ func getPayloadConfigFromUUID(payloadUUID string) (PayloadConfiguration, error)
|
||||
JOIN payloadtype ON payload.payload_type_id = payloadtype.id
|
||||
JOIN filemeta ON payload.file_id = filemeta.id
|
||||
WHERE
|
||||
payload.uuid=$1`, payloadUUID); err != nil {
|
||||
payload.uuid=$1 AND payload.operation_id=$2`, payloadUUID, operationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get payload when searching for payloads")
|
||||
return payloadConfiguration, err
|
||||
} else {
|
||||
payloadConfiguration.Description = payload.Description
|
||||
payloadConfiguration.SelectedOS = payload.Os
|
||||
payloadConfiguration.PayloadType = payload.Payloadtype.Name
|
||||
payloadConfiguration.C2Profiles = GetPayloadC2ProfileInformation(payload)
|
||||
payloadConfiguration.BuildParameters = GetBuildParameterInformation(payload.ID)
|
||||
payloadConfiguration.Commands = GetPayloadCommandInformation(payload)
|
||||
payloadConfiguration.Filename = string(payload.Filemeta.Filename)
|
||||
payloadConfiguration.AgentFileID = payload.Filemeta.AgentFileID
|
||||
payloadConfiguration.UUID = payload.UuID
|
||||
payloadConfiguration.BuildPhase = payload.BuildPhase
|
||||
if payload.WrappedPayloadID.Valid {
|
||||
// get the associated UUID for the wrapped payload
|
||||
wrappedPayload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&wrappedPayload, `SELECT uuid FROM payload WHERE id=$1`, payload.WrappedPayloadID.Int64); err != nil {
|
||||
logging.LogError(err, "Failed to fetch wrapped payload information")
|
||||
} else {
|
||||
payloadConfiguration.WrappedPayloadUUID = wrappedPayload.UuID
|
||||
}
|
||||
}
|
||||
return payloadConfiguration, nil
|
||||
}
|
||||
payloadConfiguration.Description = payload.Description
|
||||
payloadConfiguration.SelectedOS = payload.Os
|
||||
payloadConfiguration.PayloadType = payload.Payloadtype.Name
|
||||
payloadConfiguration.C2Profiles = GetPayloadC2ProfileInformation(payload)
|
||||
payloadConfiguration.BuildParameters = GetBuildParameterInformation(payload.ID)
|
||||
payloadConfiguration.Commands = GetPayloadCommandInformation(payload)
|
||||
payloadConfiguration.Filename = string(payload.Filemeta.Filename)
|
||||
payloadConfiguration.AgentFileID = payload.Filemeta.AgentFileID
|
||||
payloadConfiguration.UUID = payload.UuID
|
||||
payloadConfiguration.BuildPhase = payload.BuildPhase
|
||||
if payload.WrappedPayloadID.Valid {
|
||||
// get the associated UUID for the wrapped payload
|
||||
wrappedPayload := databaseStructs.Payload{}
|
||||
err = database.DB.Get(&wrappedPayload, `SELECT
|
||||
uuid
|
||||
FROM payload
|
||||
WHERE id=$1 AND operation_id=$2`, payload.WrappedPayloadID.Int64, operationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch wrapped payload information")
|
||||
} else {
|
||||
payloadConfiguration.WrappedPayloadUUID = wrappedPayload.UuID
|
||||
}
|
||||
}
|
||||
return payloadConfiguration, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -31,104 +32,107 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PAYLOAD_UPDATE_BUILD_STEP, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_PAYLOAD_UPDATE_BUILD_STEP, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCPayloadUpdateBuildStep, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_PAYLOAD_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCPayloadUpdateBuildStep(input MythicRPCPayloadUpdateBuildStepMessage) MythicRPCPayloadUpdateBuildStepMessageResponse {
|
||||
func MythicRPCPayloadUpdateBuildStep(input MythicRPCPayloadUpdateBuildStepMessage, authContext RabbitMQAuthContext) MythicRPCPayloadUpdateBuildStepMessageResponse {
|
||||
response := MythicRPCPayloadUpdateBuildStepMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT
|
||||
err := database.DB.Get(&payload, `SELECT
|
||||
id
|
||||
FROM payload
|
||||
WHERE uuid=$1`, input.PayloadUUID); err != nil {
|
||||
WHERE uuid=$1 AND operation_id=$2`, input.PayloadUUID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find payload")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
// we need to set the endtime for the specified step to now() and also the start time for the next step to now()
|
||||
stepNow := time.Now().UTC()
|
||||
allBuildSteps := []databaseStructs.PayloadBuildStep{}
|
||||
if err := database.DB.Select(&allBuildSteps, `SELECT
|
||||
}
|
||||
// we need to set the endtime for the specified step to now() and also the start time for the next step to now()
|
||||
stepNow := time.Now().UTC()
|
||||
allBuildSteps := []databaseStructs.PayloadBuildStep{}
|
||||
err = database.DB.Select(&allBuildSteps, `SELECT
|
||||
id, step_number, step_name, start_time, end_time
|
||||
FROM payload_build_step
|
||||
WHERE payload_id=$1 ORDER BY step_number ASC`, payload.ID); err != nil {
|
||||
logging.LogError(err, "Failed to fetch current build steps for payload")
|
||||
} else {
|
||||
setStartTimeOfNextStep := false
|
||||
var prevSteps []databaseStructs.PayloadBuildStep
|
||||
for _, step := range allBuildSteps {
|
||||
if setStartTimeOfNextStep {
|
||||
if !step.StartTime.Valid {
|
||||
setStartTimeOfNextStep = false
|
||||
step.StartTime.Time = stepNow
|
||||
step.StartTime.Valid = true
|
||||
if _, err := database.DB.NamedExec(`UPDATE payload_build_step SET
|
||||
WHERE payload_id=$1 ORDER BY step_number ASC`, payload.ID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch current build steps for payload")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
setStartTimeOfNextStep := false
|
||||
var prevSteps []databaseStructs.PayloadBuildStep
|
||||
for _, step := range allBuildSteps {
|
||||
if setStartTimeOfNextStep {
|
||||
if !step.StartTime.Valid {
|
||||
setStartTimeOfNextStep = false
|
||||
step.StartTime.Time = stepNow
|
||||
step.StartTime.Valid = true
|
||||
_, err = database.DB.NamedExec(`UPDATE payload_build_step SET
|
||||
start_time=:start_time
|
||||
WHERE id=:id`, step); err != nil {
|
||||
logging.LogError(err, "Failed to update payload build step")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
// break out early here so that we don't keep adding steps as "previous"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if step.StepName == input.StepName {
|
||||
setStartTimeOfNextStep = true
|
||||
step.StepStdout = input.StepStdout
|
||||
step.StepStderr = input.StepStderr
|
||||
step.Success = input.StepSuccess
|
||||
step.StepSkip = input.StepSkip
|
||||
step.EndTime.Valid = true
|
||||
step.EndTime.Time = stepNow
|
||||
if !step.StartTime.Valid {
|
||||
step.StartTime.Valid = true
|
||||
step.StartTime.Time = stepNow
|
||||
}
|
||||
if _, err := database.DB.NamedExec(`UPDATE payload_build_step SET
|
||||
step_stdout=:step_stdout, step_stderr=:step_stderr,
|
||||
step_success=:step_success, end_time=:end_time, start_time=:start_time, step_skip=:step_skip
|
||||
WHERE id=:id`, step); err != nil {
|
||||
logging.LogError(err, "Failed to update payload build step")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
}
|
||||
if !step.EndTime.Valid {
|
||||
prevSteps = append(prevSteps, step)
|
||||
}
|
||||
|
||||
}
|
||||
for _, step := range prevSteps {
|
||||
// loop through all the steps that weren't marked as done before this one and update them
|
||||
if !step.StartTime.Valid {
|
||||
step.StartTime.Valid = true
|
||||
step.StartTime.Time = stepNow
|
||||
}
|
||||
if !step.EndTime.Valid {
|
||||
step.EndTime.Valid = true
|
||||
step.EndTime.Time = stepNow
|
||||
}
|
||||
step.StepSkip = true
|
||||
step.Success = input.StepSuccess
|
||||
step.StepStdout = step.StepStdout + "\nAutomatically marked as done due to future step completing"
|
||||
if _, err := database.DB.NamedExec(`UPDATE payload_build_step SET
|
||||
step_stdout=:step_stdout, step_success=:step_success, end_time=:end_time, start_time=:start_time,
|
||||
step_skip=:step_skip
|
||||
WHERE id=:id`, step); err != nil {
|
||||
WHERE id=:id`, step)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to update payload build step")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
// break out early here so that we don't keep adding steps as "previous"
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
if step.StepName == input.StepName {
|
||||
setStartTimeOfNextStep = true
|
||||
step.StepStdout = input.StepStdout
|
||||
step.StepStderr = input.StepStderr
|
||||
step.Success = input.StepSuccess
|
||||
step.StepSkip = input.StepSkip
|
||||
step.EndTime.Valid = true
|
||||
step.EndTime.Time = stepNow
|
||||
if !step.StartTime.Valid {
|
||||
step.StartTime.Valid = true
|
||||
step.StartTime.Time = stepNow
|
||||
}
|
||||
_, err = database.DB.NamedExec(`UPDATE payload_build_step SET
|
||||
step_stdout=:step_stdout, step_stderr=:step_stderr,
|
||||
step_success=:step_success, end_time=:end_time, start_time=:start_time, step_skip=:step_skip
|
||||
WHERE id=:id`, step)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to update payload build step")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
}
|
||||
if !step.EndTime.Valid {
|
||||
prevSteps = append(prevSteps, step)
|
||||
}
|
||||
|
||||
}
|
||||
for _, step := range prevSteps {
|
||||
// loop through all the steps that weren't marked as done before this one and update them
|
||||
if !step.StartTime.Valid {
|
||||
step.StartTime.Valid = true
|
||||
step.StartTime.Time = stepNow
|
||||
}
|
||||
if !step.EndTime.Valid {
|
||||
step.EndTime.Valid = true
|
||||
step.EndTime.Time = stepNow
|
||||
}
|
||||
step.StepSkip = true
|
||||
step.Success = input.StepSuccess
|
||||
step.StepStdout = step.StepStdout + "\nAutomatically marked as done due to future step completing"
|
||||
_, err = database.DB.NamedExec(`UPDATE payload_build_step SET
|
||||
step_stdout=:step_stdout, step_success=:step_success, end_time=:end_time, start_time=:start_time,
|
||||
step_skip=:step_skip
|
||||
WHERE id=:id`, step)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to update payload build step")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
func processMythicRPCPayloadUpdateBuildStep(msg amqp.Delivery) interface{} {
|
||||
@@ -136,11 +140,16 @@ func processMythicRPCPayloadUpdateBuildStep(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCPayloadUpdateBuildStepMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCPayloadUpdateBuildStep(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCPayloadUpdateBuildStep(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MythicRPCPayloadOnHostCreateMessage struct {
|
||||
@@ -30,70 +32,91 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PAYLOADONHOST_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_PAYLOADONHOST_CREATE,
|
||||
Handler: processMythicRPCPayloadOnHostCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_PAYLOAD_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_PAYLOADONHOST_CREATE
|
||||
func MythicRPCPayloadOnHostCreate(input MythicRPCPayloadOnHostCreateMessage) MythicRPCPayloadOnHostCreateMessageResponse {
|
||||
func MythicRPCPayloadOnHostCreate(input MythicRPCPayloadOnHostCreateMessage, authContext RabbitMQAuthContext) MythicRPCPayloadOnHostCreateMessageResponse {
|
||||
response := MythicRPCPayloadOnHostCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
callback.operation_id "callback.operation_id",
|
||||
callback.host "callback.host"
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
WHERE task.id = $1`, input.TaskID); err != nil {
|
||||
WHERE task.id = $1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch task")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
if input.PayloadOnHost.Host == "" {
|
||||
input.PayloadOnHost.Host = task.Callback.Host
|
||||
} else {
|
||||
if input.PayloadOnHost.Host == "" {
|
||||
input.PayloadOnHost.Host = task.Callback.Host
|
||||
} else {
|
||||
input.PayloadOnHost.Host = strings.ToUpper(input.PayloadOnHost.Host)
|
||||
}
|
||||
payloadId := 0
|
||||
if input.PayloadOnHost.PayloadId != nil {
|
||||
payloadId = *input.PayloadOnHost.PayloadId
|
||||
} else if input.PayloadOnHost.PayloadUUID != nil {
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1 AND operation_id=$2`,
|
||||
*input.PayloadOnHost.PayloadUUID, task.OperationID); err != nil {
|
||||
logging.LogError(err, "Failed to find specified payload UUID")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
payloadId = payload.ID
|
||||
}
|
||||
}
|
||||
if payloadId == 0 {
|
||||
response.Error = fmt.Sprintf("Failed to find the specified payload")
|
||||
return response
|
||||
} else if _, err := database.DB.Exec(`INSERT INTO payloadonhost
|
||||
(host, payload_id, operation_id, task_id) VALUES
|
||||
($1, $2, $3, $4)`,
|
||||
input.PayloadOnHost.Host, payloadId, task.OperationID, task.ID); err != nil {
|
||||
logging.LogError(err, "Failed to create new payload on host value")
|
||||
input.PayloadOnHost.Host = strings.ToUpper(input.PayloadOnHost.Host)
|
||||
}
|
||||
payloadId := 0
|
||||
if input.PayloadOnHost.PayloadId != nil {
|
||||
payload := databaseStructs.Payload{}
|
||||
err = database.DB.Get(&payload, `SELECT
|
||||
id
|
||||
FROM payload
|
||||
WHERE id=$1 AND operation_id=$2`,
|
||||
*input.PayloadOnHost.PayloadId, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find specified payload UUID")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
}
|
||||
payloadId = payload.ID
|
||||
} else if input.PayloadOnHost.PayloadUUID != nil {
|
||||
payload := databaseStructs.Payload{}
|
||||
err = database.DB.Get(&payload, `SELECT
|
||||
id
|
||||
FROM payload
|
||||
WHERE uuid=$1 AND operation_id=$2`,
|
||||
*input.PayloadOnHost.PayloadUUID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to find specified payload UUID")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
payloadId = payload.ID
|
||||
}
|
||||
if payloadId == 0 {
|
||||
response.Error = fmt.Sprintf("Failed to find the specified payload")
|
||||
return response
|
||||
}
|
||||
_, err = database.DB.Exec(`INSERT INTO payloadonhost
|
||||
(host, payload_id, operation_id, task_id) VALUES
|
||||
($1, $2, $3, $4)`,
|
||||
input.PayloadOnHost.Host, payloadId, authContext.OperationID, task.ID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to create new payload on host value")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCPayloadOnHostCreate(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCPayloadOnHostCreateMessage{}
|
||||
responseMsg := MythicRPCPayloadOnHostCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCPayloadOnHostCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCPayloadOnHostCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -24,11 +26,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PROCESS_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_PROCESS_CREATE,
|
||||
Handler: processMythicRPCProcessCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_BROWSER_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_PROCESS_CREATE
|
||||
func MythicRPCProcessCreate(input MythicRPCProcessCreateMessage) MythicRPCProcessCreateMessageResponse {
|
||||
func MythicRPCProcessCreate(input MythicRPCProcessCreateMessage, authContext RabbitMQAuthContext) MythicRPCProcessCreateMessageResponse {
|
||||
response := MythicRPCProcessCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -37,7 +40,7 @@ func MythicRPCProcessCreate(input MythicRPCProcessCreateMessage) MythicRPCProces
|
||||
return response
|
||||
}
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
task.id, task.status, task.completed, task.status_timestamp_processed, task.operator_id, task.operation_id,
|
||||
task.apitokens_id, task.eventstepinstance_id,
|
||||
callback.host "callback.host",
|
||||
@@ -49,29 +52,36 @@ func MythicRPCProcessCreate(input MythicRPCProcessCreateMessage) MythicRPCProces
|
||||
FROM task
|
||||
JOIN callback ON task.callback_id = callback.id
|
||||
JOIN payload ON callback.registered_payload_id = payload.id
|
||||
WHERE task.id = $1`, input.TaskID); err != nil {
|
||||
WHERE task.id = $1 AND task.operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch task")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err := HandleAgentMessagePostResponseProcesses(task, &input.Processes, int(task.APITokensID.Int64)); err != nil {
|
||||
}
|
||||
err = HandleAgentMessagePostResponseProcesses(task, &input.Processes, int(task.APITokensID.Int64))
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to create processes in MythicRPCProcessCreate")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCProcessCreate(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCProcessCreateMessage{}
|
||||
responseMsg := MythicRPCProcessCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCProcessCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCProcessCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
@@ -12,7 +14,6 @@ import (
|
||||
)
|
||||
|
||||
type MythicRPCProcessSearchMessage struct {
|
||||
TaskID int `json:"task_id"` //required
|
||||
SearchProcess MythicRPCProcessSearchProcessData `json:"process"`
|
||||
}
|
||||
type MythicRPCProcessSearchMessageResponse struct {
|
||||
@@ -40,108 +41,105 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PROCESS_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_PROCESS_SEARCH,
|
||||
Handler: processMythicRPCProcessSearch,
|
||||
Scopes: []string{mythicjwt.SCOPE_BROWSER_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_PROCESS_SEARCH
|
||||
func MythicRPCProcessSearch(input MythicRPCProcessSearchMessage) MythicRPCProcessSearchMessageResponse {
|
||||
func MythicRPCProcessSearch(input MythicRPCProcessSearchMessage, authContext RabbitMQAuthContext) MythicRPCProcessSearchMessageResponse {
|
||||
response := MythicRPCProcessSearchMessageResponse{
|
||||
Success: false,
|
||||
Processes: []MythicRPCProcessSearchProcessData{},
|
||||
}
|
||||
paramDict := make(map[string]interface{})
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
task.id, task.operation_id
|
||||
FROM task
|
||||
WHERE task.id=$1`, input.TaskID); err != nil {
|
||||
paramDict["operation_id"] = authContext.OperationID
|
||||
searchString := `SELECT * FROM mythictree WHERE operation_id=:operation_id AND tree_type='process' `
|
||||
if input.SearchProcess.Host != nil {
|
||||
paramDict["host"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.Host)
|
||||
searchString += "AND host ILIKE :host "
|
||||
}
|
||||
if input.SearchProcess.ProcessID != nil {
|
||||
paramDict["process_id"] = fmt.Sprintf("%d", *input.SearchProcess.ProcessID)
|
||||
searchString += "AND metadata->>'process_id'=:process_id "
|
||||
}
|
||||
if input.SearchProcess.Architecture != nil {
|
||||
paramDict["architecture"] = *input.SearchProcess.Architecture
|
||||
searchString += "AND metadata->>'architecture'=:architecture "
|
||||
}
|
||||
if input.SearchProcess.ParentProcessID != nil {
|
||||
paramDict["parent_process_id"] = fmt.Sprintf("%d", *input.SearchProcess.ParentProcessID)
|
||||
searchString += "AND metadata->>'parent_process_id'=:parent_process_id "
|
||||
}
|
||||
if input.SearchProcess.BinPath != nil {
|
||||
paramDict["bin_path"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.BinPath)
|
||||
searchString += "AND metadata->>'bin_path' ILIKE :bin_path "
|
||||
}
|
||||
if input.SearchProcess.Name != nil {
|
||||
paramDict["name"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.Name)
|
||||
searchString += "AND name ILIKE :name "
|
||||
}
|
||||
if input.SearchProcess.User != nil {
|
||||
paramDict["user"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.User)
|
||||
searchString += "AND metadata->>'user' ILIKE :user "
|
||||
}
|
||||
if input.SearchProcess.CommandLine != nil {
|
||||
paramDict["command_line"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.CommandLine)
|
||||
searchString += "AND metadata->>'command_line' ILIKE :command_line "
|
||||
}
|
||||
if input.SearchProcess.IntegrityLevel != nil {
|
||||
paramDict["integrity_level"] = fmt.Sprintf("%d", *input.SearchProcess.IntegrityLevel)
|
||||
searchString += "AND metadata->>'integrity_level'=:integrity_level "
|
||||
}
|
||||
if input.SearchProcess.Description != nil {
|
||||
paramDict["description"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.Description)
|
||||
searchString += "AND metadata->>'description' ILIKE :description "
|
||||
}
|
||||
if input.SearchProcess.Signer != nil {
|
||||
paramDict["signer"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.Signer)
|
||||
searchString += "AND metadata->>'signer' ILIKE :signer "
|
||||
}
|
||||
searchString += " ORDER BY id DESC"
|
||||
rows, err := database.DB.NamedQuery(searchString, paramDict)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to search artifact information")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
paramDict["operation_id"] = task.OperationID
|
||||
searchString := `SELECT * FROM mythictree WHERE operation_id=:operation_id AND tree_type='process' `
|
||||
if input.SearchProcess.Host != nil {
|
||||
paramDict["host"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.Host)
|
||||
searchString += "AND host ILIKE :host "
|
||||
}
|
||||
if input.SearchProcess.ProcessID != nil {
|
||||
paramDict["process_id"] = fmt.Sprintf("%d", *input.SearchProcess.ProcessID)
|
||||
searchString += "AND metadata->>'process_id'=:process_id "
|
||||
}
|
||||
if input.SearchProcess.Architecture != nil {
|
||||
paramDict["architecture"] = *input.SearchProcess.Architecture
|
||||
searchString += "AND metadata->>'architecture'=:architecture "
|
||||
}
|
||||
if input.SearchProcess.ParentProcessID != nil {
|
||||
paramDict["parent_process_id"] = fmt.Sprintf("%d", *input.SearchProcess.ParentProcessID)
|
||||
searchString += "AND metadata->>'parent_process_id'=:parent_process_id "
|
||||
}
|
||||
if input.SearchProcess.BinPath != nil {
|
||||
paramDict["bin_path"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.BinPath)
|
||||
searchString += "AND metadata->>'bin_path' ILIKE :bin_path "
|
||||
}
|
||||
if input.SearchProcess.Name != nil {
|
||||
paramDict["name"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.Name)
|
||||
searchString += "AND name ILIKE :name "
|
||||
}
|
||||
if input.SearchProcess.User != nil {
|
||||
paramDict["user"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.User)
|
||||
searchString += "AND metadata->>'user' ILIKE :user "
|
||||
}
|
||||
if input.SearchProcess.CommandLine != nil {
|
||||
paramDict["command_line"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.CommandLine)
|
||||
searchString += "AND metadata->>'command_line' ILIKE :command_line "
|
||||
}
|
||||
if input.SearchProcess.IntegrityLevel != nil {
|
||||
paramDict["integrity_level"] = fmt.Sprintf("%d", *input.SearchProcess.IntegrityLevel)
|
||||
searchString += "AND metadata->>'integrity_level'=:integrity_level "
|
||||
}
|
||||
if input.SearchProcess.Description != nil {
|
||||
paramDict["description"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.Description)
|
||||
searchString += "AND metadata->>'description' ILIKE :description "
|
||||
}
|
||||
if input.SearchProcess.Signer != nil {
|
||||
paramDict["signer"] = fmt.Sprintf("%%%s%%", *input.SearchProcess.Signer)
|
||||
searchString += "AND metadata->>'signer' ILIKE :signer "
|
||||
}
|
||||
searchString += " ORDER BY id DESC"
|
||||
if rows, err := database.DB.NamedQuery(searchString, paramDict); err != nil {
|
||||
logging.LogError(err, "Failed to search artifact information")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
searchResult := databaseStructs.MythicTree{}
|
||||
if err = rows.StructScan(&searchResult); err != nil {
|
||||
logging.LogError(err, "Failed to get row from artifacts for search")
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
searchResult := databaseStructs.MythicTree{}
|
||||
if err = rows.StructScan(&searchResult); err != nil {
|
||||
logging.LogError(err, "Failed to get row from artifacts for search")
|
||||
} else {
|
||||
returnedProcess := MythicRPCProcessSearchProcessData{}
|
||||
if err := mapstructure.Decode(searchResult.Metadata.StructValue(), &returnedProcess); err != nil {
|
||||
logging.LogError(err, "Failed to decode process search result to struct")
|
||||
} else {
|
||||
returnedProcess.Host = &searchResult.Host
|
||||
nm := string(searchResult.Name)
|
||||
returnedProcess.Name = &nm
|
||||
response.Processes = append(response.Processes, returnedProcess)
|
||||
}
|
||||
}
|
||||
returnedProcess := MythicRPCProcessSearchProcessData{}
|
||||
if err := mapstructure.Decode(searchResult.Metadata.StructValue(), &returnedProcess); err != nil {
|
||||
logging.LogError(err, "Failed to decode process search result to struct")
|
||||
} else {
|
||||
returnedProcess.Host = &searchResult.Host
|
||||
nm := string(searchResult.Name)
|
||||
returnedProcess.Name = &nm
|
||||
response.Processes = append(response.Processes, returnedProcess)
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCProcessSearch(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCProcessSearchMessage{}
|
||||
responseMsg := MythicRPCProcessSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCProcessSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCProcessSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -29,13 +31,14 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PROXY_START,
|
||||
RoutingKey: MYTHIC_RPC_PROXY_START,
|
||||
Handler: processMythicRPCProxyStart,
|
||||
Scopes: []string{mythicjwt.SCOPE_TASK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_PROXY
|
||||
//
|
||||
// Creates a FileMeta object for a specific task in Mythic's database and writes contents to disk with a random UUID filename.
|
||||
func MythicRPCProxyStart(input MythicRPCProxyStartMessage) MythicRPCProxyStartMessageResponse {
|
||||
func MythicRPCProxyStart(input MythicRPCProxyStartMessage, authContext RabbitMQAuthContext) MythicRPCProxyStartMessageResponse {
|
||||
response := MythicRPCProxyStartMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -44,7 +47,10 @@ func MythicRPCProxyStart(input MythicRPCProxyStartMessage) MythicRPCProxyStartMe
|
||||
return response
|
||||
}
|
||||
task := databaseStructs.Task{ID: input.TaskID}
|
||||
err := database.DB.Get(&task, `SELECT id, operation_id, callback_id FROM task WHERE id=$1`, task.ID)
|
||||
err := database.DB.Get(&task, `
|
||||
SELECT id, operation_id, callback_id
|
||||
FROM task
|
||||
WHERE id=$1 AND operation_id=$2`, task.ID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get task from database to start socks")
|
||||
response.Error = err.Error()
|
||||
@@ -89,11 +95,16 @@ func processMythicRPCProxyStart(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCProxyStartMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCProxyStart(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCProxyStart(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -29,67 +30,74 @@ func init() {
|
||||
Queue: MYTHIC_RPC_PROXY_STOP,
|
||||
RoutingKey: MYTHIC_RPC_PROXY_STOP,
|
||||
Handler: processMythicRPCProxyStop,
|
||||
Scopes: []string{mythicjwt.SCOPE_TASK_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_PROXY
|
||||
//
|
||||
// Creates a FileMeta object for a specific task in Mythic's database and writes contents to disk with a random UUID filename.
|
||||
func MythicRPCProxyStop(input MythicRPCProxyStopMessage) MythicRPCProxyStopMessageResponse {
|
||||
func MythicRPCProxyStop(input MythicRPCProxyStopMessage, authContext RabbitMQAuthContext) MythicRPCProxyStopMessageResponse {
|
||||
response := MythicRPCProxyStopMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
task := databaseStructs.Task{ID: input.TaskID}
|
||||
if err := database.DB.Get(&task, `SELECT id, operation_id, callback_id FROM task WHERE id=$1`, task.ID); err != nil {
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
id, operation_id, callback_id
|
||||
FROM task
|
||||
WHERE id=$1 AND operation_id=$2`, task.ID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to get task from database to start socks")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
switch input.PortType {
|
||||
case CALLBACK_PORT_TYPE_RPORTFWD:
|
||||
fallthrough
|
||||
case CALLBACK_PORT_TYPE_SOCKS:
|
||||
fallthrough
|
||||
case CALLBACK_PORT_TYPE_INTERACTIVE:
|
||||
}
|
||||
switch input.PortType {
|
||||
case CALLBACK_PORT_TYPE_RPORTFWD:
|
||||
fallthrough
|
||||
case CALLBACK_PORT_TYPE_SOCKS:
|
||||
fallthrough
|
||||
case CALLBACK_PORT_TYPE_INTERACTIVE:
|
||||
if input.Port == 0 {
|
||||
// lookup the port that might need to be closed for this PortType and CallbackID
|
||||
input.Port = proxyPorts.GetPortForTypeAndCallback(task.ID, task.CallbackID, input.PortType)
|
||||
if input.Port == 0 {
|
||||
// lookup the port that might need to be closed for this PortType and CallbackID
|
||||
input.Port = proxyPorts.GetPortForTypeAndCallback(task.ID, task.CallbackID, input.PortType)
|
||||
if input.Port == 0 {
|
||||
response.Error = fmt.Sprintf("Failed to find port for type, %s, and task, %d", input.PortType, task.ID)
|
||||
return response
|
||||
}
|
||||
response.Error = fmt.Sprintf("Failed to find port for type, %s, and task, %d", input.PortType, task.ID)
|
||||
return response
|
||||
}
|
||||
}
|
||||
response.LocalPort = input.Port
|
||||
err := proxyPorts.Remove(task.CallbackID,
|
||||
input.PortType,
|
||||
input.Port,
|
||||
task.OperationID,
|
||||
"",
|
||||
0,
|
||||
input.Username,
|
||||
input.Password)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to stop callback port")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
response.LocalPort = input.Port
|
||||
err = proxyPorts.Remove(task.CallbackID,
|
||||
input.PortType,
|
||||
input.Port,
|
||||
task.OperationID,
|
||||
"",
|
||||
0,
|
||||
input.Username,
|
||||
input.Password)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to stop callback port")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCProxyStop(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCProxyStopMessage{}
|
||||
responseMsg := MythicRPCProxyStopMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCProxyStop(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCProxyStop(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -25,13 +26,14 @@ func init() {
|
||||
Queue: MYTHIC_RPC_RESPONSE_CREATE,
|
||||
RoutingKey: MYTHIC_RPC_RESPONSE_CREATE,
|
||||
Handler: processMythicRPCPayloadResponseCreate,
|
||||
Scopes: []string{mythicjwt.SCOPE_RESPONSE_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_RESPONSE_CREATE
|
||||
//
|
||||
// Creates a FileMeta object for a specific task in Mythic's database and writes contents to disk with a random UUID filename.
|
||||
func MythicRPCResponseCreate(input MythicRPCResponseCreateMessage) MythicRPCResponseCreateMessageResponse {
|
||||
func MythicRPCResponseCreate(input MythicRPCResponseCreateMessage, authContext RabbitMQAuthContext) MythicRPCResponseCreateMessageResponse {
|
||||
response := MythicRPCResponseCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -42,7 +44,10 @@ func MythicRPCResponseCreate(input MythicRPCResponseCreateMessage) MythicRPCResp
|
||||
response.Error = "Response must have actual bytes"
|
||||
return response
|
||||
}
|
||||
err := database.DB.Get(&task, `SELECT operation_id, agent_task_id FROM task WHERE id=$1`, input.TaskID)
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
operation_id, agent_task_id, eventstepinstance_id, apitokens_id
|
||||
FROM task
|
||||
WHERE id=$1 AND operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to fetch task from database")
|
||||
response.Error = err.Error()
|
||||
@@ -63,11 +68,16 @@ func processMythicRPCPayloadResponseCreate(msg amqp.Delivery) interface{} {
|
||||
responseMsg := MythicRPCResponseCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCResponseCreate(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCResponseCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -30,13 +31,14 @@ func init() {
|
||||
Queue: MYTHIC_RPC_RESPONSE_SEARCH,
|
||||
RoutingKey: MYTHIC_RPC_RESPONSE_SEARCH,
|
||||
Handler: processMythicRPCPayloadResponseSearch,
|
||||
Scopes: []string{mythicjwt.SCOPE_RESPONSE_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// Endpoint: MYTHIC_RPC_RESPONSE_SEARCH
|
||||
//
|
||||
// Creates a FileMeta object for a specific task in Mythic's database and writes contents to disk with a random UUID filename.
|
||||
func MythicRPCResponseSearch(input MythicRPCResponseSearchMessage) MythicRPCResponseSearchMessageResponse {
|
||||
func MythicRPCResponseSearch(input MythicRPCResponseSearchMessage, authContext RabbitMQAuthContext) MythicRPCResponseSearchMessageResponse {
|
||||
response := MythicRPCResponseSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -48,43 +50,50 @@ func MythicRPCResponseSearch(input MythicRPCResponseSearchMessage) MythicRPCResp
|
||||
responseSearch = "%" + responseSearch + "%"
|
||||
}
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
err := database.DB.Get(&task, `SELECT
|
||||
operation_id, display_id, id
|
||||
FROM task
|
||||
WHERE id=$1`, input.TaskID); err != nil {
|
||||
WHERE id=$1 AND operation_id=$2`, input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch task for MythicRPCResponseSearch")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else if err := database.DB.Select(&results, `SELECT
|
||||
id, response, task_id
|
||||
FROM response
|
||||
WHERE operation_id=$1 AND response LIKE $2 AND task_id=$3 ORDER BY id DESC`,
|
||||
task.OperationID, responseSearch, input.TaskID); err != nil {
|
||||
}
|
||||
err = database.DB.Select(&results, `SELECT
|
||||
id, response, task_id
|
||||
FROM response
|
||||
WHERE operation_id=$1 AND response LIKE $2 AND task_id=$3 ORDER BY id DESC`,
|
||||
authContext.OperationID, responseSearch, input.TaskID)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to fetch responses for MythicRPCResponseSearch")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
for _, resp := range results {
|
||||
response.Responses = append(response.Responses, MythicRPCResponse{
|
||||
ResponseID: resp.ID,
|
||||
Response: resp.Response,
|
||||
TaskID: resp.TaskID,
|
||||
})
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
for _, resp := range results {
|
||||
response.Responses = append(response.Responses, MythicRPCResponse{
|
||||
ResponseID: resp.ID,
|
||||
Response: resp.Response,
|
||||
TaskID: resp.TaskID,
|
||||
})
|
||||
}
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
func processMythicRPCPayloadResponseSearch(msg amqp.Delivery) interface{} {
|
||||
incomingMessage := MythicRPCResponseSearchMessage{}
|
||||
responseMsg := MythicRPCResponseSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
if err := json.Unmarshal(msg.Body, &incomingMessage); err != nil {
|
||||
err := json.Unmarshal(msg.Body, &incomingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to unmarshal JSON into struct")
|
||||
responseMsg.Error = err.Error()
|
||||
} else {
|
||||
return MythicRPCResponseSearch(incomingMessage)
|
||||
return responseMsg
|
||||
}
|
||||
return responseMsg
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCResponseSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package rabbitmq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/eventing"
|
||||
@@ -33,11 +35,12 @@ func init() {
|
||||
Queue: MYTHIC_RPC_TAG_CREATE, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_TAG_CREATE, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCTagCreate, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_TAG_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCTagCreate(input MythicRPCTagCreateMessage) MythicRPCTagCreateMessageResponse {
|
||||
func MythicRPCTagCreate(input MythicRPCTagCreateMessage, authContext RabbitMQAuthContext) MythicRPCTagCreateMessageResponse {
|
||||
response := MythicRPCTagCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
@@ -46,19 +49,12 @@ func MythicRPCTagCreate(input MythicRPCTagCreateMessage) MythicRPCTagCreateMessa
|
||||
URL: input.URL,
|
||||
Source: input.Source,
|
||||
Data: GetMythicJSONTextFromStruct(input.Data),
|
||||
Operation: authContext.OperationID,
|
||||
}
|
||||
operationID := 0
|
||||
err := database.DB.Get(&operationID, "SELECT operation_id FROM tagtype WHERE id = $1", tag.TagTypeID)
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
tag.Operation = operationID
|
||||
if input.MythicTreeID != nil {
|
||||
mythicTree := databaseStructs.MythicTree{}
|
||||
err = database.DB.Get(&mythicTree, `SELECT id FROM mythictree WHERE id=$1 AND operation_id=$2`,
|
||||
*input.MythicTreeID, operationID)
|
||||
err := database.DB.Get(&mythicTree, `SELECT id FROM mythictree WHERE id=$1 AND operation_id=$2`,
|
||||
*input.MythicTreeID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(nil, "Failed to get mythictree info")
|
||||
response.Success = false
|
||||
@@ -70,8 +66,8 @@ func MythicRPCTagCreate(input MythicRPCTagCreateMessage) MythicRPCTagCreateMessa
|
||||
}
|
||||
if input.FileID != nil {
|
||||
fileMeta := databaseStructs.Filemeta{}
|
||||
err = database.DB.Get(&fileMeta, `SELECT id FROM filemeta WHERE id=$1 AND operation_id=$2`,
|
||||
*input.FileID, operationID)
|
||||
err := database.DB.Get(&fileMeta, `SELECT id FROM filemeta WHERE id=$1 AND operation_id=$2`,
|
||||
*input.FileID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(nil, "Failed to get filemeta info")
|
||||
response.Success = false
|
||||
@@ -83,8 +79,8 @@ func MythicRPCTagCreate(input MythicRPCTagCreateMessage) MythicRPCTagCreateMessa
|
||||
}
|
||||
if input.TaskID != nil {
|
||||
task := databaseStructs.Task{}
|
||||
err = database.DB.Get(&task, `SELECT id FROM task WHERE id=$1 AND operation_id=$2`,
|
||||
*input.TaskID, operationID)
|
||||
err := database.DB.Get(&task, `SELECT id FROM task WHERE id=$1 AND operation_id=$2`,
|
||||
*input.TaskID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(nil, "Failed to get task info")
|
||||
response.Success = false
|
||||
@@ -96,8 +92,8 @@ func MythicRPCTagCreate(input MythicRPCTagCreateMessage) MythicRPCTagCreateMessa
|
||||
}
|
||||
if input.CredentialID != nil {
|
||||
credential := databaseStructs.Credential{}
|
||||
err = database.DB.Get(&credential, `SELECT id FROM credential WHERE id=$1 AND operation_id=$2`,
|
||||
*input.CredentialID, operationID)
|
||||
err := database.DB.Get(&credential, `SELECT id FROM credential WHERE id=$1 AND operation_id=$2`,
|
||||
*input.CredentialID, authContext.OperationID)
|
||||
if err != nil {
|
||||
logging.LogError(nil, "Failed to get credential info")
|
||||
response.Success = false
|
||||
@@ -107,10 +103,18 @@ func MythicRPCTagCreate(input MythicRPCTagCreateMessage) MythicRPCTagCreateMessa
|
||||
tag.Credential.Valid = true
|
||||
tag.Credential.Int64 = int64(credential.ID)
|
||||
}
|
||||
if authContext.APITokensID > 0 {
|
||||
tag.APITokensID.Valid = true
|
||||
tag.APITokensID.Int64 = int64(authContext.APITokensID)
|
||||
}
|
||||
if authContext.EventStepInstanceID > 0 {
|
||||
tag.EventStepInstanceID.Valid = true
|
||||
tag.EventStepInstanceID.Int64 = int64(authContext.EventStepInstanceID)
|
||||
}
|
||||
statement, err := database.DB.PrepareNamed(`INSERT INTO tag
|
||||
(operation_id, data, url, source, tagtype_id, mythictree_id, filemeta_id, task_id, response_id, credential_id, keylog_id, taskartifact_id)
|
||||
(operation_id, data, url, source, tagtype_id, mythictree_id, filemeta_id, task_id, response_id, credential_id, keylog_id, taskartifact_id, apitokens_id, eventstepinstance_id)
|
||||
VALUES
|
||||
(:operation_id, :data, :url, :source, :tagtype_id, :mythictree_id, :filemeta_id, :task_id, :response_id, :credential_id, :keylog_id, :taskartifact_id)
|
||||
(:operation_id, :data, :url, :source, :tagtype_id, :mythictree_id, :filemeta_id, :task_id, :response_id, :credential_id, :keylog_id, :taskartifact_id, :apitokens_id, :eventstepinstance_id)
|
||||
RETURNING id`)
|
||||
if err != nil {
|
||||
logging.LogError(nil, "Failed to prepare statement for adding tag")
|
||||
@@ -147,5 +151,10 @@ func processMythicRPCTagCreate(msg amqp.Delivery) interface{} {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCTagCreate(incomingMessage)
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCTagCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package rabbitmq
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
type MythicRPCTagSearchMessage struct {
|
||||
TaskID int `json:"task_id"`
|
||||
SearchTagID *int `json:"search_tag_id"`
|
||||
SearchTagTaskID *int `json:"search_tag_task_id"`
|
||||
SearchTagFileID *int `json:"search_tag_file_id,omitempty"`
|
||||
@@ -55,20 +55,15 @@ func init() {
|
||||
Queue: MYTHIC_RPC_TAG_SEARCH, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_TAG_SEARCH, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCTagSearch, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_TAG_READ},
|
||||
})
|
||||
}
|
||||
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCTagSearch(input MythicRPCTagSearchMessage) MythicRPCTagSearchMessageResponse {
|
||||
func MythicRPCTagSearch(input MythicRPCTagSearchMessage, authContext RabbitMQAuthContext) MythicRPCTagSearchMessageResponse {
|
||||
response := MythicRPCTagSearchMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
operationID := 0
|
||||
err := database.DB.Get(&operationID, `SELECT task.operation_id FROM task WHERE id=$1`, input.TaskID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
paramDict := make(map[string]interface{})
|
||||
setAnySearchValues := false
|
||||
searchString := `SELECT
|
||||
@@ -154,11 +149,10 @@ func MythicRPCTagSearch(input MythicRPCTagSearchMessage) MythicRPCTagSearchMessa
|
||||
}
|
||||
if !setAnySearchValues {
|
||||
searchString += `WHERE tag.operation_id=:operation_id `
|
||||
paramDict["operation_id"] = operationID
|
||||
} else {
|
||||
searchString += `AND tag.operation_id=:operation_id `
|
||||
paramDict["operation_id"] = operationID
|
||||
}
|
||||
paramDict["operation_id"] = authContext.OperationID
|
||||
searchString += " ORDER BY tag.id DESC"
|
||||
query, args, err := sqlx.Named(searchString, paramDict)
|
||||
if err != nil {
|
||||
@@ -224,5 +218,10 @@ func processMythicRPCTagSearch(msg amqp.Delivery) interface{} {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCTagSearch(incomingMessage)
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCTagSearch(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
|
||||
"github.com/its-a-feature/Mythic/database"
|
||||
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
|
||||
"github.com/its-a-feature/Mythic/logging"
|
||||
"github.com/its-a-feature/Mythic/utils/structs"
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
|
||||
@@ -32,25 +34,30 @@ func init() {
|
||||
Queue: MYTHIC_RPC_TAGTYPE_GET_OR_CREATE, // swap out with queue in rabbitmq.constants.go file
|
||||
RoutingKey: MYTHIC_RPC_TAGTYPE_GET_OR_CREATE, // swap out with routing key in rabbitmq.constants.go file
|
||||
Handler: processMythicRPCTagtypeGetOrCreate, // points to function that takes in amqp.Delivery and returns interface{}
|
||||
Scopes: []string{mythicjwt.SCOPE_TAG_WRITE},
|
||||
})
|
||||
}
|
||||
|
||||
// MYTHIC_RPC_OBJECT_ACTION - Say what the function does
|
||||
func MythicRPCTagtypeGetOrCreate(input MythicRPCTagTypeGetOrCreateMessage) MythicRPCTagTypeGetOrCreateMessageResponse {
|
||||
func MythicRPCTagtypeGetOrCreate(input MythicRPCTagTypeGetOrCreateMessage, authContext RabbitMQAuthContext) MythicRPCTagTypeGetOrCreateMessageResponse {
|
||||
response := MythicRPCTagTypeGetOrCreateMessageResponse{
|
||||
Success: false,
|
||||
}
|
||||
operationID := 0
|
||||
err := database.DB.Get(&operationID, `SELECT task.operation_id FROM task WHERE id=$1`, input.TaskID)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
tt := databaseStructs.TagType{
|
||||
Operation: operationID,
|
||||
Operation: authContext.OperationID,
|
||||
}
|
||||
if authContext.APITokensID > 0 {
|
||||
tt.APITokensID = structs.NullInt64{}
|
||||
tt.APITokensID.Valid = true
|
||||
tt.APITokensID.Int64 = int64(authContext.APITokensID)
|
||||
}
|
||||
if authContext.EventStepInstanceID > 0 {
|
||||
tt.EventStepInstanceID = structs.NullInt64{}
|
||||
tt.EventStepInstanceID.Valid = true
|
||||
tt.EventStepInstanceID.Int64 = int64(authContext.EventStepInstanceID)
|
||||
}
|
||||
paramDict := make(map[string]interface{})
|
||||
paramDict["operation_id"] = operationID
|
||||
paramDict["operation_id"] = authContext.OperationID
|
||||
searchString := `SELECT * FROM tagtype WHERE operation_id=:operation_id `
|
||||
if input.GetOrCreateTagTypeID != nil {
|
||||
searchString += `AND id=:id `
|
||||
@@ -86,9 +93,9 @@ func MythicRPCTagtypeGetOrCreate(input MythicRPCTagTypeGetOrCreateMessage) Mythi
|
||||
response.Error = "failed to find tagtype and no name provided to create one"
|
||||
return response
|
||||
}
|
||||
statement, err := database.DB.PrepareNamed(`INSERT INTO tagtype
|
||||
(name, description, color, operation_id)
|
||||
VALUES (:name, :description, :color, :operation_id)
|
||||
statement, err = database.DB.PrepareNamed(`INSERT INTO tagtype
|
||||
(name, description, color, operation_id, apitokens_id, eventstepinstance_id)
|
||||
VALUES (:name, :description, :color, :operation_id, :apitokens_id, :eventstepinstance_id)
|
||||
RETURNING id`)
|
||||
if err != nil {
|
||||
response.Error = err.Error()
|
||||
@@ -101,13 +108,13 @@ func MythicRPCTagtypeGetOrCreate(input MythicRPCTagTypeGetOrCreateMessage) Mythi
|
||||
}
|
||||
response.TagType = getTagTypeDataFromDatabaseTagType(tt)
|
||||
return response
|
||||
} else if err != nil {
|
||||
}
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to exec sqlx.IN modified statement")
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
response.TagType = getTagTypeDataFromDatabaseTagType(tt)
|
||||
}
|
||||
response.TagType = getTagTypeDataFromDatabaseTagType(tt)
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
@@ -132,5 +139,10 @@ func processMythicRPCTagtypeGetOrCreate(msg amqp.Delivery) interface{} {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCTagtypeGetOrCreate(incomingMessage)
|
||||
authContext, err := GetRabbitMQAuthContextFromHeaders(msg.Headers)
|
||||
if err != nil {
|
||||
responseMsg.Error = err.Error()
|
||||
return responseMsg
|
||||
}
|
||||
return MythicRPCTagtypeGetOrCreate(incomingMessage, authContext)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user