mirror of
https://github.com/its-a-feature/Mythic.git
synced 2026-11-12 13:04:09 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96b24b48a7 | ||
|
|
bdc722f6ce | ||
|
|
f9e71d0859 | ||
|
|
742d878928 | ||
|
|
01ea8bc896 | ||
|
|
94418bcd52 | ||
|
|
3ae9c5fe18 | ||
|
|
5e85a20d14 | ||
|
|
5f46db6ede | ||
|
|
e37e057a89 | ||
|
|
33a46d7db7 | ||
|
|
540aec0f00 | ||
|
|
29be09eef4 | ||
|
|
a5a1a972d7 | ||
|
|
82648e8241 | ||
|
|
e4da2c887f | ||
|
|
39f44f7dec | ||
|
|
0f51584493 | ||
|
|
dc50ecc0dc | ||
|
|
59211a2fbf | ||
|
|
f8d355a29c | ||
|
|
1da8fdcd2a | ||
|
|
2dcc3b5699 | ||
|
|
8deec59270 | ||
|
|
071abeaf74 | ||
|
|
6eb53da6b5 | ||
|
|
d605a2f82c | ||
|
|
31db999945 | ||
|
|
9ae193be76 | ||
|
|
fc731af819 | ||
|
|
d21dbf293b | ||
|
|
b6d98b1794 | ||
|
|
448a57b82b | ||
|
|
1d54d5ea0c | ||
|
|
4d793d5998 | ||
|
|
60455c26cb | ||
|
|
183f0605c2 | ||
|
|
37d3cc8b1b | ||
|
|
9532b96f7a | ||
|
|
e1806f0fd5 | ||
|
|
fa235b6378 | ||
|
|
b87219b1e9 | ||
|
|
b5459b56c8 | ||
|
|
ca6d7d2ce0 | ||
|
|
6148c5f0fb | ||
|
|
6e14b2a472 | ||
|
|
307df57df8 | ||
|
|
4a4d95f1ca | ||
|
|
ffc5041349 | ||
|
|
ecda245963 | ||
|
|
6ea67c69b9 | ||
|
|
64d32eb116 | ||
|
|
9f7a1ce770 | ||
|
|
4a3818988c | ||
|
|
1ec6318b62 | ||
|
|
60902a6e66 | ||
|
|
ddda1a266c | ||
|
|
8dc9f351d9 |
No files matched your search
@@ -167,3 +167,6 @@ _ReSharper*/
|
||||
jupyter-docker/jupyter/MythicExamples/.ipynb_checkpoints/
|
||||
|
||||
nginx-docker/config/blockips.conf
|
||||
/MythicReactUI/build/
|
||||
/MythicReactUI/node_modules/
|
||||
/.gocache/
|
||||
@@ -4,6 +4,43 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [3.4.36] - 2026-06-21
|
||||
|
||||
### Changed
|
||||
|
||||
- Addressed 3 issues from GitHub about spectators and overly scoped hasura filters
|
||||
|
||||
## [3.4.35] - 2026-04-16
|
||||
|
||||
### Changed
|
||||
|
||||
- Merged PR #546 to address missing return in GenerateAPITokenWebhook that would allow non-admin users to generate API Tokens for Bot accounts
|
||||
- Added server name to emitted log messages
|
||||
- Merged PR #545 to address missing operation_id in task's log messages for logging containers
|
||||
- Updated C2 profile code in the UI to properly handle `ui_position` from C2 Profile parameters in payload build
|
||||
|
||||
## [3.4.34] - 2026-04-13
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated way that SendAllOperationsMessage functions to reduce duplicate messages to event log
|
||||
- Reduced the number of lookups per error message to increment count by caching error lookup data
|
||||
- Updated event log messages to ensure proper UTF8 encoding (and removal of bad characters)
|
||||
- Using channels to ensure no duplicate messages are created due to go concurrency
|
||||
|
||||
## [3.4.33] - 2026-04-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Fixed an issue where payload/command syncing would error out and leave dangling database connections
|
||||
- Updated the postgres docker image to use customized database info for health checks
|
||||
|
||||
## [3.4.32] - 2026-04-06
|
||||
|
||||
### Changed
|
||||
|
||||
- Merged PR to add GET route for /auth_acs/:containerName/:IDPName that was POST only
|
||||
|
||||
## [3.4.31] - 2026-03-23
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -4,6 +4,36 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.3.113] - 2026-07-01
|
||||
|
||||
### Changed
|
||||
|
||||
- Fixed a bug in the process browser that would set a filter to undefined instead of ""
|
||||
|
||||
## [0.3.112] - 2026-05-17
|
||||
|
||||
- Updated the single task view to show interactive tasking surrounding results
|
||||
|
||||
## [0.3.111] - 2026-05-07
|
||||
|
||||
### Changed
|
||||
|
||||
- Fixed an issue with useLazyQuery spamming requests
|
||||
|
||||
## [0.3.110] - 2026-04-16
|
||||
|
||||
### Changed
|
||||
|
||||
- Added more generic MythicPageBody and MythicServerHeader components to wrap most pages/headers
|
||||
- Added a new border color you can select for borders of a few components
|
||||
- Updated a few places with hard-coded px values to more scalable rem values
|
||||
|
||||
## [0.3.109] - 2026-04-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated the UI to sort c2 profile parameters during payload build by ui_position if specified
|
||||
|
||||
## [0.3.107] - 2026-03-23
|
||||
|
||||
### Changed
|
||||
|
||||
Generated
+22
-19
@@ -10404,9 +10404,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
|
||||
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -13746,9 +13746,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -14246,9 +14246,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-forge": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz",
|
||||
"integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
|
||||
"integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
|
||||
"dev": true,
|
||||
"license": "(BSD-3-Clause OR GPL-2.0)",
|
||||
"engines": {
|
||||
@@ -14870,9 +14870,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -15554,16 +15554,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-load-config/node_modules/yaml": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
|
||||
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
|
||||
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
"node": ">= 14.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/eemeli"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-loader": {
|
||||
@@ -21222,9 +21225,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"version": "1.10.3",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
|
||||
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
|
||||
@@ -153,6 +153,10 @@ export const operatorSettingDefaults = {
|
||||
outputTextColor: {
|
||||
dark: '#f6f6f6',
|
||||
light: '#282828',
|
||||
},
|
||||
borderColor: {
|
||||
dark: "#595858",
|
||||
light: "#c5c5c5"
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -166,6 +166,8 @@ export function App(props) {
|
||||
preferences?.palette?.outputBackgroundColor?.light || operatorSettingDefaults.palette.outputBackgroundColor.light,
|
||||
outputTextColor: themeMode === 'dark' ? preferences?.palette?.outputTextColor?.dark || operatorSettingDefaults.palette.outputTextColor.dark :
|
||||
preferences?.palette?.outputTextColor?.light || operatorSettingDefaults.palette.outputTextColor.light,
|
||||
borderColor: themeMode === 'dark' ? preferences?.palette?.borderColor?.dark || operatorSettingDefaults.palette.borderColor.dark :
|
||||
preferences?.palette?.borderColor?.light || operatorSettingDefaults.palette.borderColor.light,
|
||||
})
|
||||
}catch(error){
|
||||
console.log(error);
|
||||
@@ -251,6 +253,8 @@ export function App(props) {
|
||||
operatorSettingDefaults.palette.outputBackgroundColor.light,
|
||||
outputTextColor: themeMode === 'dark' ? operatorSettingDefaults.palette.outputTextColor.dark :
|
||||
operatorSettingDefaults.palette.outputTextColor.light,
|
||||
borderColor: themeMode === 'dark' ? operatorSettingDefaults.palette.borderColor.dark :
|
||||
operatorSettingDefaults.palette.borderColor.light,
|
||||
typography: {
|
||||
fontSize: 12,//operatorSettingDefaults?.fontSize,
|
||||
fontFamily: operatorSettingDefaults?.fontFamily
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
export const MythicPageBody = ({children}) => {
|
||||
return (
|
||||
<div style={{
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
margin: "0.5rem",
|
||||
}}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import {useTheme} from '@mui/material/styles';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import {Typography} from "@mui/material";
|
||||
|
||||
export const MythicPageHeader = ({children, title, style={}, headerVariant="h5"}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Paper elevation={2}
|
||||
variant={"elevation"}
|
||||
style={{
|
||||
backgroundColor: theme.pageHeader.main,
|
||||
color: theme.pageHeaderText.main,
|
||||
border: `1px solid ${theme.borderColor}`,
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: "0.5rem",
|
||||
padding: "0.5rem",
|
||||
borderRadius: "5px",
|
||||
...style
|
||||
}}
|
||||
>
|
||||
<Typography variant={headerVariant} style={{textAlign: "left", display: "inline-block", marginLeft: "1rem", width: "100%"}}>
|
||||
{title}
|
||||
</Typography>
|
||||
<div style={{display: "flex", marginRight: "1rem", alignItems: "center"}}>
|
||||
{children}
|
||||
</div>
|
||||
</Paper>
|
||||
)
|
||||
|
||||
}
|
||||
@@ -78,6 +78,8 @@ import {
|
||||
} from "@hello-pangea/dnd";
|
||||
import {reorder} from "./MythicComponents/MythicDraggableList";
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import LogoutIcon from '@mui/icons-material/Logout';
|
||||
import TuneIcon from '@mui/icons-material/Tune';
|
||||
|
||||
const PREFIX = 'TopAppBarVertical';
|
||||
|
||||
@@ -898,16 +900,29 @@ function TopBarRightShortcutsVertical({me, menuOpen, serverName}){
|
||||
{
|
||||
name: (
|
||||
<>
|
||||
<Typography paragraph={true} variant="caption" style={{marginBottom: "0"}}>Signed in as:</Typography>
|
||||
<Typography paragraph={true} variant="body1" style={{marginBottom: "0", fontWeight: 600}}> {me?.user?.username || "" } </Typography>
|
||||
</>
|
||||
<Typography variant={"caption"} style={{marginBottom: "0"}}>Signed in as:</Typography>
|
||||
<Typography variant={"body1"} style={{marginBottom: "0", fontWeight: 600}}>{me?.user?.username}</Typography>
|
||||
</>),
|
||||
disabled: true,
|
||||
click: handleSettingsClose
|
||||
},
|
||||
{
|
||||
name: (
|
||||
<div style={{display: "flex", alignItems: "center"}}>
|
||||
<TuneIcon style={{marginRight: "0.5rem"}}/>
|
||||
<Typography variant="body1" style={{marginBottom: "0", }}> Settings </Typography>
|
||||
</div>
|
||||
),
|
||||
to: "/new/settings",
|
||||
component: Link,
|
||||
click: handleSettingsClose
|
||||
},
|
||||
{
|
||||
name: "Logout",
|
||||
name: (
|
||||
<div style={{display: "flex", alignItems: "center"}}>
|
||||
<LogoutIcon style={{marginRight: "0.5rem"}} /> Logout
|
||||
</div>
|
||||
),
|
||||
click: handleLogout,
|
||||
to: "",
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {BrowserScriptsTable} from './BrowserScriptsTable';
|
||||
import {snackActions} from '../../utilities/Snackbar';
|
||||
import { Backdrop } from '@mui/material';
|
||||
import {CircularProgress} from '@mui/material';
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
|
||||
|
||||
const SUB_BrowserScripts = gql`
|
||||
@@ -135,7 +136,7 @@ export function BrowserScripts({me}){
|
||||
}
|
||||
}, [])
|
||||
return (
|
||||
<React.Fragment>
|
||||
<MythicPageBody>
|
||||
<Backdrop open={backdropOpen} style={{zIndex: 2, position: "absolute"}} invisible={false}>
|
||||
<CircularProgress color="inherit" />
|
||||
</Backdrop>
|
||||
@@ -145,6 +146,6 @@ export function BrowserScripts({me}){
|
||||
onSubmitEdit={onSubmitEdit} onRevert={onRevert} onSubmitNew={onSubmitCreateNewBrowserScript}
|
||||
/>
|
||||
|
||||
</React.Fragment>
|
||||
</MythicPageBody>
|
||||
);
|
||||
}
|
||||
@@ -13,30 +13,27 @@ import AddCircleIcon from '@mui/icons-material/AddCircle';
|
||||
import Button from '@mui/material/Button';
|
||||
import { MythicDialog } from '../../MythicComponents/MythicDialog';
|
||||
import {EditScriptDialog} from './EditScriptDialog';
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
|
||||
|
||||
export function BrowserScriptsTable(props){
|
||||
const theme = useTheme();
|
||||
const [openNewScriptDialog, setOpenNewScriptDialog] = React.useState(false);
|
||||
return (
|
||||
<div style={{display: "flex", flexDirection: "column", height: "100%"}}>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
|
||||
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
|
||||
Browser Scripts
|
||||
</Typography>
|
||||
<Button size="small" onClick={() => setOpenNewScriptDialog(true)} style={{float: "right", marginRight: "30px", color: "white"}}
|
||||
<>
|
||||
<MythicPageHeader title={"Browser Scripts"}>
|
||||
<Button size="small" onClick={() => setOpenNewScriptDialog(true)} style={{whiteSpace: "nowrap", color: "white"}}
|
||||
startIcon={<AddCircleIcon color={"success"} style={{backgroundColor: "white", borderRadius: "10px"}}/>} >
|
||||
New Script
|
||||
</Button>
|
||||
{openNewScriptDialog ? (
|
||||
{openNewScriptDialog &&
|
||||
<MythicDialog fullWidth={true} maxWidth="xl" open={openNewScriptDialog}
|
||||
onClose={()=>{setOpenNewScriptDialog(false);}}
|
||||
innerDialog={
|
||||
<EditScriptDialog me={props.me} onClose={()=>{setOpenNewScriptDialog(false);}} title="Create New Browser Script" new={true} onSubmitEdit={props.onSubmitNew} />
|
||||
} />
|
||||
) : null
|
||||
} />
|
||||
}
|
||||
</Paper>
|
||||
</MythicPageHeader>
|
||||
<TableContainer className="mythicElement" style={{height: "100%", flexGrow: 1}}>
|
||||
<Table stickyHeader={true} size="small" style={{"tableLayout": "fixed", "maxWidth": "100%", "overflow": "scroll"}}>
|
||||
<TableHead>
|
||||
@@ -61,8 +58,8 @@ export function BrowserScriptsTable(props){
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</div>
|
||||
</TableContainer>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@@ -197,11 +197,13 @@ export const CallbacksTabsFileBrowserTable = (props) => {
|
||||
return tempData;
|
||||
}, [allData, sortData]);
|
||||
const onSubmitFilterOptions = (value) => {
|
||||
setFilterOptions({...filterOptions, [selectedColumn.key]: value });
|
||||
|
||||
try{
|
||||
updateSetting({setting_name: `file_browser_filter_options`, value: {...filterOptions,[selectedColumn.key]: value }});
|
||||
let newValue = value ? value : "";
|
||||
setFilterOptions({...filterOptions, [selectedColumn.key]: newValue });
|
||||
updateSetting({setting_name: `file_browser_filter_options`, value: {...filterOptions,[selectedColumn.key]: newValue }});
|
||||
}catch(error){
|
||||
console.log("failed to save filter options");
|
||||
console.log("failed to save filter options", error);
|
||||
}
|
||||
}
|
||||
const filterRow = (row) => {
|
||||
|
||||
@@ -393,11 +393,12 @@ export const CallbacksTabsProcessBrowserTable = ({treeAdjMatrix, treeRootData, m
|
||||
return tempData;
|
||||
}, [allData, sortData]);
|
||||
const onSubmitFilterOptions = (value) => {
|
||||
setFilterOptions({...filterOptions, [selectedColumn.current.key]: value });
|
||||
let newValue = value ? value : "";
|
||||
try{
|
||||
updateSetting({setting_name: `process_browser_filter_options`, value: {...filterOptions,[selectedColumn.current.key]: value }});
|
||||
setFilterOptions({...filterOptions, [selectedColumn.current.key]: newValue });
|
||||
updateSetting({setting_name: `process_browser_filter_options`, value: {...filterOptions,[selectedColumn.current.key]: newValue }});
|
||||
}catch(error){
|
||||
console.log("failed to save filter options");
|
||||
console.log("failed to save filter options", error, filterOptions, selectedColumn.current.key, newValue);
|
||||
}
|
||||
if(viewSingleTreeData){
|
||||
return
|
||||
@@ -495,7 +496,7 @@ export const CallbacksTabsProcessBrowserTable = ({treeAdjMatrix, treeRootData, m
|
||||
}
|
||||
|
||||
setSingleTreeData(singleTreeAdjMatrix);
|
||||
onSubmitFilterOptions({});
|
||||
onSubmitFilterOptions(undefined);
|
||||
}
|
||||
const toggleViewSingleTreeData = () => {
|
||||
setViewSingleTreeData(!viewSingleTreeData);
|
||||
@@ -767,7 +768,7 @@ export const CallbacksTabsProcessBrowserTable = ({treeAdjMatrix, treeRootData, m
|
||||
<MythicModifyStringDialog
|
||||
title='Filter Column'
|
||||
onSubmit={onSubmitFilterOptions}
|
||||
value={filterOptions[selectedColumn.current]}
|
||||
value={String(filterOptions[selectedColumn.current.key])}
|
||||
onClose={() => {
|
||||
setOpenContextMenu(false);
|
||||
}}
|
||||
|
||||
@@ -827,7 +827,7 @@ export function CallbacksTabsTaskingInputPreMemo(props){
|
||||
buffer += value;
|
||||
return;
|
||||
}
|
||||
else if(value === " "){
|
||||
else if([" ", "\t", "\r", "\n"].includes(value)){
|
||||
if(backslash){
|
||||
backslash = false;
|
||||
//buffer += " ";
|
||||
|
||||
@@ -39,6 +39,7 @@ query getPayloadTypesC2ProfilesQuery($payloadType: String!, $operation_id: Int!)
|
||||
required
|
||||
verifier_regex
|
||||
choices
|
||||
ui_position
|
||||
}
|
||||
c2profileparametersinstances(where: {instance_name: {_is_null: false}, operation_id: {_eq: $operation_id}}, distinct_on: instance_name, order_by: {instance_name: asc}){
|
||||
instance_name
|
||||
@@ -61,6 +62,7 @@ query getProfileInstanceQuery($name: String!, $operation_id: Int!, $c2_profile_i
|
||||
required
|
||||
verifier_regex
|
||||
choices
|
||||
ui_position
|
||||
c2profile {
|
||||
name
|
||||
}
|
||||
@@ -86,6 +88,7 @@ query getDefaultC2ProfileParameters($c2profile_id: Int!) {
|
||||
required
|
||||
verifier_regex
|
||||
choices
|
||||
ui_position
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,7 +153,13 @@ export function Step4C2Profiles(props){
|
||||
onCompleted: data => {
|
||||
const profiles = data.c2profile.map( (c2) => {
|
||||
const parameters = getModifiedC2Params(c2, c2.c2profileparameters, props.buildOptions, false);
|
||||
parameters.sort((a,b) => -b.description.localeCompare(a.description));
|
||||
let hasUiPositions = parameters.filter(p => p.ui_position > 0).length > 0;
|
||||
if(hasUiPositions){
|
||||
parameters.sort((a,b) => a.ui_position > b.ui_position ? 1 : a.ui_position === b.ui_position ? 0 : -1);
|
||||
} else {
|
||||
parameters.sort((a,b) => -b.description.localeCompare(a.description));
|
||||
}
|
||||
|
||||
return {...c2, c2profileparameters: parameters, "selected_instance": "None"};
|
||||
});
|
||||
profiles.sort((a, b) => -b.name.localeCompare(a.name))
|
||||
@@ -285,7 +294,13 @@ export function Step4C2Profiles(props){
|
||||
return inst;
|
||||
});
|
||||
updates = getModifiedC2Params(c2, updates, props.buildOptions, true);
|
||||
updates.sort( (a, b) => a.description < b.description ? -1 : 1);
|
||||
//updates.sort( (a, b) => a.description < b.description ? -1 : 1);
|
||||
let hasUiPositions = updates.filter(p => p.ui_position > 0).length > 0;
|
||||
if(hasUiPositions){
|
||||
updates.sort((a,b) => a.ui_position > b.ui_position ? 1 : a.ui_position === b.ui_position ? 0 : -1);
|
||||
} else {
|
||||
updates.sort((a,b) => -b.description.localeCompare(a.description));
|
||||
}
|
||||
const updatedc2 = includedC2Profiles.map( (curc2, indx) => {
|
||||
if(index === indx){
|
||||
return {...curc2, c2profileparameters: updates, selected_instance: evt.target.value};
|
||||
@@ -310,7 +325,13 @@ export function Step4C2Profiles(props){
|
||||
choices: getDefaultChoices(param)};
|
||||
});
|
||||
updates = getModifiedC2Params(c2, updates, props.buildOptions, false);
|
||||
updates.sort( (a, b) => a.description < b.description ? -1 : 1);
|
||||
let hasUiPositions = updates.filter(p => p.ui_position > 0).length > 0;
|
||||
if(hasUiPositions){
|
||||
updates.sort((a,b) => a.ui_position > b.ui_position ? 1 : a.ui_position === b.ui_position ? 0 : -1);
|
||||
} else {
|
||||
updates.sort((a,b) => -b.description.localeCompare(a.description));
|
||||
}
|
||||
//updates.sort( (a, b) => a.description < b.description ? -1 : 1);
|
||||
const updatedc2 = includedC2Profiles.map( (curc2, indx) => {
|
||||
if(index === indx){
|
||||
return {...curc2, c2profileparameters: updates, selected_instance: 'None'};
|
||||
|
||||
@@ -15,6 +15,8 @@ import Select from '@mui/material/Select';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import {alertCount} from "../../../cache";
|
||||
import {levelOptions} from "./EventFeed";
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
|
||||
const EventList = ({onUpdateLevel, onUpdateResolution, operationeventlog}) => {
|
||||
return (
|
||||
@@ -51,42 +53,41 @@ export function EventFeedTable(props){
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<div style={{display: "flex", flexDirection: "column", height: "100%", maxWidth: "100%", overflowX: "hidden"}}>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.body, marginBottom: "5px", marginRight: "5px"}}>
|
||||
<Grid container spacing={1} style={{ marginTop: "0px"}}>
|
||||
<Grid style={{paddingTop: 0}} size={10}>
|
||||
<MythicTextField placeholder="Search..." value={search} marginBottom={"0px"} marginTop={"0px"}
|
||||
onChange={handleSearchValueChange} onEnter={submitSearch} InputProps={{
|
||||
endAdornment:
|
||||
<React.Fragment>
|
||||
<Tooltip title="Search">
|
||||
<IconButton onClick={submitSearch} size="large"><SearchIcon style={{color: theme.palette.info.main}}/></IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Resolve Viewable Errors">
|
||||
<IconButton onClick={props.resolveViewableErrors} size="large"><AutoFixHighIcon style={{color: theme.palette.success.main}}/></IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Resolve All Errors">
|
||||
<IconButton onClick={props.resolveAllErrors} size="large"><HealingIcon style={{color: theme.palette.success.main}}/></IconButton>
|
||||
</Tooltip>
|
||||
</React.Fragment>,
|
||||
style: {padding: 0}
|
||||
}}/>
|
||||
</Grid>
|
||||
<Grid style={{paddingTop: 0, paddingLeft: 0}} size={2}>
|
||||
<Select
|
||||
style={{width: "100%"}}
|
||||
value={level}
|
||||
onChange={handleLevelValueChange}
|
||||
>
|
||||
{
|
||||
levelOptions.map((opt, i) => (
|
||||
<MenuItem key={"levelFilter" + opt} value={opt}>{opt}</MenuItem>
|
||||
))
|
||||
}
|
||||
</Select>
|
||||
</Grid>
|
||||
<MythicPageBody>
|
||||
<Grid container spacing={1} style={{ marginTop: "0px"}}>
|
||||
<Grid style={{paddingTop: 0}} size={10}>
|
||||
<MythicTextField placeholder="Search..." value={search} marginBottom={"0px"} marginTop={"0px"}
|
||||
onChange={handleSearchValueChange} onEnter={submitSearch} InputProps={{
|
||||
endAdornment:
|
||||
<React.Fragment>
|
||||
<Tooltip title="Search">
|
||||
<IconButton onClick={submitSearch} size="large"><SearchIcon style={{color: theme.palette.info.main}}/></IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Resolve Viewable Errors">
|
||||
<IconButton onClick={props.resolveViewableErrors} size="large"><AutoFixHighIcon style={{color: theme.palette.success.main}}/></IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Resolve All Errors">
|
||||
<IconButton onClick={props.resolveAllErrors} size="large"><HealingIcon style={{color: theme.palette.success.main}}/></IconButton>
|
||||
</Tooltip>
|
||||
</React.Fragment>,
|
||||
style: {padding: 0}
|
||||
}}/>
|
||||
</Grid>
|
||||
</Paper>
|
||||
<Grid style={{paddingTop: 0, paddingLeft: 0}} size={2}>
|
||||
<Select
|
||||
style={{width: "100%"}}
|
||||
value={level}
|
||||
onChange={handleLevelValueChange}
|
||||
>
|
||||
{
|
||||
levelOptions.map((opt, i) => (
|
||||
<MenuItem key={"levelFilter" + opt} value={opt}>{opt}</MenuItem>
|
||||
))
|
||||
}
|
||||
</Select>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<div style={{display: "flex", flexDirection: "column", overflowY: "auto", flexGrow: 1, overflowX: "Hidden"}}>
|
||||
<EventList
|
||||
onUpdateResolution={props.onUpdateResolution}
|
||||
@@ -98,6 +99,6 @@ export function EventFeedTable(props){
|
||||
siblingCount={1} onChange={props.onChangePage} showFirstButton={true} showLastButton={true} style={{padding: "20px"}}/>
|
||||
<Typography style={{paddingLeft: "10px"}}>Total Results: {props.pageData.totalCount}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</MythicPageBody>
|
||||
);
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useQuery, gql} from '@apollo/client';
|
||||
import LinearProgress from '@mui/material/LinearProgress';
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
import TableCell from '@mui/material/TableCell';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import TableHead from '@mui/material/TableHead';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import {useTheme} from '@mui/material/styles';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
import { MythicStyledTooltip } from '../../MythicComponents/MythicStyledTooltip';
|
||||
|
||||
const GetC2ProfilesAndPayloadTypes = gql`
|
||||
query GetC2AndPayloadType {
|
||||
c2profile(where: {deleted: {_eq: false}}) {
|
||||
name
|
||||
id
|
||||
container_running
|
||||
}
|
||||
payloadtype(where: {deleted: {_eq: false}, agent_type: {_eq: "agent"}}) {
|
||||
name
|
||||
wrapper
|
||||
id
|
||||
container_running
|
||||
payloadtypec2profiles {
|
||||
c2profile {
|
||||
name
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
wrappers: payloadtype(where: {deleted: {_eq: false}, agent_type: {_eq: "agent"}, wrapper: {_eq: true}}) {
|
||||
name
|
||||
wrapper
|
||||
id
|
||||
container_running
|
||||
wrap_these_payload_types {
|
||||
wrapped {
|
||||
wrapper
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function AgentC2Overview(props){
|
||||
const theme = useTheme();
|
||||
const [c2Profiles, setC2Profiles] = React.useState([]);
|
||||
const [payloadTypeRows, setPayloadTypeRows] = React.useState([]);
|
||||
const [payloadTypeRowsNoWrappers, setPayloadTypeRowsNoWrappers] = React.useState([]);
|
||||
const [wrappers, setWrappers] = React.useState([]);
|
||||
const { loading } = useQuery(GetC2ProfilesAndPayloadTypes, {fetchPolicy: "network-only",
|
||||
onCompleted: (data) => {
|
||||
const c2Headers = data.c2profile.map( (c2) => c2.name);
|
||||
const payloadRows = data.payloadtype.map( (payload) => {
|
||||
const payloadc2 = payload.payloadtypec2profiles.map( (c2) => {
|
||||
return c2.c2profile.name;
|
||||
})
|
||||
return {name: payload.name, payloadtypec2profiles: payloadc2, wrapper: payload.wrapper };
|
||||
});
|
||||
|
||||
c2Headers.sort();
|
||||
payloadRows.sort( (a,b) => a.name < b.name ? -1 : 1);
|
||||
const payloadTypeNoWrappers = payloadRows.filter( p => !p.wrapper);
|
||||
const wrapperRows = data.wrappers.map( (payload) => {
|
||||
const wrapped = payload.wrap_these_payload_types.map( (w) => {
|
||||
return w.wrapped.name;
|
||||
});
|
||||
return {name: payload.name, wrapped}
|
||||
});
|
||||
wrapperRows.sort( (a,b) => a.name < b.name ? -1 : 1);
|
||||
setWrappers(wrapperRows);
|
||||
setC2Profiles(c2Headers);
|
||||
setPayloadTypeRows(payloadRows);
|
||||
setPayloadTypeRowsNoWrappers(payloadTypeNoWrappers);
|
||||
},
|
||||
onError: (data) => {
|
||||
|
||||
}
|
||||
});
|
||||
if (loading) {
|
||||
return <LinearProgress />;
|
||||
}
|
||||
return (
|
||||
<div style={{ marginTop: "10px", marginRight: "5px"}}>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main, marginBottom: "5px", marginTop: "10px", marginRight: "5px"}} variant={"elevation"}>
|
||||
<Typography variant="h3" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
|
||||
Agent / C2 Overview
|
||||
</Typography>
|
||||
</Paper>
|
||||
<TableContainer component={Paper} className="mythicElement">
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow hover>
|
||||
<TableCell></TableCell>
|
||||
{c2Profiles.map( (c2) => (
|
||||
<TableCell key={c2}>{c2}</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{payloadTypeRowsNoWrappers.map( (payload) => (
|
||||
<TableRow key={payload.name} hover>
|
||||
<TableCell>{payload.name}</TableCell>
|
||||
{c2Profiles.map( (c2) => (
|
||||
<TableCell key={'payload' + c2}>
|
||||
{payload.payloadtypec2profiles.includes(c2) ?
|
||||
<MythicStyledTooltip title={payload.name + " supports " + c2}>
|
||||
<CheckCircleIcon color="success"/>
|
||||
</MythicStyledTooltip>
|
||||
: ""}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
{wrappers.length > 0 &&
|
||||
<TableContainer component={Paper} className="mythicElement" style={{marginTop: "10px"}}>
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow hover>
|
||||
<TableCell></TableCell>
|
||||
{payloadTypeRows.map( (pt) => (
|
||||
<TableCell key={'wrapped' + pt.name}>{pt.name}</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{wrappers.map( (payload) => (
|
||||
<TableRow key={'wrapper' + payload.name} hover>
|
||||
<TableCell>{payload.name}</TableCell>
|
||||
{payloadTypeRows.map( (wr) => (
|
||||
<TableCell key={'payload' + wr.name}>
|
||||
{payload.wrapped.includes(wr.name) ?
|
||||
<MythicStyledTooltip title={payload.name + " wraps " + wr.name}>
|
||||
<CheckCircleIcon color="success"/>
|
||||
</MythicStyledTooltip>
|
||||
: ""}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -57,6 +57,7 @@ import {copyStringToClipboard} from "../../utilities/Clipboard";
|
||||
import MythicStyledTableCell from "../../MythicComponents/MythicTableCell";
|
||||
import {faCopy} from '@fortawesome/free-solid-svg-icons';
|
||||
import MythicTextField from "../../MythicComponents/MythicTextField";
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
|
||||
const LeadDashboardQuery = gql`
|
||||
${taskingDataFragment}
|
||||
@@ -1521,13 +1522,7 @@ const Top10RecentScreenshotsDashboardElement = ({me, data, editing, removeElemen
|
||||
<>
|
||||
<TableDataCard title={"Recent Screenshots"}
|
||||
tableHead={
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<MythicTableCell></MythicTableCell>
|
||||
<MythicTableCell></MythicTableCell>
|
||||
<MythicTableCell></MythicTableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
null
|
||||
}
|
||||
tableBody={
|
||||
<TableBody>
|
||||
@@ -1594,11 +1589,7 @@ const Top10RecentTasksDashboardElement = ({me, data, editing, removeElement}) =>
|
||||
<>
|
||||
<TableDataCard title={"Recent Tasking"}
|
||||
tableHead={
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<MythicTableCell></MythicTableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
null
|
||||
}
|
||||
tableBody={
|
||||
<TableBody>
|
||||
@@ -1714,20 +1705,20 @@ const OperatorDashboard = ({me, setLoading, loading}) => {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px", marginTop: "5px"}}>
|
||||
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem",}}>
|
||||
<ActiveCallbacksDashboardElement me={me} data={analysisData}/>
|
||||
<Top10RecentPayloadsDashboardElement me={me} data={analysisData}/>
|
||||
<Top10RecentWorkflowsDashboardElement me={me} data={analysisData}/>
|
||||
</div>
|
||||
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px"}}>
|
||||
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem"}}>
|
||||
<ProxyUsageDashboardElement me={me} data={analysisData}/>
|
||||
<Top10RecentFileDownloadsDashboardElement me={me} data={analysisData}/>
|
||||
<Top10RecentCredentialsDashboardElement me={me} data={analysisData}/>
|
||||
</div>
|
||||
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px"}}>
|
||||
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem"}}>
|
||||
<Top10RecentTasksDashboardElement me={me} data={analysisData}/>
|
||||
</div>
|
||||
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px"}}>
|
||||
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem"}}>
|
||||
<Top10RecentScreenshotsDashboardElement me={me} data={analysisData}/>
|
||||
<MyOperationsDashboardElement me={me} data={analysisData} reloadDashboard={reloadDashboard}/>
|
||||
</div>
|
||||
@@ -1754,16 +1745,16 @@ const LeadDashboard = ({me, setLoading, loading}) => {
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px", marginTop: "5px"}}>
|
||||
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem",}}>
|
||||
<ActiveCallbacksDashboardElement me={me} data={analysisData} />
|
||||
<Top10CommandStatsDashboardElement me={me} data={analysisData} />
|
||||
<Top10UserContextsDashboardElement me={me} data={analysisData} />
|
||||
<Top10HostContextsDashboardElement me={me} data={analysisData} />
|
||||
</div>
|
||||
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px"}}>
|
||||
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem"}}>
|
||||
<ActivityPerDayDashboardElement me={me} data={analysisData} />
|
||||
</div>
|
||||
<div style={{display: "flex", marginLeft: "5px"}}>
|
||||
<div style={{display: "flex", marginLeft: "0.5rem"}}>
|
||||
<TaskStatusDashboardElement me={me} data={analysisData} />
|
||||
<OperatorActivityDashboardElement me={me} data={analysisData} />
|
||||
<Top10ArtifactsDashboardElement me={me} data={analysisData} />
|
||||
@@ -1932,7 +1923,7 @@ const CustomDashboard = ({me, setLoading, loading, editing}) => {
|
||||
<div style={{height: "100%", overflowY: "auto"}}>
|
||||
{dashboards.map((d, i) => (
|
||||
<div key={"dashboardRow" + i}
|
||||
style={{display: "flex", marginLeft: "5px", marginTop: "5px", borderRadius: "4px", }}>
|
||||
style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem", borderRadius: "5px", }}>
|
||||
{editing &&
|
||||
<div style={{display: "flex", flexDirection: "column", width: "40px", justifyContent: "center"}}>
|
||||
<MythicStyledTooltip title={"Add New Chart To This Row"}>
|
||||
@@ -1976,7 +1967,6 @@ const CustomDashboard = ({me, setLoading, loading, editing}) => {
|
||||
}
|
||||
|
||||
export function CallbacksCard({me}) {
|
||||
const theme = useTheme();
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [editing, setEditing] = React.useState(false);
|
||||
const initialDashboardView = GetMythicSetting({
|
||||
@@ -1992,24 +1982,20 @@ export function CallbacksCard({me}) {
|
||||
setLoading(true);
|
||||
}
|
||||
return (
|
||||
<div style={{width: "100%", height: "100%", display: "flex", flexDirection: "column", position: "relative"}}>
|
||||
<Paper elevation={5} variant={"elevation"} style={{
|
||||
backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main,
|
||||
display: "flex", alignItems: "center", justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "10px"}}>
|
||||
<>
|
||||
<MythicPageHeader title={
|
||||
<>
|
||||
Welcome <b>{me.user.username}</b>
|
||||
{me.user.current_operation_id === 0 ? null : (
|
||||
<>
|
||||
{" to"} <b>{me.user.current_operation}</b>'s Dashboard
|
||||
{" to"} <b>{me.user.current_operation}</b>'s Dashboard
|
||||
</>
|
||||
)}
|
||||
|
||||
</Typography>
|
||||
<span>
|
||||
</>
|
||||
}>
|
||||
<div style={{display: "flex", alignItems: "center"}}>
|
||||
<TextField
|
||||
style={{color: "white", marginTop: "5px", width: "10rem"}}
|
||||
style={{color: "white", width: "10rem", margin: "0.5rem"}}
|
||||
InputProps={{style: {color: "white"}}}
|
||||
size={"small"}
|
||||
select={true}
|
||||
@@ -2022,8 +2008,8 @@ export function CallbacksCard({me}) {
|
||||
<MenuItem key={"searchopt" + opt} value={opt}>{opt}</MenuItem>
|
||||
))
|
||||
}
|
||||
</TextField>
|
||||
<MythicStyledTooltip title={"Analyze Operation Data Again"}>
|
||||
</TextField>
|
||||
<MythicStyledTooltip title={"Analyze Operation Data Again"} tooltipStyle={{display: "inline-block"}}>
|
||||
<IconButton style={{color: "white"}} onClick={() => setLoading(true)}>
|
||||
<ReplayIcon />
|
||||
</IconButton>
|
||||
@@ -2035,8 +2021,8 @@ export function CallbacksCard({me}) {
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
}
|
||||
</span>
|
||||
</Paper>
|
||||
</div>
|
||||
</MythicPageHeader>
|
||||
<div style={{width: "100%", height: "100%", display: "flex", flexDirection: "column", position: "relative", overflowY: "auto"}}>
|
||||
{loading &&
|
||||
<div style={{
|
||||
@@ -2070,7 +2056,7 @@ export function CallbacksCard({me}) {
|
||||
me={me} loading={loading} setLoading={setLoading} editing={editing}
|
||||
/>}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
);
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
||||
import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
const normalColors = [
|
||||
'#09bdff',
|
||||
@@ -45,16 +46,17 @@ export const PieChartCard = ({
|
||||
const toggleLegend = () => {
|
||||
setShowLegend(!showLegend);
|
||||
}
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div style={{
|
||||
marginRight: "5px",
|
||||
<Paper variant={"elevation"} elevation={1} style={{
|
||||
marginRight: "0.5rem",
|
||||
width: width,
|
||||
height: "100%",
|
||||
border: "1px solid gray",
|
||||
borderRadius: "4px",
|
||||
border: `1px solid ${theme.borderColor}`,
|
||||
borderRadius: "5px",
|
||||
overflow: "hidden",
|
||||
}}>
|
||||
<h3 style={{marginTop: 0, marginLeft: "5px", marginBottom: 0, paddingBottom: 0,}}>
|
||||
<h3 style={{marginTop: 0, marginLeft: "0.5rem", marginBottom: 0, paddingBottom: 0,}}>
|
||||
{editing &&
|
||||
<span>
|
||||
<MythicStyledTooltip title={"Remove element"}>
|
||||
@@ -120,7 +122,7 @@ export const PieChartCard = ({
|
||||
}}>
|
||||
{innerElement}
|
||||
</PieChart>
|
||||
</div>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
function GaugePointer() {
|
||||
@@ -151,8 +153,7 @@ export const GaugeCard = ({data, width = "100%", additionalStyles, innerElement,
|
||||
right: 10,
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
}, colors = normalColors, onClick, title = "", editing, removeElement, customizeElement
|
||||
}) => {
|
||||
}, colors = normalColors, onClick, title = "", editing, removeElement, customizeElement }) => {
|
||||
const theme = useTheme();
|
||||
const getFillColor = () => {
|
||||
if(data['total'] === 0){return theme.palette.text.disabled}
|
||||
@@ -166,15 +167,15 @@ export const GaugeCard = ({data, width = "100%", additionalStyles, innerElement,
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div style={{
|
||||
marginRight: "5px",
|
||||
<Paper variant={"elevation"} elevation={1} style={{
|
||||
marginRight: "0.5rem",
|
||||
width: width,
|
||||
height: "100%",
|
||||
border: "1px solid gray",
|
||||
border: `1px solid ${theme.borderColor}`,
|
||||
borderRadius: "5px",
|
||||
overflow: "hidden",
|
||||
borderRadius: "4px",
|
||||
}}>
|
||||
<h3 style={{marginTop: 0, marginLeft: "5px", marginBottom: 0, paddingBottom: 0,}}>
|
||||
<h3 style={{marginTop: 0, marginLeft: "0.5rem", marginBottom: 0, paddingBottom: 0,}}>
|
||||
{editing &&
|
||||
<span>
|
||||
<MythicStyledTooltip title={"Remove element"}>
|
||||
@@ -208,21 +209,22 @@ export const GaugeCard = ({data, width = "100%", additionalStyles, innerElement,
|
||||
})}
|
||||
>
|
||||
</Gauge>
|
||||
</div>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
export const CallbackDataCard = ({mainTitle, secondTitle, mainElement, secondaryElement, width="100%",
|
||||
editing, removeElement}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div style={{
|
||||
marginRight: "5px",
|
||||
<Paper variant={"elevation"} elevation={1} style={{
|
||||
marginRight: "0.5rem",
|
||||
width: width,
|
||||
height: "100%",
|
||||
border: "1px solid gray",
|
||||
border: `1px solid ${theme.borderColor}`,
|
||||
borderRadius: "5px",
|
||||
overflow: "hidden",
|
||||
borderRadius: "4px",
|
||||
}} >
|
||||
<h5 style={{marginTop: 0, marginLeft: "5px", marginBottom: 0, paddingBottom: 0}}>
|
||||
<h2 style={{marginTop: 0, marginLeft: "0.5rem", marginBottom: 0, paddingBottom: 0}}>
|
||||
{editing &&
|
||||
<span >
|
||||
<MythicStyledTooltip title={"Remove element"}>
|
||||
@@ -233,7 +235,7 @@ export const CallbackDataCard = ({mainTitle, secondTitle, mainElement, secondary
|
||||
</span>
|
||||
}
|
||||
{mainTitle}
|
||||
</h5>
|
||||
</h2>
|
||||
<div style={{height: 180, cursor: "pointer"}}>
|
||||
<MythicStyledTooltip title={"Go to Active Callbacks"}>
|
||||
{mainElement}
|
||||
@@ -243,43 +245,46 @@ export const CallbackDataCard = ({mainTitle, secondTitle, mainElement, secondary
|
||||
{secondaryElement}
|
||||
</MythicStyledTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
export const TableDataCard = ({title, width = "100%", tableHead, tableBody, editing, removeElement, customizeElement}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div style={{
|
||||
marginRight: "5px",
|
||||
<Paper variant={"elevation"} elevation={1} style={{
|
||||
marginRight: "0.5rem",
|
||||
width: width,
|
||||
height: "100%",
|
||||
border: "1px solid gray",
|
||||
borderRadius: "4px",
|
||||
border: `1px solid ${theme.borderColor}`,
|
||||
borderRadius: "5px",
|
||||
overflow: "hidden",
|
||||
}}>
|
||||
<h3 style={{marginTop: 0, marginLeft: "5px", marginBottom: 0, paddingBottom: 0}}>
|
||||
<h3 style={{marginTop: 0, marginLeft: "0.5rem", marginBottom: 0, paddingBottom: 0}}>
|
||||
{editing &&
|
||||
<span >
|
||||
<MythicStyledTooltip title={"Remove element"}>
|
||||
<IconButton onClick={removeElement} >
|
||||
<DeleteIcon color={"error"}/>
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
</span>
|
||||
<MythicStyledTooltip title={"Remove element"}>
|
||||
<IconButton onClick={removeElement} >
|
||||
<DeleteIcon color={"error"}/>
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
</span>
|
||||
}
|
||||
{title}
|
||||
<span style={{float: "right", marginRight: "5px"}}>
|
||||
<span style={{float: "right", marginRight: "0.5rem"}}>
|
||||
{customizeElement}
|
||||
</span>
|
||||
</h3>
|
||||
<div style={{height: 200, overflowY: "auto"}}>
|
||||
<Table style={{ "maxWidth": "100%", "overflow": "auto"}} stickyHeader size="small">
|
||||
<div style={{height: 200, overflowY: "auto", width: "100%"}}>
|
||||
<Table style={{ maxWidth: "100%", overflow: "auto"}} stickyHeader size="small">
|
||||
{tableHead}
|
||||
{tableBody}
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
export const LineTimeChartCard = ({data, additionalStyles}) => {
|
||||
const theme = useTheme();
|
||||
const [value, setValue] = React.useState([0, 0]);
|
||||
const [range, setRange] = React.useState([0, 0]);
|
||||
React.useEffect( () => {
|
||||
@@ -309,12 +314,12 @@ export const LineTimeChartCard = ({data, additionalStyles}) => {
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div style={{
|
||||
marginBottom: "5px",
|
||||
marginTop: "10px",
|
||||
<Paper variant={"elevation"} elevation={1} style={{
|
||||
marginRight: "0.5rem",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
border: "1px solid gray",
|
||||
border: `1px solid ${theme.borderColor}`,
|
||||
borderRadius: "5px",
|
||||
overflow: "hidden",
|
||||
}} >
|
||||
<Typography variant={"h3"} style={{margin: 0, padding: 0, position: "relative", left: "30%"}}>
|
||||
@@ -369,11 +374,12 @@ export const LineTimeChartCard = ({data, additionalStyles}) => {
|
||||
max={range[1]}
|
||||
sx={{ mt: 2, width: "80%", left: "10%" }}
|
||||
/>
|
||||
</div>
|
||||
</Paper>
|
||||
|
||||
)
|
||||
}
|
||||
export const LineTimeMultiChartCard = ({data, additionalStyles, colors=normalColors, view_utc_time, editing, removeElement, customizeElement}) => {
|
||||
const theme = useTheme();
|
||||
const [value, setValue] = React.useState([0, 0]);
|
||||
const [range, setRange] = React.useState([0, 0]);
|
||||
React.useEffect( () => {
|
||||
@@ -423,13 +429,13 @@ export const LineTimeMultiChartCard = ({data, additionalStyles, colors=normalCol
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div style={{
|
||||
<Paper variant={"elevation"} elevation={1} style={{
|
||||
width: "100%",
|
||||
marginRight: "0.5rem",
|
||||
height: "100%",
|
||||
marginRight: "5px",
|
||||
border: "1px solid gray",
|
||||
border: `1px solid ${theme.borderColor}`,
|
||||
borderRadius: "5px",
|
||||
overflow: "hidden",
|
||||
borderRadius: "4px",
|
||||
}} >
|
||||
<Typography variant={"h3"} style={{margin: 0, padding: 0, position: "relative", left: "30%"}}>
|
||||
{editing &&
|
||||
@@ -500,7 +506,7 @@ export const LineTimeMultiChartCard = ({data, additionalStyles, colors=normalCol
|
||||
max={range[1]}
|
||||
sx={{ mt: 2, width: "80%", left: "10%" }}
|
||||
/>
|
||||
</div>
|
||||
</Paper>
|
||||
|
||||
)
|
||||
}
|
||||
@@ -509,12 +515,14 @@ export const StackedBarChartCard = ({data, labels, title, width="100%", hidden,
|
||||
top: 40,
|
||||
bottom: 10,
|
||||
}}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div style={{
|
||||
marginRight: "5px",
|
||||
<Paper variant={"elevation"} elevation={1} style={{
|
||||
marginRight: "0.5rem",
|
||||
width: width,
|
||||
height: "100%",
|
||||
border: "1px solid gray",
|
||||
border: `1px solid ${theme.borderColor}`,
|
||||
borderRadius: "5px",
|
||||
overflow: "hidden",
|
||||
}} >
|
||||
<h3 style={{marginTop: 0, marginLeft: "5px", marginBottom: 0, paddingBottom: 0, position: "absolute"}}>
|
||||
@@ -544,6 +552,6 @@ export const StackedBarChartCard = ({data, labels, title, width="100%", hidden,
|
||||
}
|
||||
}
|
||||
}} />
|
||||
</div>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import React from 'react';
|
||||
import {CallbacksCard} from "./CallbacksCard";
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
|
||||
|
||||
export function Home({me}) {
|
||||
return (
|
||||
<div style={{width: "100%", height: "100%", display: "flex"}}>
|
||||
<MythicPageBody>
|
||||
<CallbacksCard me={me} />
|
||||
</div>
|
||||
</MythicPageBody>
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { Backdrop } from '@mui/material';
|
||||
import {CircularProgress} from '@mui/material';
|
||||
import { MythicSelectFromListDialog } from '../../MythicComponents/MythicSelectFromListDialog';
|
||||
import { MythicDialog } from '../../MythicComponents/MythicDialog';
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
|
||||
const Get_MITREATTACK = gql`
|
||||
query GetMitreAttack {
|
||||
@@ -367,7 +368,7 @@ export function MitreAttack({me}){
|
||||
getTasksFilteredByTag({variables: {tasks: taskIds}})
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
<MythicPageBody>
|
||||
<Backdrop open={backdropOpen} style={{zIndex: 2, position: "absolute"}} invisible={false}>
|
||||
<CircularProgress color="inherit" disableShrink />
|
||||
</Backdrop>
|
||||
@@ -388,7 +389,7 @@ export function MitreAttack({me}){
|
||||
action={"select"} identifier={"cmd"} display={"cmd"}/>}
|
||||
/>
|
||||
}
|
||||
</React.Fragment>
|
||||
</MythicPageBody>
|
||||
|
||||
);
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import {CircularProgress} from '@mui/material';
|
||||
import { MythicDisplayTextDialog} from '../../MythicComponents/MythicDisplayTextDialog';
|
||||
import { MythicDialog } from '../../MythicComponents/MythicDialog';
|
||||
import { SelectPayloadTypeDialog } from './SelectPayloadTypeDialog';
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
|
||||
|
||||
export function MitreGrid({entries, onGetCommands, onGetTasks, onGetCommandsFiltered, onGetTasksFiltered, onFilterByTags, showCountGrouping}){
|
||||
@@ -23,12 +24,8 @@ export function MitreGrid({entries, onGetCommands, onGetTasks, onGetCommandsFilt
|
||||
const [backdropOpen, setBackdropOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<div style={{display: "flex", flexDirection: "column", height: "100%"}}>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main,
|
||||
marginBottom: "5px", marginRight: "5px"}} variant={"elevation"}>
|
||||
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
|
||||
{"MITRE ATT&CK Mappings"}
|
||||
</Typography>
|
||||
<>
|
||||
<MythicPageHeader title={"MITRE ATT&CK Mappings"}>
|
||||
<PoperDropdown onGetCommands={onGetCommands}
|
||||
onGetTasks={onGetTasks}
|
||||
onGetCommandsFiltered={onGetCommandsFiltered}
|
||||
@@ -38,7 +35,7 @@ export function MitreGrid({entries, onGetCommands, onGetTasks, onGetCommandsFilt
|
||||
showCountGrouping={showCountGrouping}
|
||||
entries={entries}
|
||||
/>
|
||||
</Paper>
|
||||
</MythicPageHeader>
|
||||
|
||||
<div style={{display: "flex", flexGrow: 1, overflow: "auto"}}>
|
||||
{backdropOpen && <Backdrop open={backdropOpen} style={{zIndex: 2, position: "absolute"}} invisible={false}>
|
||||
@@ -47,7 +44,7 @@ export function MitreGrid({entries, onGetCommands, onGetTasks, onGetCommandsFilt
|
||||
}
|
||||
<MitreGridDisplay entries={entries} showCountGrouping={showCountGrouping} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import {MythicConfirmDialog} from '../../MythicComponents/MythicConfirmDialog';
|
||||
import TuneIcon from '@mui/icons-material/Tune';
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
|
||||
const newBlockListEntry = gql`
|
||||
mutation newBlockListsEntries($entries: [disabledcommandsprofile_insert_input!]!) {
|
||||
@@ -174,14 +175,11 @@ export function CommandBlockListTable(props){
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
|
||||
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
|
||||
Command Block Lists
|
||||
</Typography>
|
||||
<>
|
||||
<MythicPageHeader title={"Command Block Lists"}>
|
||||
<Button size="small"
|
||||
onClick={()=>{setOpenNewDialog(true);}}
|
||||
style={{marginRight: "10px", float: "right", color: "white"}}
|
||||
style={{color: "white", whiteSpace: "nowrap"}}
|
||||
startIcon={<AddCircleIcon color="success" style={{backgroundColor: "white", borderRadius: "10px"}}/>}
|
||||
>New Block List</Button>
|
||||
{openNew &&
|
||||
@@ -191,7 +189,7 @@ export function CommandBlockListTable(props){
|
||||
/>}
|
||||
/>
|
||||
}
|
||||
</Paper>
|
||||
</MythicPageHeader>
|
||||
<TableContainer className="mythicElement">
|
||||
<Table size="small" style={{"tableLayout": "fixed", "maxWidth": "calc(100vw)", "overflow": "scroll"}}>
|
||||
<TableHead>
|
||||
@@ -211,7 +209,7 @@ export function CommandBlockListTable(props){
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</React.Fragment>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import { MythicStyledTooltip } from '../../MythicComponents/MythicStyledTooltip'
|
||||
import { IconButton } from '@mui/material';
|
||||
import PersonAddIcon from '@mui/icons-material/PersonAdd';
|
||||
import {meState} from "../../../cache";
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
|
||||
const newOperatorMutation = gql`
|
||||
mutation NewOperator($username: String!, $password: String!) {
|
||||
@@ -143,105 +144,100 @@ export function OperationTable(props){
|
||||
newOperation({variables: {name: operation_name}})
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
|
||||
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
|
||||
Operations
|
||||
</Typography>
|
||||
{showDeleted ? (
|
||||
<MythicStyledTooltip title={"Hide Deleted Operations"} tooltipStyle={{float: "right"}}>
|
||||
<IconButton size="small" style={{float: "right", marginRight: "10px"}}
|
||||
variant="contained" onClick={() => setShowDeleted(!showDeleted)}>
|
||||
<VisibilityIcon />
|
||||
<>
|
||||
<MythicPageHeader title={"Operations"}>
|
||||
<Button size="small" onClick={() => {setOpenNewOperationDialog(true);}}
|
||||
style={{ color: "white", whiteSpace: "nowrap"}}
|
||||
startIcon={<AddCircleIcon color="success" style={{backgroundColor: "white", borderRadius: "10px"}}/>} >
|
||||
New Operation
|
||||
</Button>
|
||||
<MythicStyledTooltip title={"Create new operator"} tooltipStyle={{}}>
|
||||
<IconButton size="small"
|
||||
onClick={()=>{setOpenNewOperatorDialog(true);}}
|
||||
style={{}}
|
||||
variant="contained">
|
||||
<PersonAddIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
|
||||
) : (
|
||||
<MythicStyledTooltip title={"Show Deleted Operations"} tooltipStyle={{float: "right"}}>
|
||||
<IconButton size="small" style={{float: "right", marginRight: "10px"}}
|
||||
variant="contained" onClick={() => setShowDeleted(!showDeleted)} >
|
||||
<VisibilityOffIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
)}
|
||||
<MythicStyledTooltip title={"Create new operator"} tooltipStyle={{float: "right"}}>
|
||||
<IconButton size="small"
|
||||
onClick={()=>{setOpenNewOperatorDialog(true);}}
|
||||
style={{marginRight: "10px", float: "right"}}
|
||||
variant="contained">
|
||||
<PersonAddIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
<Button size="small" onClick={() => {setOpenNewOperationDialog(true);}}
|
||||
style={{marginRight: "20px", float: "right", color: "white"}}
|
||||
startIcon={<AddCircleIcon color="success" style={{backgroundColor: "white", borderRadius: "10px"}}/>} >
|
||||
New Operation
|
||||
</Button>
|
||||
{showDeleted ? (
|
||||
<MythicStyledTooltip title={"Hide Deleted Operations"} tooltipStyle={{}}>
|
||||
<IconButton size="small" style={{}}
|
||||
variant="contained" onClick={() => setShowDeleted(!showDeleted)}>
|
||||
<VisibilityIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
) : (
|
||||
<MythicStyledTooltip title={"Show Deleted Operations"} tooltipStyle={{}}>
|
||||
<IconButton size="small" style={{}}
|
||||
variant="contained" onClick={() => setShowDeleted(!showDeleted)} >
|
||||
<VisibilityOffIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
)}
|
||||
{openNewOperator &&
|
||||
<MythicDialog open={openNewOperator}
|
||||
onClose={()=>{setOpenNewOperatorDialog(false);}}
|
||||
innerDialog={<SettingsOperatorDialog title="New Operator" onAccept={onSubmitNewOperator} handleClose={()=>{setOpenNewOperatorDialog(false);}} {...props}/>}
|
||||
/>
|
||||
}
|
||||
{openNewOperation &&
|
||||
<MythicDialog
|
||||
fullWidth={true}
|
||||
open={openNewOperation}
|
||||
onClose={() => {setOpenNewOperationDialog(false);}}
|
||||
innerDialog={
|
||||
<MythicModifyStringDialog title={"New Operation's Name"}
|
||||
onClose={() => {setOpenNewOperationDialog(false);}}
|
||||
value={""}
|
||||
onSubmit={onSubmitNewOperation}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
|
||||
{openNewOperator &&
|
||||
<MythicDialog open={openNewOperator}
|
||||
onClose={()=>{setOpenNewOperatorDialog(false);}}
|
||||
innerDialog={<SettingsOperatorDialog title="New Operator" onAccept={onSubmitNewOperator} handleClose={()=>{setOpenNewOperatorDialog(false);}} {...props}/>}
|
||||
/>
|
||||
}
|
||||
{openNewOperation &&
|
||||
<MythicDialog
|
||||
fullWidth={true}
|
||||
open={openNewOperation}
|
||||
onClose={() => {setOpenNewOperationDialog(false);}}
|
||||
innerDialog={
|
||||
<MythicModifyStringDialog title={"New Operation's Name"}
|
||||
onClose={() => {setOpenNewOperationDialog(false);}}
|
||||
value={""}
|
||||
onSubmit={onSubmitNewOperation}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
|
||||
</Paper>
|
||||
<TableContainer className="mythicElement">
|
||||
{props.operations.length === 0 &&
|
||||
<div style={{display: "flex", flexDirection: "column", flexGrow: 1}}>
|
||||
<div style={{
|
||||
position: "absolute",
|
||||
left: "35%",
|
||||
top: "50%"
|
||||
}}>
|
||||
{"No Operations available!"}<br/>
|
||||
{"Ask a Mythic admin or operation lead to add you to an operation."}
|
||||
</MythicPageHeader>
|
||||
<TableContainer className="mythicElement">
|
||||
{props.operations.length === 0 &&
|
||||
<div style={{display: "flex", flexDirection: "column", flexGrow: 1}}>
|
||||
<div style={{
|
||||
position: "absolute",
|
||||
left: "35%",
|
||||
top: "50%"
|
||||
}}>
|
||||
{"No Operations available!"}<br/>
|
||||
{"Ask a Mythic admin or operation lead to add you to an operation."}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<Table size="small" style={{"tableLayout": "fixed", "overflow": "scroll"}}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={{width: "2rem"}}></TableCell>
|
||||
<TableCell style={{width: "8rem"}}>Configure</TableCell>
|
||||
<TableCell style={{width: "8rem"}}>Operators</TableCell>
|
||||
<TableCell>Operation Name</TableCell>
|
||||
<TableCell>Operation Admin</TableCell>
|
||||
<TableCell style={{width: "8rem"}}>Analysis</TableCell>
|
||||
<TableCell style={{width: "11rem"}}>Operation Status</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{props.operations.map((op) => (
|
||||
showDeleted || !op.deleted ? (
|
||||
<OperationTableRow
|
||||
me={props.me}
|
||||
key={"operation" + op.id}
|
||||
onUpdateOperation={onUpdateOperation}
|
||||
onUpdateCurrentOperation={props.onUpdateCurrentOperation}
|
||||
updateDeleted={props.updateDeleted}
|
||||
{...op} operator={props.operator}
|
||||
/>
|
||||
) : null
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</React.Fragment>
|
||||
}
|
||||
<Table size="small" style={{"tableLayout": "fixed", "overflow": "scroll"}}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={{width: "2rem"}}></TableCell>
|
||||
<TableCell style={{width: "8rem"}}>Configure</TableCell>
|
||||
<TableCell style={{width: "8rem"}}>Operators</TableCell>
|
||||
<TableCell>Operation Name</TableCell>
|
||||
<TableCell>Operation Admin</TableCell>
|
||||
<TableCell style={{width: "8rem"}}>Analysis</TableCell>
|
||||
<TableCell style={{width: "11rem"}}>Operation Status</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{props.operations.map((op) => (
|
||||
showDeleted || !op.deleted ? (
|
||||
<OperationTableRow
|
||||
me={props.me}
|
||||
key={"operation" + op.id}
|
||||
onUpdateOperation={onUpdateOperation}
|
||||
onUpdateCurrentOperation={props.onUpdateCurrentOperation}
|
||||
updateDeleted={props.updateDeleted}
|
||||
{...op} operator={props.operator}
|
||||
/>
|
||||
) : null
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {useQuery, gql, useLazyQuery} from '@apollo/client';
|
||||
import {CommandBlockListTable} from './CommandBlockListTable';
|
||||
import { snackActions } from '../../utilities/Snackbar';
|
||||
import {useMythicLazyQuery} from "../../utilities/useMythicLazyQuery";
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
|
||||
const GET_Operations = gql`
|
||||
query GetOperations {
|
||||
@@ -114,13 +115,13 @@ export function Operations(props){
|
||||
getBlockLists().then(({data}) => getBlockListsSuccess(data)).catch(({data}) => getBlockListsError(data));
|
||||
}, []);
|
||||
return (
|
||||
<div style={{ height: "100%", display: "flex", flexDirection: "column"}}>
|
||||
<MythicPageBody>
|
||||
<OperationTable operations={operations}
|
||||
onUpdateOperation={onUpdateOperation}
|
||||
onNewOperation={onNewOperation} me={props.me}
|
||||
onUpdateCurrentOperation={onUpdateCurrentOperation}
|
||||
updateDeleted={updateDeleted}/>
|
||||
<CommandBlockListTable blockLists={blockLists} me={props.me} />
|
||||
</div>
|
||||
</MythicPageBody>
|
||||
);
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||
import { IconButton } from '@mui/material';
|
||||
import {useTheme} from '@mui/material/styles';
|
||||
import {CustomBrowserRow} from "./CustomBrowserCard";
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
|
||||
const SUB_Payload_Types = gql`
|
||||
subscription getPayloadTypesSubscription {
|
||||
@@ -215,11 +217,8 @@ export function PayloadTypesC2Profiles({me}){
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div style={{ height: "100%", display: "flex", flexDirection: "column", width: "100%"}}>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
|
||||
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
|
||||
Installed Services
|
||||
</Typography>
|
||||
<MythicPageBody>
|
||||
<MythicPageHeader title={"Installed Services"}>
|
||||
{showDeleted ? (
|
||||
<MythicStyledTooltip title={"Hide Deleted Services"} tooltipStyle={{float: "right"}}>
|
||||
<IconButton size="small" style={{float: "right"}} variant="contained" onClick={() => setShowDeleted(!showDeleted)}><VisibilityIcon /></IconButton>
|
||||
@@ -230,7 +229,7 @@ export function PayloadTypesC2Profiles({me}){
|
||||
<IconButton size="small" style={{float: "right"}} variant="contained" onClick={() => setShowDeleted(!showDeleted)} ><VisibilityOffIcon /></IconButton>
|
||||
</MythicStyledTooltip>
|
||||
)}
|
||||
</Paper>
|
||||
</MythicPageHeader>
|
||||
<AppBar position="static" color="default" className={"no-box-shadow"}>
|
||||
<Tabs
|
||||
value={value}
|
||||
@@ -285,7 +284,7 @@ export function PayloadTypesC2Profiles({me}){
|
||||
{
|
||||
getTabComponent()
|
||||
}
|
||||
</div>
|
||||
</MythicPageBody>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { PayloadsTable } from './PayloadsTable';
|
||||
import {useMutation, gql, useSubscription, useQuery, useLazyQuery} from '@apollo/client';
|
||||
import { snackActions } from '../../utilities/Snackbar';
|
||||
import {getSkewedNow} from "../../utilities/Time";
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
|
||||
export const payloadFragment = gql`
|
||||
fragment payloadData on payload {
|
||||
@@ -325,7 +326,7 @@ export function Payloads(props){
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
return (
|
||||
<div style={{display: "flex", height: "100%", flexDirection: "column", position: "relative"}}>
|
||||
<MythicPageBody>
|
||||
|
||||
<PayloadsTable pageData={pageData} me={props.me}
|
||||
openBackdrop={openBackdrop}
|
||||
@@ -338,7 +339,7 @@ export function Payloads(props){
|
||||
onCallbacksAllowedChanged={onCallbacksAllowedChanged}
|
||||
onChangeShowAutogenerated={onChangeShowAutogenerated}
|
||||
/>
|
||||
</div>
|
||||
</MythicPageBody>
|
||||
|
||||
);
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import ClickAwayListener from '@mui/material/ClickAwayListener';
|
||||
import {useNavigate} from 'react-router-dom';
|
||||
import Pagination from '@mui/material/Pagination';
|
||||
import { Backdrop, CircularProgress } from '@mui/material';
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
|
||||
|
||||
export function PayloadsTable({payload, onDeletePayload, onUpdateCallbackAlert, onRestorePayload, me,
|
||||
@@ -74,70 +75,68 @@ export function PayloadsTable({payload, onDeletePayload, onUpdateCallbackAlert,
|
||||
setDropdownOpen(false);
|
||||
};
|
||||
return (
|
||||
<div style={{display: "flex", flexDirection: "column", height: "100%"}}>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
|
||||
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
|
||||
Payloads
|
||||
</Typography>
|
||||
<>
|
||||
<MythicPageHeader title={"Payloads"}>
|
||||
|
||||
<ButtonGroup variant="text" ref={dropdownAnchorRef} aria-label="split button" style={{float: "right", marginRight: "10px", color: "white"}}>
|
||||
<Button size="small" style={{color: "white"}} aria-controls={dropdownOpen ? 'split-button-menu' : undefined}
|
||||
aria-expanded={dropdownOpen ? 'true' : undefined}
|
||||
aria-haspopup="menu"
|
||||
onClick={() => setDropdownOpen(!dropdownOpen)}>
|
||||
Actions <ArrowDropDownIcon />
|
||||
aria-expanded={dropdownOpen ? 'true' : undefined}
|
||||
aria-haspopup="menu"
|
||||
onClick={() => setDropdownOpen(!dropdownOpen)}>
|
||||
Actions <ArrowDropDownIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<Popper open={dropdownOpen} anchorEl={dropdownAnchorRef.current} role={undefined} transition disablePortal style={{zIndex: 10}}>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Grow
|
||||
{...TransitionProps}
|
||||
style={{
|
||||
transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom',
|
||||
}}
|
||||
>
|
||||
<Paper className={"dropdownMenuColored"}>
|
||||
<ClickAwayListener onClickAway={() => setDropdownOpen(false)}>
|
||||
<MenuList id="split-button-menu">
|
||||
{dropDownOptions.map((option, index) => (
|
||||
<MenuItem
|
||||
key={option.name}
|
||||
onClick={(event) => handleMenuItemClick(event, index)}
|
||||
>
|
||||
{option.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Grow>
|
||||
)}
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Grow
|
||||
{...TransitionProps}
|
||||
style={{
|
||||
transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom',
|
||||
}}
|
||||
>
|
||||
<Paper className={"dropdownMenuColored"}>
|
||||
<ClickAwayListener onClickAway={() => setDropdownOpen(false)}>
|
||||
<MenuList id="split-button-menu">
|
||||
{dropDownOptions.map((option, index) => (
|
||||
<MenuItem
|
||||
key={option.name}
|
||||
onClick={(event) => handleMenuItemClick(event, index)}
|
||||
>
|
||||
{option.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Grow>
|
||||
)}
|
||||
</Popper>
|
||||
{openPayloadImport &&
|
||||
<MythicDialog fullWidth={true} maxWidth="sm" open={openPayloadImport}
|
||||
onClose={()=>{setOpenPayloadImport(false);}}
|
||||
innerDialog={<ImportPayloadConfigDialog onClose={()=>{setOpenPayloadImport(false);}} />}
|
||||
onClose={()=>{setOpenPayloadImport(false);}}
|
||||
innerDialog={<ImportPayloadConfigDialog onClose={()=>{setOpenPayloadImport(false);}} />}
|
||||
/>
|
||||
}
|
||||
</Paper>
|
||||
</MythicPageHeader>
|
||||
<div style={{flexGrow: 1, overflowY: "auto", height: "100%", position: "relative"}}>
|
||||
{openBackdrop &&
|
||||
<Backdrop open={openBackdrop} style={{zIndex: 2000, position: "absolute"}}>
|
||||
<CircularProgress color="inherit" disableShrink />
|
||||
</Backdrop>
|
||||
}
|
||||
<Table stickyHeader size="small" style={{ "maxWidth": "100%", "overflow": "auto", tableLayout: "fixed"}}>
|
||||
<TableHead >
|
||||
<TableRow>
|
||||
<TableCell style={{width: "6rem"}}></TableCell>
|
||||
<TableCell style={{width: "3rem"}}></TableCell>
|
||||
<TableCell>File</TableCell>
|
||||
<TableCell>Progress</TableCell>
|
||||
<TableCell>Description</TableCell>
|
||||
<TableCell >C2 Status</TableCell>
|
||||
<TableCell>Tags</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
<Table stickyHeader size="small" style={{ "maxWidth": "100%", "overflow": "auto", tableLayout: "fixed"}}>
|
||||
<TableHead >
|
||||
<TableRow>
|
||||
<TableCell style={{width: "6rem"}}></TableCell>
|
||||
<TableCell style={{width: "3rem"}}></TableCell>
|
||||
<TableCell>File</TableCell>
|
||||
<TableCell>Progress</TableCell>
|
||||
<TableCell>Description</TableCell>
|
||||
<TableCell >C2 Status</TableCell>
|
||||
<TableCell>Tags</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{payload.map( (op) => (
|
||||
<PayloadsTableRow
|
||||
me={me}
|
||||
@@ -150,15 +149,16 @@ export function PayloadsTable({payload, onDeletePayload, onUpdateCallbackAlert,
|
||||
{...op}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div style={{background: "transparent", display: "flex", justifyContent: "center", alignItems: "center", paddingTop: "5px", paddingBottom: "10px"}}>
|
||||
<Pagination count={Math.ceil(pageData.totalCount / pageData.fetchLimit)} variant="outlined" color="primary" boundaryCount={1}
|
||||
siblingCount={1} onChange={onChangePage} showFirstButton={true} showLastButton={true} style={{padding: "20px"}}/>
|
||||
siblingCount={1} onChange={onChangePage} showFirstButton={true} showLastButton={true} style={{padding: "20px"}}/>
|
||||
<Typography style={{paddingLeft: "10px"}}>Total Results: {pageData.totalCount}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,18 +3,16 @@ import Paper from '@mui/material/Paper';
|
||||
import {useTheme} from '@mui/material/styles';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import {ReportingTable} from './ReportingTable';
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
|
||||
|
||||
export function Reporting() {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div style={{height: "100%", display: "flex", flexDirection: "column"}}>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
|
||||
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
|
||||
Mythic Report Generation
|
||||
</Typography>
|
||||
</Paper>
|
||||
<MythicPageBody>
|
||||
<MythicPageHeader title={"Mythic Report Generation"}>
|
||||
</MythicPageHeader>
|
||||
<ReportingTable />
|
||||
</div>
|
||||
</MythicPageBody>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { SettingsOperatorTable } from './SettingsOperatorTable';
|
||||
import {useMutation, useQuery, gql} from '@apollo/client';
|
||||
import { meState } from '../../../cache';
|
||||
import { snackActions } from '../../utilities/Snackbar';
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
|
||||
const GET_Operator = gql`
|
||||
query GetOperators {
|
||||
@@ -275,7 +276,7 @@ export function Settings({me}){
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{display: "flex", height: "100%", flexDirection: "column"}}>
|
||||
<MythicPageBody>
|
||||
<SettingsOperatorTable
|
||||
me={me}
|
||||
onViewUTCChanged={onViewUTCChanged}
|
||||
@@ -287,6 +288,6 @@ export function Settings({me}){
|
||||
onUsernameChanged={onUsernameChanged}
|
||||
onPasswordChanged={onPasswordChanged}
|
||||
operators={operators} />
|
||||
</div>
|
||||
</MythicPageBody>
|
||||
);
|
||||
}
|
||||
@@ -26,6 +26,8 @@ import {SettingsOperatorAPITokenSearchDialog} from "./SettingsOperatorAPITokenSe
|
||||
import ForwardToInboxTwoToneIcon from '@mui/icons-material/ForwardToInboxTwoTone';
|
||||
import {InviteLinksDialog} from "./InviteLinksDialog";
|
||||
import {useLazyQuery} from '@apollo/client';
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
|
||||
|
||||
|
||||
@@ -72,23 +74,43 @@ export function SettingsOperatorTable(props){
|
||||
getGlobalSettings().then(({data}) => setInviteLinksEnabled(data.getGlobalSettings.settings["server_config"]["allow_invite_links"]));
|
||||
}, []);
|
||||
return (
|
||||
<div style={{display: "flex", flexDirection: "column", height: "100%"}}>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
|
||||
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
|
||||
Settings
|
||||
</Typography>
|
||||
<MythicStyledTooltip title={"Adjust Global Settings"} tooltipStyle={{float: "right", marginRight: "10px", marginLeft: "5px"}}>
|
||||
<>
|
||||
<MythicPageHeader title={"Settings"}>
|
||||
<MythicStyledTooltip title={"Manage Invite Links"} tooltipStyle={{}}>
|
||||
<IconButton size={"small"} disabled={!inviteLinksEnabled || !userIsAdmin}
|
||||
onClick={()=>{setOpenInviteLinksDialog(true);}} variant={"contained"} >
|
||||
<ForwardToInboxTwoToneIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
<MythicStyledTooltip title={"Create new user account"} tooltipStyle={{}}>
|
||||
<IconButton size="small" onClick={()=>{setOpenNewDialog(true);}} variant="contained">
|
||||
<PersonAddIcon/>
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
<MythicStyledTooltip title={"Create new Bot account"} tooltipStyle={{}}>
|
||||
<IconButton size={"small"}
|
||||
onClick={()=>{setOpenNewBotDialog(true);}} variant="contained">
|
||||
<SmartToyTwoToneIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
<MythicStyledTooltip title={"Search for API Tokens"} tooltipStyle={{}} >
|
||||
<IconButton size={"small"} variant="contained"
|
||||
onClick={() => setOpenAPITokenSearch(true)}>
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
<MythicStyledTooltip title={"Adjust Global Settings"} tooltipStyle={{}}>
|
||||
<IconButton size="small" variant="contained"
|
||||
onClick={() => setOpenGlobalSettingsDialog(!openGlobalSettingsDialog)} >
|
||||
<TuneIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
{showDeleted ? (
|
||||
<MythicStyledTooltip title={"Hide Deleted Operators"} tooltipStyle={{float: "right"}}>
|
||||
<MythicStyledTooltip title={"Hide Deleted Operators"} tooltipStyle={{}}>
|
||||
<IconButton size="small" variant="contained" onClick={() => setShowDeleted(!showDeleted)}><VisibilityIcon /></IconButton>
|
||||
</MythicStyledTooltip>
|
||||
) : (
|
||||
<MythicStyledTooltip title={"Show Deleted Operators"} tooltipStyle={{float: "right"}}>
|
||||
<MythicStyledTooltip title={"Show Deleted Operators"} tooltipStyle={{}}>
|
||||
<IconButton size="small" variant="contained" onClick={() => setShowDeleted(!showDeleted)} ><VisibilityOffIcon /></IconButton>
|
||||
</MythicStyledTooltip>
|
||||
)}
|
||||
@@ -99,12 +121,6 @@ export function SettingsOperatorTable(props){
|
||||
onClose={closeGlobalSettingsDialog} />}
|
||||
/>
|
||||
}
|
||||
<MythicStyledTooltip title={"Search for API Tokens"} tooltipStyle={{float: "right", marginRight: "5px",}} >
|
||||
<IconButton size={"small"} variant="contained"
|
||||
onClick={() => setOpenAPITokenSearch(true)}>
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
{openAPITokenSearch &&
|
||||
<MythicDialog open={openAPITokenSearch} maxWidth={"xl"} fullWidth={true}
|
||||
onClose={()=>{setOpenAPITokenSearch(false);}}
|
||||
@@ -112,23 +128,6 @@ export function SettingsOperatorTable(props){
|
||||
onClose={()=>{setOpenAPITokenSearch(false);}} />}
|
||||
/>
|
||||
}
|
||||
<MythicStyledTooltip title={"Create new Bot account"} tooltipStyle={{float: "right", marginLeft: "5px"}}>
|
||||
<IconButton size={"small"}
|
||||
onClick={()=>{setOpenNewBotDialog(true);}} variant="contained">
|
||||
<SmartToyTwoToneIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
<MythicStyledTooltip title={"Create new user account"} tooltipStyle={{float: "right", marginLeft: "5px"}}>
|
||||
<IconButton size="small" onClick={()=>{setOpenNewDialog(true);}} variant="contained">
|
||||
<PersonAddIcon/>
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
<MythicStyledTooltip title={"Manage Invite Links"} tooltipStyle={{float: "right", marginLeft: "5px"}}>
|
||||
<IconButton size={"small"} disabled={!inviteLinksEnabled || !userIsAdmin}
|
||||
onClick={()=>{setOpenInviteLinksDialog(true);}} variant={"contained"} >
|
||||
<ForwardToInboxTwoToneIcon />
|
||||
</IconButton>
|
||||
</MythicStyledTooltip>
|
||||
{openInviteLinksDialog &&
|
||||
<MythicDialog open={openInviteLinksDialog} maxWidth={"xl"} fullWidth={true}
|
||||
onClose={()=>{setOpenInviteLinksDialog(false);}}
|
||||
@@ -151,7 +150,7 @@ export function SettingsOperatorTable(props){
|
||||
innerDialog={<SettingsBotDialog title="New Bot Account" onAccept={onSubmitNewBot} handleClose={()=>{setOpenNewBotDialog(false);}} {...props}/>}
|
||||
/>
|
||||
}
|
||||
</Paper>
|
||||
</MythicPageHeader>
|
||||
<div style={{display: "flex", flexGrow: 1, overflowY: "auto", alignItems: "flex-start"}}>
|
||||
<Table stickyHeader size="small" style={{"tableLayout": "fixed",}}>
|
||||
<TableHead >
|
||||
@@ -189,7 +188,7 @@ export function SettingsOperatorTable(props){
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -242,6 +242,10 @@ export function SettingsOperatorUIConfigDialog(props) {
|
||||
dark: isValidColor(initialPalette?.outputTextColor?.dark) ? initialPalette?.outputTextColor?.dark : operatorSettingDefaults.palette.outputTextColor.dark,
|
||||
light: isValidColor(initialPalette?.outputTextColor?.light) ? initialPalette?.outputTextColor?.light : operatorSettingDefaults.palette.outputTextColor.light,
|
||||
},
|
||||
borderColor: {
|
||||
dark: isValidColor(initialPalette?.borderColor?.dark) ? initialPalette?.borderColor?.dark : operatorSettingDefaults.palette.borderColor.dark,
|
||||
light: isValidColor(initialPalette?.borderColor?.light) ? initialPalette?.borderColor?.light : operatorSettingDefaults.palette.borderColor.light,
|
||||
},
|
||||
});
|
||||
const paletteOptionsSolidColor = [
|
||||
{name: "primary", display: "Primary"},
|
||||
@@ -264,7 +268,8 @@ export function SettingsOperatorUIConfigDialog(props) {
|
||||
{name: "taskContextColor", display: "Tasking Context Generic Background Color"},
|
||||
{name: "taskContextImpersonationColor", display: "Tasking Context User Background Color"},
|
||||
{name: "taskContextExtraColor", display: "Tasking Context Extra Info Background Color"},
|
||||
{name: "emptyFolderColor", display: "Color of the empty folder icon and text in file-based browsers"}
|
||||
{name: "emptyFolderColor", display: "Color of the empty folder icon and text in file-based browsers"},
|
||||
{name: "borderColor", display: "Color of borders around elements"}
|
||||
]
|
||||
const [resumeNotifications, setResumeNotifications] = React.useState(false);
|
||||
const [_, updateSettings, clearSettings] = useSetMythicSetting();
|
||||
|
||||
@@ -66,11 +66,10 @@ query taskMetadataQuery($task_range: [Int!]) {
|
||||
display_id
|
||||
}
|
||||
}`;
|
||||
const lazyQueryOptions = {fetchPolicy: "no-cache"};
|
||||
export function TaskMetadataTable(props){
|
||||
const [tasks, setTasks] = React.useState([]);
|
||||
|
||||
const getMetadata = useMythicLazyQuery(MetadataQuery, {fetchPolicy: "no-cache"
|
||||
});
|
||||
const getMetadata = useMythicLazyQuery(MetadataQuery, lazyQueryOptions);
|
||||
useEffect( () => {
|
||||
getMetadata({variables: {task_range: props.taskIDs } }).then(({data}) => {
|
||||
setTasks(data.task);
|
||||
|
||||
@@ -15,6 +15,7 @@ import {useTheme} from '@mui/material/styles';
|
||||
import {taskingDataFragment} from '../Callbacks/CallbackMutations'
|
||||
import {meState} from "../../../cache";
|
||||
import { useReactiveVar } from '@apollo/client';
|
||||
import {TaskDisplayInteractiveSearch} from "../Search/SearchTabInteractiveTasks";
|
||||
|
||||
const tasksQuery = gql`
|
||||
${taskingDataFragment}
|
||||
@@ -258,7 +259,11 @@ export function SingleTaskView(props){
|
||||
task.type === "task" ? (
|
||||
<div key={"taskdisplay:" + task.display_id} style={{marginRight: "5px"}}>
|
||||
<div style={{width: removing ? "95%" : "100%", display: "inline-block"}}>
|
||||
<TaskDisplay me={me} task={task} command_id={task.command === null ? 0 : task.command.id} />
|
||||
{task.is_interactive_task ? (
|
||||
<TaskDisplayInteractiveSearch key={"taskinteractdisplay" + task.id} me={me} task={task} responsesSurrounding={5} />
|
||||
) : (
|
||||
<TaskDisplay key={"taskinteractdisplay" + task.id} me={me} task={task} command_id={task.command == null ? 0 : task.command.id} />
|
||||
)}
|
||||
</div>
|
||||
{removing ? (
|
||||
<Switch
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { TagtypesTable } from './TagtypesTable';
|
||||
import {useMutation, gql, useQuery} from '@apollo/client';
|
||||
import { snackActions } from '../../utilities/Snackbar';
|
||||
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
|
||||
const tagtypeFragment = gql`
|
||||
fragment tagtypeData on tagtype {
|
||||
id
|
||||
@@ -89,8 +90,8 @@ export function Tags(props){
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
return (
|
||||
<div style={{display: "flex", height: "100%", flexDirection: "column"}}>
|
||||
<MythicPageBody>
|
||||
<TagtypesTable onDeleteTagtype={onDeleteTagtype} tagtypes={tagtypes} onNewTag={onNewTag} onUpdateTagtype={onUpdateTagtype} me={props.me}/>
|
||||
</div>
|
||||
</MythicPageBody>
|
||||
);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import FileDownloadIcon from '@mui/icons-material/FileDownload';
|
||||
import AddCircleIcon from '@mui/icons-material/AddCircle';
|
||||
import {downloadFileFromMemory} from '../../utilities/Clipboard';
|
||||
import {useMythicLazyQuery} from "../../utilities/useMythicLazyQuery";
|
||||
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
|
||||
|
||||
const importTagtypesMutation = gql`
|
||||
mutation importMultipleTagtypes($tagtypes: String!) {
|
||||
@@ -83,11 +84,8 @@ export function TagtypesTable({tagtypes, onDeleteTagtype, onNewTag, onUpdateTagt
|
||||
.then(({data}) => exportTagTypesSuccess(data)).catch(({data}) => exportTagTypesError(data));
|
||||
}
|
||||
return (
|
||||
<div style={{display: "flex", flexDirection: "column", width: "100%", height: "100%"}}>
|
||||
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
|
||||
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
|
||||
Types of Tags
|
||||
</Typography>
|
||||
<>
|
||||
<MythicPageHeader title={"Types of Tags"}>
|
||||
<Button component="label" size={"small"}
|
||||
style={{float:"right", marginRight: "5px", color: "white"}} >
|
||||
<FileUploadIcon /> {"Import"}
|
||||
@@ -105,7 +103,7 @@ export function TagtypesTable({tagtypes, onDeleteTagtype, onNewTag, onUpdateTagt
|
||||
innerDialog={<NewTagtypesDialog onClose={()=>{setOpenNewDialog(false);}} onSubmit={onNewTag} />}
|
||||
/>
|
||||
}
|
||||
</Paper>
|
||||
</MythicPageHeader>
|
||||
<div style={{display: "flex", flexGrow: 1, overflow: "auto"}}>
|
||||
<TableContainer className="mythicElement">
|
||||
<Table stickyHeader={true} size="small" style={{ "maxWidth": "100%", "overflow": "scroll"}}>
|
||||
@@ -132,7 +130,7 @@ export function TagtypesTable({tagtypes, onDeleteTagtype, onNewTag, onUpdateTagt
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import {jwtDecode} from 'jwt-decode';
|
||||
import {meState} from './cache';
|
||||
import {getSkewedNow} from "./components/utilities/Time";
|
||||
|
||||
export const mythicUIVersion = "0.3.108";
|
||||
export const mythicUIVersion = "0.3.114";
|
||||
|
||||
let fetchingNewToken = false;
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ html, body, #root {
|
||||
.MuiTableCell-head {
|
||||
background-color: ${(props) => props.theme.tableHeader} !important;
|
||||
font-weight: bold;
|
||||
border-top: 2px solid grey;
|
||||
border-bottom: 2px solid grey;
|
||||
border-top: 2px solid ${(props) => props.theme.borderColor};
|
||||
border-bottom: 2px solid ${(props) => props.theme.borderColor};
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: ${(props) => props.theme.palette.mode === 'dark' ? props.theme.tableHover + "80" : props.theme.tableHover + "80"};
|
||||
@@ -146,13 +146,13 @@ tspan {
|
||||
opacity: 1;
|
||||
}
|
||||
.groupNode {
|
||||
border: 1px solid grey;
|
||||
border: 1px solid ${(props) => props.theme.borderColor};
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
background: ${(props) => props.theme.palette.graphGroupRGBA} !important;
|
||||
}
|
||||
.groupEventNode {
|
||||
border: 1px solid grey;
|
||||
border: 1px solid ${(props) => props.theme.borderColor};
|
||||
padding: 2px;
|
||||
border-radius: 5px;
|
||||
background: ${(props) => props.theme.palette.graphGroupRGBA} !important;
|
||||
@@ -167,7 +167,7 @@ tspan {
|
||||
}
|
||||
.context-menu {
|
||||
background-color: ${(props) => props.theme.palette.background.paper};
|
||||
border: 1px solid grey;
|
||||
border: 1px solid ${(props) => props.theme.borderColor};
|
||||
border-radius: 5px;
|
||||
box-shadow: 10px 19px 20px rgba(0, 0, 0, 10%);
|
||||
position: absolute;
|
||||
@@ -212,11 +212,11 @@ tspan {
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
user-select: none;
|
||||
border-top: 2px solid grey;
|
||||
border-bottom: 2px solid grey;
|
||||
border-top: 2px solid ${(props) => props.theme.borderColor};
|
||||
border-bottom: 2px solid ${(props) => props.theme.borderColor};
|
||||
background-color: ${(props) => props.theme.tableHeader} !important;
|
||||
&:first-child-of-type {
|
||||
border-left: 2px solid grey;
|
||||
border-left: 2px solid ${(props) => props.theme.borderColor};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${(props) => props.theme.tableHover};
|
||||
@@ -232,7 +232,7 @@ tspan {
|
||||
padding: 0 0.5em;
|
||||
box-sizing: border-box;
|
||||
font-family: monospace;
|
||||
border-bottom: 1px solid grey;
|
||||
border-bottom: 1px solid ${(props) => props.theme.borderColor};
|
||||
cursor: default !important;
|
||||
}
|
||||
.MythicResizableGrid-cellInner {
|
||||
@@ -276,7 +276,7 @@ tspan {
|
||||
width: 100%;
|
||||
}
|
||||
.MuiPaper-root {
|
||||
border: 1px solid grey;
|
||||
border: 1px solid ${(props) => props.theme.borderColor};
|
||||
border-radius: 5px;
|
||||
background-color: ${(props) => props.theme.palette.background.paper};
|
||||
background-image: unset;
|
||||
@@ -290,13 +290,13 @@ tspan {
|
||||
|
||||
.MuiList-root {
|
||||
background-color: ${(props) => props.theme.palette.background.paper};
|
||||
border: 1px solid grey;
|
||||
border: 1px solid ${(props) => props.theme.borderColor};
|
||||
border-radius: 3px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.dropdownMenuColored {
|
||||
background-color: ${(props) => props.theme.palette.background.paper} !important;
|
||||
border: 1px solid grey;
|
||||
border: 1px solid ${(props) => props.theme.borderColor};
|
||||
border-radius: 5px;
|
||||
//color: white;
|
||||
}
|
||||
@@ -331,25 +331,25 @@ tspan {
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
.MuiInputLabel-root {
|
||||
color: grey !important;
|
||||
color: ${(props) => props.theme.borderColor} !important;
|
||||
}
|
||||
.MuiOutlinedInput-notchedOutline {
|
||||
border-color: grey !important;
|
||||
border-color: ${(props) => props.theme.borderColor} !important;
|
||||
}
|
||||
.MuiInput-underline {
|
||||
border-color: grey !important;
|
||||
border-color: ${(props) => props.theme.borderColor} !important;
|
||||
}
|
||||
.MuiSelect-outlined {
|
||||
border-color: grey !important;
|
||||
border-color: ${(props) => props.theme.borderColor} !important;
|
||||
}
|
||||
.Mui-focused {
|
||||
border-color: grey !important;
|
||||
border-color: ${(props) => props.theme.borderColor} !important;
|
||||
}
|
||||
.MuiInputBase-input {
|
||||
border-color: grey !important;
|
||||
border-color: ${(props) => props.theme.borderColor} !important;
|
||||
}
|
||||
.MuiInput-root::after {
|
||||
border-color: grey !important;
|
||||
border-color: ${(props) => props.theme.borderColor} !important;
|
||||
}
|
||||
.MuiTableCell-root {
|
||||
padding: 0 16px 0 16px;
|
||||
@@ -386,22 +386,22 @@ tspan {
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
padding: 0 10px 0 10px;
|
||||
border-top: 1px solid grey;
|
||||
border-left: 2px solid grey;
|
||||
border-right: 1px solid grey;
|
||||
border-bottom: 1px solid grey;
|
||||
border-top: 1px solid ${(props) => props.theme.borderColor};
|
||||
border-left: 2px solid ${(props) => props.theme.borderColor};
|
||||
border-right: 1px solid ${(props) => props.theme.borderColor};
|
||||
border-bottom: 1px solid ${(props) => props.theme.borderColor};
|
||||
position: relative;
|
||||
top: 2px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.empty-table-header {
|
||||
border: 1px solid grey !important;
|
||||
border: 1px solid ${(props) => props.theme.borderColor} !important;
|
||||
height: 0px !important;
|
||||
padding: 0px !important;
|
||||
margin: 0px !important;
|
||||
}
|
||||
.code-box {
|
||||
border: 1px solid grey !important;
|
||||
border: 1px solid ${(props) => props.theme.borderColor} !important;
|
||||
padding: 0px 5px 0px 5px;
|
||||
overflow: auto;
|
||||
white-space: pre;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
BINARY_NAME=mythic-cli
|
||||
LOCAL_PATH=$(shell pwd)
|
||||
BUILDER_IMAGE=ghcr.io/its-a-feature/mythic_cli:v3.4.0.52
|
||||
BUILDER_IMAGE=ghcr.io/its-a-feature/mythic_cli:v3.4.0.61
|
||||
.PHONY: default
|
||||
default: build_linux ;
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@ package config
|
||||
|
||||
// MythicDockerLatest is the most recent tagged version pushed to GitHub packages
|
||||
|
||||
const MythicDockerLatest = "v3.4.0.52"
|
||||
const MythicDockerLatest = "v3.4.0.61"
|
||||
|
||||
@@ -110,6 +110,9 @@ func setMythicConfigDefaultValues() {
|
||||
mythicEnv.SetDefault("global_restart_policy", "always")
|
||||
mythicEnvInfo["global_restart_policy"] = `This sets the restart policy for the containers within Mythic. Valid options should only be 'always', 'unless-stopped', and 'on-failure'. The default of 'always' will ensure that Mythic comes back up even when the server reboots. The 'unless-stopped' value means that Mythic should come back online after reboot unless you specifically ran './mythic-cli stop' first.`
|
||||
|
||||
mythicEnv.SetDefault("mythic_docker_image_mirror", "")
|
||||
mythicEnvInfo["mythic_docker_image_mirror"] = `This sets an internal Docker registry to pull images from instead of their original upstream registries. When empty (the default), every image is pulled from its original location (for example, "ghcr.io/its-a-feature/mythic_server:<global_docker_latest>" for core services, or whatever URL is declared in each installed agent's/C2 profile's config.json "remote_images" entry). When set to a non-empty value, the leading registry host of every image is replaced with this value while the rest of the path is preserved. For example, with mythic_docker_image_mirror="myregistry.local/mirror", the core image "ghcr.io/its-a-feature/mythic_server:0.0.5" becomes "myregistry.local/mirror/its-a-feature/mythic_server:0.0.5" and an agent image "ghcr.io/mythicagents/apollo:latest" becomes "myregistry.local/mirror/mythicagents/apollo:latest". The same rewrite is applied every time the docker-compose file is generated, so this can be changed and applied with start/build without re-installing services. This setting has no effect on services where *_use_build_context is true. Images without a registry host (for example, "nginx:latest") are treated as having no host and the full path is preserved under the mirror.`
|
||||
|
||||
// nginx configuration ---------------------------------------------
|
||||
mythicEnv.SetDefault("nginx_port", 7443)
|
||||
mythicEnvInfo["nginx_port"] = `This sets the port used for the Nginx reverse proxy - this port is used by the React UI and Mythic's Scripting`
|
||||
@@ -268,6 +271,9 @@ If it's anything else, mythic-cli will not spin up this container as it assumes
|
||||
mythicEnv.SetDefault("postgres_password", utils.GenerateRandomPassword(30))
|
||||
mythicEnvInfo["postgres_password"] = `This is the randomly generated password that mythic_server and mythic_graphql use to connect to the mythic_postgres container`
|
||||
|
||||
mythicEnv.SetDefault("postgres_sslmode", "disable")
|
||||
mythicEnvInfo["postgres_sslmode"] = `This specifies the SSL mode used when connecting to the PostgreSQL database (e.g. disable, require, verify-ca, verify-full)`
|
||||
|
||||
mythicEnv.SetDefault("postgres_cpus", defaultNumberOfCPUs)
|
||||
mythicEnvInfo["postgres_cpus"] = `Set this to limit the maximum number of CPUs this service is able to consume`
|
||||
|
||||
@@ -678,7 +684,7 @@ func GetConfigHelp(entries []string) map[string]string {
|
||||
} else if strings.HasSuffix(setting, "use_build_context") {
|
||||
output[setting] = `This setting determines if you use the pre-built image hosted on GitHub/DockerHub or if you use your local InstalledService/[agent]/Dockerfile to build a new local image for your container. If you're wanting to make changes to an agent or c2 profile (adding commands, updating code, etc), then you need to set this to 'true' and update your Dockerfile to copy in your modified code. If your container code is Golang, then you'll also need to make sure your modified Dockerfile rebuilds that Go code based on your changes (probably with a 'go build' or 'make build' command depending on the agent). If your container code is Python, then simply copying in the changes should be sufficient. Also make sure you change [agent]_use_volume to 'false' so that your changes don't get overwritten by an old volume. Alternatively, you could remove the old volume with './mythic-cli volume rm [agent]_volume' and then build your new container with './mythic-cli build [agent]'.`
|
||||
} else if strings.HasSuffix(setting, "remote_image") {
|
||||
output[setting] = `This setting configures the remote image to use if you have *_use_build_context set to false. This value should get automatically updated by the agent/c2 profile's repo as new releases are created. This value will also get updated each time you install an agent. So if you want to pull an agent's latest image, just re-install the agent (or manually update this value and restart the local container).`
|
||||
output[setting] = `This setting configures the remote image to use if you have *_use_build_context set to false. This value should get automatically updated by the agent/c2 profile's repo as new releases are created. This value will also get updated each time you install an agent. So if you want to pull an agent's latest image, just re-install the agent (or manually update this value and restart the local container). When mythic_docker_image_mirror is set, this value is kept as the upstream image URL but the image actually pulled is rewritten to come from the mirror; clear the mirror to pull from the URL shown here as-is.`
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,9 @@ func InstallFolder(installPath string, overWrite bool, keepVolume bool, installU
|
||||
}
|
||||
}
|
||||
latestVersion := installConfig.GetStringMapString("remote_images")
|
||||
if mirror := config.GetMythicEnv().GetString("mythic_docker_image_mirror"); mirror != "" {
|
||||
log.Printf("[*] mythic_docker_image_mirror is set to %q; this service's image(s) will be pulled from there instead of their upstream registry\n", mirror)
|
||||
}
|
||||
for key, val := range latestVersion {
|
||||
config.SetNewConfigStrings(fmt.Sprintf("%s_remote_image", key), val)
|
||||
if !config.GetMythicEnv().InConfig(fmt.Sprintf("%s_use_volume", key)) {
|
||||
|
||||
@@ -87,8 +87,14 @@ func ServiceStart(containers []string, keepVolume bool) error {
|
||||
log.Printf("[-] Failed to remove images\n%v\n", err)
|
||||
return err
|
||||
}
|
||||
updateNginxBlockLists()
|
||||
generateCerts()
|
||||
// nginx-specific housekeeping only applies when mythic_nginx runs locally
|
||||
if isServiceInternal("mythic_nginx") {
|
||||
updateNginxBlockLists()
|
||||
if err := generateCerts(); err != nil {
|
||||
log.Printf("[-] Failed to generate certs: %v\n", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
TestMythicRabbitmqConnection()
|
||||
TestMythicConnection()
|
||||
Status(false)
|
||||
|
||||
@@ -44,7 +44,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
pStruct["image"] = service
|
||||
} else {
|
||||
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
|
||||
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
|
||||
}
|
||||
|
||||
pStruct["cpus"] = mythicEnv.GetInt("POSTGRES_CPUS")
|
||||
@@ -67,16 +67,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
} else {
|
||||
delete(pStruct, "ports")
|
||||
pStruct["network_mode"] = "host"
|
||||
pStruct["extra_hosts"] = []string{
|
||||
"mythic_server:127.0.0.1",
|
||||
"mythic_rabbitmq:127.0.0.1",
|
||||
"mythic_nginx:127.0.0.1",
|
||||
"mythic_react:127.0.0.1",
|
||||
"mythic_documentation:127.0.0.1",
|
||||
"mythic_graphql:127.0.0.1",
|
||||
"mythic_jupyter:127.0.0.1",
|
||||
"mythic_postgres:127.0.0.1",
|
||||
}
|
||||
pStruct["extra_hosts"] = getExtraHosts()
|
||||
if mythicEnv.GetBool("postgres_bind_localhost_only") {
|
||||
pStruct["command"] = "postgres -c \"max_connections=100\" -p ${POSTGRES_PORT} -c config_file=/etc/postgresql.conf -c \"listen_addresses=localhost\""
|
||||
} else {
|
||||
@@ -126,7 +117,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
pStruct["image"] = service
|
||||
} else {
|
||||
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
|
||||
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
|
||||
}
|
||||
if mythicEnv.GetString("mythic_docker_networking") == "bridge" {
|
||||
if mythicEnv.GetBool("documentation_bind_localhost_only") {
|
||||
@@ -146,16 +137,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
} else {
|
||||
delete(pStruct, "ports")
|
||||
pStruct["network_mode"] = "host"
|
||||
pStruct["extra_hosts"] = []string{
|
||||
"mythic_server:127.0.0.1",
|
||||
"mythic_rabbitmq:127.0.0.1",
|
||||
"mythic_nginx:127.0.0.1",
|
||||
"mythic_react:127.0.0.1",
|
||||
"mythic_documentation:127.0.0.1",
|
||||
"mythic_graphql:127.0.0.1",
|
||||
"mythic_jupyter:127.0.0.1",
|
||||
"mythic_postgres:127.0.0.1",
|
||||
}
|
||||
pStruct["extra_hosts"] = getExtraHosts()
|
||||
if mythicEnv.GetBool("documentation_bind_localhost_only") {
|
||||
pStruct["environment"] = []string{
|
||||
"DOCUMENTATION_PORT=${DOCUMENTATION_PORT}",
|
||||
@@ -185,13 +167,21 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
}
|
||||
case "mythic_graphql":
|
||||
pStruct["depends_on"] = map[string]map[string]string{
|
||||
"mythic_postgres": {
|
||||
dependsOn := map[string]map[string]string{}
|
||||
if isServiceInternal("mythic_server") {
|
||||
dependsOn["mythic_server"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_server": {
|
||||
}
|
||||
}
|
||||
if isServiceInternal("mythic_postgres") {
|
||||
dependsOn["mythic_postgres"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
}
|
||||
}
|
||||
if len(dependsOn) > 0 {
|
||||
pStruct["depends_on"] = dependsOn
|
||||
} else {
|
||||
delete(pStruct, "depends_on")
|
||||
}
|
||||
if mythicEnv.GetBool("hasura_use_build_context") {
|
||||
pStruct["build"] = map[string]interface{}{
|
||||
@@ -200,7 +190,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
pStruct["image"] = service
|
||||
} else {
|
||||
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
|
||||
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
|
||||
}
|
||||
|
||||
pStruct["cpus"] = mythicEnv.GetInt("HASURA_CPUS")
|
||||
@@ -208,8 +198,8 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
pStruct["mem_limit"] = mythicEnv.GetString("hasura_mem_limit")
|
||||
}
|
||||
environment := []string{
|
||||
"HASURA_GRAPHQL_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}",
|
||||
"HASURA_GRAPHQL_METADATA_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}",
|
||||
"HASURA_GRAPHQL_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=${POSTGRES_SSLMODE:-disable}",
|
||||
"HASURA_GRAPHQL_METADATA_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=${POSTGRES_SSLMODE:-disable}",
|
||||
"HASURA_GRAPHQL_ENABLE_CONSOLE=true",
|
||||
"HASURA_GRAPHQL_DEV_MODE=false",
|
||||
"HASURA_GRAPHQL_ADMIN_SECRET=${HASURA_SECRET}",
|
||||
@@ -239,16 +229,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
} else {
|
||||
delete(pStruct, "ports")
|
||||
pStruct["network_mode"] = "host"
|
||||
pStruct["extra_hosts"] = []string{
|
||||
"mythic_server:127.0.0.1",
|
||||
"mythic_rabbitmq:127.0.0.1",
|
||||
"mythic_nginx:127.0.0.1",
|
||||
"mythic_react:127.0.0.1",
|
||||
"mythic_documentation:127.0.0.1",
|
||||
"mythic_graphql:127.0.0.1",
|
||||
"mythic_jupyter:127.0.0.1",
|
||||
"mythic_postgres:127.0.0.1",
|
||||
}
|
||||
pStruct["extra_hosts"] = getExtraHosts()
|
||||
if mythicEnv.GetBool("hasura_bind_localhost_only") {
|
||||
environment = append(environment, "HASURA_GRAPHQL_SERVER_HOST=127.0.0.1")
|
||||
} else {
|
||||
@@ -264,22 +245,36 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
delete(pStruct, "volumes")
|
||||
}
|
||||
case "mythic_nginx":
|
||||
pStruct["depends_on"] = map[string]map[string]string{
|
||||
"mythic_server": {
|
||||
dependsOn := map[string]map[string]string{}
|
||||
if isServiceInternal("mythic_server") {
|
||||
dependsOn["mythic_server"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_react": {
|
||||
}
|
||||
}
|
||||
if isServiceInternal("mythic_react") {
|
||||
dependsOn["mythic_react"] = map[string]string{
|
||||
"condition": "service_started",
|
||||
},
|
||||
"mythic_jupyter": {
|
||||
}
|
||||
}
|
||||
if isServiceInternal("mythic_jupyter") {
|
||||
dependsOn["mythic_jupyter"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_documentation": {
|
||||
}
|
||||
}
|
||||
if isServiceInternal("mythic_documentation") {
|
||||
dependsOn["mythic_documentation"] = map[string]string{
|
||||
"condition": "service_started",
|
||||
},
|
||||
"mythic_graphql": {
|
||||
}
|
||||
}
|
||||
if isServiceInternal("mythic_graphql") {
|
||||
dependsOn["mythic_graphql"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
}
|
||||
}
|
||||
if len(dependsOn) > 0 {
|
||||
pStruct["depends_on"] = dependsOn
|
||||
} else {
|
||||
delete(pStruct, "depends_on")
|
||||
}
|
||||
if mythicEnv.GetBool("nginx_use_build_context") {
|
||||
pStruct["build"] = map[string]interface{}{
|
||||
@@ -288,7 +283,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
pStruct["image"] = service
|
||||
} else {
|
||||
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
|
||||
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
|
||||
}
|
||||
|
||||
nginxUseSSL := "ssl"
|
||||
@@ -348,16 +343,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
} else {
|
||||
delete(pStruct, "ports")
|
||||
pStruct["network_mode"] = "host"
|
||||
pStruct["extra_hosts"] = []string{
|
||||
"mythic_server:127.0.0.1",
|
||||
"mythic_rabbitmq:127.0.0.1",
|
||||
"mythic_nginx:127.0.0.1",
|
||||
"mythic_react:127.0.0.1",
|
||||
"mythic_documentation:127.0.0.1",
|
||||
"mythic_graphql:127.0.0.1",
|
||||
"mythic_jupyter:127.0.0.1",
|
||||
"mythic_postgres:127.0.0.1",
|
||||
}
|
||||
pStruct["extra_hosts"] = getExtraHosts()
|
||||
if mythicEnv.GetBool("nginx_bind_localhost_only") {
|
||||
finalNginxEnv = append(finalNginxEnv, "NGINX_BIND_IPV4=127.0.0.1", "NGINX_BIND_IPV6=[::1]")
|
||||
} else {
|
||||
@@ -393,7 +379,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
pStruct["image"] = service
|
||||
} else {
|
||||
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
|
||||
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
|
||||
}
|
||||
pStruct["cpus"] = mythicEnv.GetInt("RABBITMQ_CPUS")
|
||||
if mythicEnv.GetString("rabbitmq_mem_limit") != "" {
|
||||
@@ -434,16 +420,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
} else {
|
||||
delete(pStruct, "ports")
|
||||
pStruct["network_mode"] = "host"
|
||||
pStruct["extra_hosts"] = []string{
|
||||
"mythic_server:127.0.0.1",
|
||||
"mythic_rabbitmq:127.0.0.1",
|
||||
"mythic_nginx:127.0.0.1",
|
||||
"mythic_react:127.0.0.1",
|
||||
"mythic_documentation:127.0.0.1",
|
||||
"mythic_graphql:127.0.0.1",
|
||||
"mythic_jupyter:127.0.0.1",
|
||||
"mythic_postgres:127.0.0.1",
|
||||
}
|
||||
pStruct["extra_hosts"] = getExtraHosts()
|
||||
if mythicEnv.GetBool("rabbitmq_bind_localhost_only") {
|
||||
environment = append(environment, "RABBITMQ_NODE_IP_ADDRESS=127.0.0.1", "RABBITMQ_NODE_PORT=${RABBITMQ_PORT}")
|
||||
} else {
|
||||
@@ -469,16 +446,26 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
}
|
||||
case "mythic_react":
|
||||
pStruct["depends_on"] = map[string]map[string]string{
|
||||
"mythic_server": {
|
||||
dependsOn := map[string]map[string]string{}
|
||||
if isServiceInternal("mythic_server") {
|
||||
dependsOn["mythic_server"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_graphql": {
|
||||
}
|
||||
}
|
||||
if isServiceInternal("mythic_graphql") {
|
||||
dependsOn["mythic_graphql"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_postgres": {
|
||||
}
|
||||
}
|
||||
if isServiceInternal("mythic_postgres") {
|
||||
dependsOn["mythic_postgres"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
}
|
||||
}
|
||||
if len(dependsOn) > 0 {
|
||||
pStruct["depends_on"] = dependsOn
|
||||
} else {
|
||||
delete(pStruct, "depends_on")
|
||||
}
|
||||
if mythicEnv.GetBool("mythic_react_debug") {
|
||||
pStruct["build"] = map[string]interface{}{
|
||||
@@ -502,7 +489,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
pStruct["image"] = service
|
||||
} else {
|
||||
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
|
||||
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
|
||||
}
|
||||
|
||||
if !mythicEnv.GetBool("mythic_react_use_volume") {
|
||||
@@ -550,16 +537,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
} else {
|
||||
delete(pStruct, "ports")
|
||||
pStruct["network_mode"] = "host"
|
||||
pStruct["extra_hosts"] = []string{
|
||||
"mythic_server:127.0.0.1",
|
||||
"mythic_rabbitmq:127.0.0.1",
|
||||
"mythic_nginx:127.0.0.1",
|
||||
"mythic_react:127.0.0.1",
|
||||
"mythic_documentation:127.0.0.1",
|
||||
"mythic_graphql:127.0.0.1",
|
||||
"mythic_jupyter:127.0.0.1",
|
||||
"mythic_postgres:127.0.0.1",
|
||||
}
|
||||
pStruct["extra_hosts"] = getExtraHosts()
|
||||
if mythicEnv.GetBool("mythic_react_bind_localhost_only") {
|
||||
pStruct["environment"] = []string{
|
||||
"MYTHIC_REACT_PORT=${MYTHIC_REACT_PORT}",
|
||||
@@ -574,16 +552,26 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
|
||||
case "mythic_jupyter":
|
||||
pStruct["depends_on"] = map[string]map[string]string{
|
||||
"mythic_server": {
|
||||
dependsOn := map[string]map[string]string{}
|
||||
if isServiceInternal("mythic_server") {
|
||||
dependsOn["mythic_server"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_postgres": {
|
||||
}
|
||||
}
|
||||
if isServiceInternal("mythic_graphql") {
|
||||
dependsOn["mythic_graphql"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_graphql": {
|
||||
}
|
||||
}
|
||||
if isServiceInternal("mythic_postgres") {
|
||||
dependsOn["mythic_postgres"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
}
|
||||
}
|
||||
if len(dependsOn) > 0 {
|
||||
pStruct["depends_on"] = dependsOn
|
||||
} else {
|
||||
delete(pStruct, "depends_on")
|
||||
}
|
||||
if mythicEnv.GetBool("jupyter_use_build_context") {
|
||||
pStruct["build"] = map[string]interface{}{
|
||||
@@ -592,7 +580,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
pStruct["image"] = service
|
||||
} else {
|
||||
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
|
||||
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
|
||||
}
|
||||
|
||||
pStruct["cpus"] = mythicEnv.GetInt("JUPYTER_CPUS")
|
||||
@@ -615,20 +603,12 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
"JUPYTER_TOKEN=${JUPYTER_TOKEN}",
|
||||
"CHOWN_EXTRA=/projects",
|
||||
"CHOWN_EXTRA_OPTS=-R",
|
||||
"JUPYTER_PORT=${JUPYTER_PORT}",
|
||||
}
|
||||
} else {
|
||||
delete(pStruct, "ports")
|
||||
pStruct["network_mode"] = "host"
|
||||
pStruct["extra_hosts"] = []string{
|
||||
"mythic_server:127.0.0.1",
|
||||
"mythic_rabbitmq:127.0.0.1",
|
||||
"mythic_nginx:127.0.0.1",
|
||||
"mythic_react:127.0.0.1",
|
||||
"mythic_documentation:127.0.0.1",
|
||||
"mythic_graphql:127.0.0.1",
|
||||
"mythic_jupyter:127.0.0.1",
|
||||
"mythic_postgres:127.0.0.1",
|
||||
}
|
||||
pStruct["extra_hosts"] = getExtraHosts()
|
||||
environment := []string{
|
||||
"JUPYTER_TOKEN=${JUPYTER_TOKEN}",
|
||||
"CHOWN_EXTRA=/projects",
|
||||
@@ -664,13 +644,21 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
}
|
||||
case "mythic_server":
|
||||
pStruct["depends_on"] = map[string]map[string]string{
|
||||
"mythic_postgres": {
|
||||
dependsOn := map[string]map[string]string{}
|
||||
if isServiceInternal("mythic_rabbitmq") {
|
||||
dependsOn["mythic_rabbitmq"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_rabbitmq": {
|
||||
}
|
||||
}
|
||||
if isServiceInternal("mythic_postgres") {
|
||||
dependsOn["mythic_postgres"] = map[string]string{
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
}
|
||||
}
|
||||
if len(dependsOn) > 0 {
|
||||
pStruct["depends_on"] = dependsOn
|
||||
} else {
|
||||
delete(pStruct, "depends_on")
|
||||
}
|
||||
if mythicEnv.GetBool("mythic_server_use_build_context") {
|
||||
pStruct["build"] = map[string]interface{}{
|
||||
@@ -679,7 +667,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
pStruct["image"] = service
|
||||
} else {
|
||||
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
|
||||
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
|
||||
}
|
||||
|
||||
pStruct["cpus"] = mythicEnv.GetInt("MYTHIC_SERVER_CPUS")
|
||||
@@ -709,6 +697,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
"POSTGRES_HOST=${POSTGRES_HOST}",
|
||||
"POSTGRES_PORT=${POSTGRES_PORT}",
|
||||
"POSTGRES_PASSWORD=${POSTGRES_PASSWORD}",
|
||||
"POSTGRES_SSLMODE=${POSTGRES_SSLMODE}",
|
||||
"RABBITMQ_HOST=${RABBITMQ_HOST}",
|
||||
"RABBITMQ_PORT=${RABBITMQ_PORT}",
|
||||
"RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}",
|
||||
@@ -739,16 +728,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
} else {
|
||||
delete(pStruct, "ports")
|
||||
pStruct["network_mode"] = "host"
|
||||
pStruct["extra_hosts"] = []string{
|
||||
"mythic_server:127.0.0.1",
|
||||
"mythic_rabbitmq:127.0.0.1",
|
||||
"mythic_nginx:127.0.0.1",
|
||||
"mythic_react:127.0.0.1",
|
||||
"mythic_documentation:127.0.0.1",
|
||||
"mythic_graphql:127.0.0.1",
|
||||
"mythic_jupyter:127.0.0.1",
|
||||
"mythic_postgres:127.0.0.1",
|
||||
}
|
||||
pStruct["extra_hosts"] = getExtraHosts()
|
||||
}
|
||||
|
||||
if _, ok := pStruct["environment"]; ok {
|
||||
@@ -773,10 +753,12 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
}
|
||||
}
|
||||
case "mythic_sync":
|
||||
pStruct["depends_on"] = map[string]map[string]string{
|
||||
"mythic_nginx": {
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
if isServiceInternal("mythic_nginx") {
|
||||
pStruct["depends_on"] = map[string]map[string]string{
|
||||
"mythic_nginx": {"condition": "service_healthy"},
|
||||
}
|
||||
} else {
|
||||
delete(pStruct, "depends_on")
|
||||
}
|
||||
absPath, err := filepath.Abs(filepath.Join(manager.GetManager().GetPathTo3rdPartyServicesOnDisk(), service))
|
||||
if err != nil {
|
||||
@@ -821,16 +803,7 @@ func AddMythicService(service string, removeVolume bool) {
|
||||
delete(pStruct, "extra_hosts")
|
||||
} else {
|
||||
pStruct["network_mode"] = "host"
|
||||
pStruct["extra_hosts"] = []string{
|
||||
"mythic_server:127.0.0.1",
|
||||
"mythic_rabbitmq:127.0.0.1",
|
||||
"mythic_nginx:127.0.0.1",
|
||||
"mythic_react:127.0.0.1",
|
||||
"mythic_documentation:127.0.0.1",
|
||||
"mythic_graphql:127.0.0.1",
|
||||
"mythic_jupyter:127.0.0.1",
|
||||
"mythic_postgres:127.0.0.1",
|
||||
}
|
||||
pStruct["extra_hosts"] = getExtraHosts()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -862,29 +835,25 @@ func Add3rdPartyService(service string, additionalConfigs map[string]interface{}
|
||||
"args": config.GetBuildArguments(),
|
||||
}
|
||||
existingConfig["network_mode"] = "host"
|
||||
existingConfig["extra_hosts"] = []string{
|
||||
"mythic_server:127.0.0.1",
|
||||
"mythic_rabbitmq:127.0.0.1",
|
||||
"mythic_nginx:127.0.0.1",
|
||||
"mythic_react:127.0.0.1",
|
||||
"mythic_documentation:127.0.0.1",
|
||||
"mythic_graphql:127.0.0.1",
|
||||
"mythic_jupyter:127.0.0.1",
|
||||
"mythic_postgres:127.0.0.1",
|
||||
existingConfig["extra_hosts"] = getExtraHosts()
|
||||
|
||||
dependsOn := map[string]map[string]string{}
|
||||
if isServiceInternal("mythic_server") {
|
||||
dependsOn["mythic_server"] = map[string]string{"condition": "service_healthy"}
|
||||
}
|
||||
existingConfig["depends_on"] = map[string]map[string]string{
|
||||
"mythic_server": {
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_rabbitmq": {
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_nginx": {
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
"mythic_graphql": {
|
||||
"condition": "service_healthy",
|
||||
},
|
||||
if isServiceInternal("mythic_rabbitmq") {
|
||||
dependsOn["mythic_rabbitmq"] = map[string]string{"condition": "service_healthy"}
|
||||
}
|
||||
if isServiceInternal("mythic_graphql") {
|
||||
dependsOn["mythic_graphql"] = map[string]string{"condition": "service_healthy"}
|
||||
}
|
||||
if isServiceInternal("mythic_nginx") {
|
||||
dependsOn["mythic_nginx"] = map[string]string{"condition": "service_healthy"}
|
||||
}
|
||||
if len(dependsOn) > 0 {
|
||||
existingConfig["depends_on"] = dependsOn
|
||||
} else {
|
||||
delete(existingConfig, "depends_on")
|
||||
}
|
||||
agentConfigs := config.GetConfigStrings([]string{fmt.Sprintf("%s_.*", service)})
|
||||
agentUseBuildContextKey := fmt.Sprintf("%s_use_build_context", service)
|
||||
@@ -893,7 +862,7 @@ func Add3rdPartyService(service string, additionalConfigs map[string]interface{}
|
||||
if useBuildContext, ok := agentConfigs[agentUseBuildContextKey]; ok {
|
||||
if useBuildContext == "false" {
|
||||
delete(existingConfig, "build")
|
||||
existingConfig["image"] = agentConfigs[agentRemoteImageKey]
|
||||
existingConfig["image"] = applyImageMirror(agentConfigs[agentRemoteImageKey], config.GetMythicEnv().GetString("mythic_docker_image_mirror"))
|
||||
}
|
||||
}
|
||||
if useVolume, ok := agentConfigs[agentUseVolumeKey]; ok {
|
||||
@@ -983,3 +952,36 @@ func Initialize() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// applyImageMirror rewrites imageURL to be served from mirror by stripping the
|
||||
// upstream registry host (the first '/'-separated segment, when it contains '.'
|
||||
// or ':' or equals 'localhost' per Docker's reference parsing) and prepending
|
||||
// mirror. An empty mirror returns imageURL unchanged.
|
||||
func applyImageMirror(imageURL, mirror string) string {
|
||||
mirror = strings.TrimSpace(mirror)
|
||||
mirror = strings.TrimRight(mirror, "/")
|
||||
if mirror == "" {
|
||||
return imageURL
|
||||
}
|
||||
if idx := strings.Index(imageURL, "/"); idx > 0 {
|
||||
first := imageURL[:idx]
|
||||
if strings.ContainsAny(first, ".:") || first == "localhost" {
|
||||
return mirror + "/" + imageURL[idx+1:]
|
||||
}
|
||||
}
|
||||
return mirror + "/" + imageURL
|
||||
}
|
||||
|
||||
func isServiceInternal(service string) bool {
|
||||
return manager.GetManager().IsServiceInternal(service)
|
||||
}
|
||||
|
||||
func getExtraHosts() []string {
|
||||
extraHosts := []string{}
|
||||
for _, service := range config.MythicPossibleServices {
|
||||
if isServiceInternal(service) {
|
||||
extraHosts = append(extraHosts, fmt.Sprintf("%s:127.0.0.1", service))
|
||||
}
|
||||
}
|
||||
return extraHosts
|
||||
}
|
||||
@@ -156,6 +156,10 @@ func Status(verbose bool) {
|
||||
log.Printf("\n[*] MythicServer is currently listening on localhost. If you have a remote Service, they will be unable to connect (i.e. one running on another server)")
|
||||
log.Printf("\n Use 'sudo ./mythic-cli config set mythic_server_bind_localhost_only false' and restart mythic ('sudo ./mythic-cli restart') to change this\n")
|
||||
}
|
||||
if mirror := mythicEnv.GetString("mythic_docker_image_mirror"); mirror != "" {
|
||||
log.Printf("\n[*] Image mirror is active: pulling all images from \"%s\" instead of their upstream registries.", mirror)
|
||||
log.Printf("\n Use 'sudo ./mythic-cli config set mythic_docker_image_mirror \"\"' and restart mythic ('sudo ./mythic-cli restart') to disable.\n")
|
||||
}
|
||||
log.Printf("[*] If you are using a remote PayloadType or C2Profile, they will need certain environment variables to properly connect to Mythic.\n")
|
||||
log.Printf(" Use 'sudo ./mythic-cli config service' for configs for these services.\n")
|
||||
}
|
||||
|
||||
@@ -112,6 +112,9 @@ func updateNginxBlockLists() {
|
||||
outputString += "deny all;"
|
||||
if !config.GetMythicEnv().GetBool("nginx_use_volume") {
|
||||
ipFilePath := filepath.Join(utils.GetCwdFromExe(), "nginx-docker", "config", "blockips.conf")
|
||||
if err := os.MkdirAll(filepath.Dir(ipFilePath), 0755); err != nil {
|
||||
log.Fatalf("[-] Failed to create nginx-docker/config directory for block list file: %v\n", err)
|
||||
}
|
||||
if err := os.WriteFile(ipFilePath, []byte(outputString), 0600); err != nil {
|
||||
log.Fatalf("[-] Failed to write out block list file: %v\n", err)
|
||||
}
|
||||
|
||||
@@ -92,6 +92,18 @@ func (d *DockerComposeManager) IsServiceRunning(service string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServiceInternal checks if a service is configured to run locally in docker vs externally
|
||||
func (d *DockerComposeManager) IsServiceInternal(service string) bool {
|
||||
if !utils.StringInSlice(service, config.MythicPossibleServices) {
|
||||
return true
|
||||
}
|
||||
intendedServices, err := config.GetIntendedMythicServiceNames()
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return utils.StringInSlice(service, intendedServices)
|
||||
}
|
||||
|
||||
// DoesImageExist use Docker API to check existing images for the specified name
|
||||
func (d *DockerComposeManager) DoesImageExist(service string) bool {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
@@ -624,9 +636,9 @@ func (d *DockerComposeManager) TestPorts(services []string) {
|
||||
var removeServices []string
|
||||
mythicEnv := config.GetMythicEnv()
|
||||
for key, val := range portChecks {
|
||||
// only check ports for services we're about to start
|
||||
// only check ports for services we're about to start that are running locally
|
||||
if utils.StringInSlice(val[1], services) {
|
||||
if mythicEnv.GetString(key) == val[1] || mythicEnv.GetString(key) == "127.0.0.1" {
|
||||
if d.IsServiceInternal(val[1]) {
|
||||
addServices = append(addServices, val[1])
|
||||
p, err := net.Listen("tcp", ":"+strconv.Itoa(mythicEnv.GetInt(val[0])))
|
||||
if err != nil {
|
||||
@@ -1413,7 +1425,7 @@ func (d *DockerComposeManager) runDockerCompose(args []string) error {
|
||||
return err
|
||||
}
|
||||
if processState.ExitCode() != 0 {
|
||||
return err
|
||||
return fmt.Errorf("docker-compose %v exited with code %d", args, processState.ExitCode())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -12,6 +12,8 @@ type CLIManager interface {
|
||||
GetManagerName() string
|
||||
// IsServiceRunning checks if a service by the specified name is currently running or not
|
||||
IsServiceRunning(service string) bool
|
||||
// IsServiceInternal checks if a service is configured to run locally in docker vs externally
|
||||
IsServiceInternal(service string) bool
|
||||
// CheckRequiredManagerVersion checks if the version of the management software installed is a valid version or not
|
||||
CheckRequiredManagerVersion() bool
|
||||
// GenerateRequiredConfig creates any necessary base configuration files needed by the manager, like a docker-compose.yml file
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/MythicMeta/Mythic_CLI/cmd/config"
|
||||
"github.com/MythicMeta/Mythic_CLI/cmd/internal"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -31,6 +33,6 @@ func start(cmd *cobra.Command, args []string) {
|
||||
keepVolume = !config.GetMythicEnv().GetBool("REBUILD_ON_START")
|
||||
}
|
||||
if err := internal.ServiceStart(args, localKeepVolume); err != nil {
|
||||
|
||||
log.Fatalf("[-] Failed to start Mythic: %v\n", err)
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,9 @@ func init() {
|
||||
}
|
||||
|
||||
func updateCheck(cmd *cobra.Command, args []string) {
|
||||
if mirror := config.GetMythicEnv().GetString("mythic_docker_image_mirror"); mirror != "" {
|
||||
log.Printf("[*] mythic_docker_image_mirror is set to %q; image URLs printed below are the upstream sources used for version checking, not the mirrored locations actually pulled by docker compose\n", mirror)
|
||||
}
|
||||
var tr = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
+8
-14
@@ -27,12 +27,9 @@ require (
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
@@ -40,24 +37,21 @@ require (
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
|
||||
go.opentelemetry.io/otel v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.42.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.1 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
)
|
||||
+38
-140
@@ -2,8 +2,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
@@ -12,24 +12,15 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0=
|
||||
github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4=
|
||||
github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@@ -38,8 +29,6 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -49,200 +38,109 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
||||
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
||||
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
|
||||
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 h1:Mbi5PKN7u322woPa85d7ebZ+SOvEoPvoiBu+ryHWgfA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
|
||||
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
|
||||
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||
google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
|
||||
google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k=
|
||||
gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
|
||||
@@ -24,12 +24,9 @@ select_permissions:
|
||||
- end_time
|
||||
- start_time
|
||||
filter:
|
||||
_or:
|
||||
- payloadtype_id:
|
||||
_is_null: false
|
||||
- payload:
|
||||
operation_id:
|
||||
_in: X-Hasura-operations
|
||||
- payload:
|
||||
operation_id:
|
||||
_eq: X-Hasura-current-operation-id
|
||||
- role: mythic_admin
|
||||
permission:
|
||||
columns:
|
||||
@@ -45,12 +42,9 @@ select_permissions:
|
||||
- end_time
|
||||
- start_time
|
||||
filter:
|
||||
_or:
|
||||
- payloadtype_id:
|
||||
_is_null: false
|
||||
- payload:
|
||||
operation_id:
|
||||
_in: X-Hasura-operations
|
||||
- payload:
|
||||
operation_id:
|
||||
_eq: X-Hasura-current-operation-id
|
||||
- role: operation_admin
|
||||
permission:
|
||||
columns:
|
||||
@@ -66,12 +60,9 @@ select_permissions:
|
||||
- end_time
|
||||
- start_time
|
||||
filter:
|
||||
_or:
|
||||
- payloadtype_id:
|
||||
_is_null: false
|
||||
- payload:
|
||||
operation_id:
|
||||
_in: X-Hasura-operations
|
||||
- payload:
|
||||
operation_id:
|
||||
_eq: X-Hasura-current-operation-id
|
||||
- role: operator
|
||||
permission:
|
||||
columns:
|
||||
@@ -87,12 +78,9 @@ select_permissions:
|
||||
- end_time
|
||||
- start_time
|
||||
filter:
|
||||
_or:
|
||||
- payloadtype_id:
|
||||
_is_null: false
|
||||
- payload:
|
||||
operation_id:
|
||||
_in: X-Hasura-operations
|
||||
- payload:
|
||||
operation_id:
|
||||
_eq: X-Hasura-current-operation-id
|
||||
- role: spectator
|
||||
permission:
|
||||
columns:
|
||||
@@ -108,9 +96,6 @@ select_permissions:
|
||||
- end_time
|
||||
- start_time
|
||||
filter:
|
||||
_or:
|
||||
- payloadtype_id:
|
||||
_is_null: false
|
||||
- payload:
|
||||
operation_id:
|
||||
_in: X-Hasura-operations
|
||||
- payload:
|
||||
operation_id:
|
||||
_eq: X-Hasura-current-operation-id
|
||||
@@ -36,6 +36,11 @@ func Initialize() {
|
||||
logging.LogInfo("Disconnecting from database and reconnecting to load new schema")
|
||||
DB.Close()
|
||||
DB = getNewDbConnection()
|
||||
if utils.MythicConfig.PostgresDebug {
|
||||
if _, err := DB.Exec("CREATE EXTENSION IF NOT EXISTS pg_stat_statements SCHEMA public;"); err != nil {
|
||||
logging.LogWarning("Failed to create pg_stat_statements extension (requires superuser)", "error", err.Error())
|
||||
}
|
||||
}
|
||||
// we need to initialize the admin user and operation
|
||||
salt := uuid.NewString()
|
||||
newUser := databaseStructs.Operator{
|
||||
@@ -161,12 +166,13 @@ func checkDBConnection() {
|
||||
func getNewDbConnection() *sqlx.DB {
|
||||
for {
|
||||
logging.LogInfo("Attempting to connect to database...", "host", utils.MythicConfig.PostgresHost, "port", utils.MythicConfig.PostgresPort)
|
||||
conn, err := sqlx.Connect("postgres", fmt.Sprintf("user='%s' password='%s' host='%s' port='%d' dbname='%s' sslmode=disable connect_timeout=10",
|
||||
conn, err := sqlx.Connect("postgres", fmt.Sprintf("user='%s' password='%s' host='%s' port='%d' dbname='%s' sslmode=%s connect_timeout=10",
|
||||
utils.MythicConfig.PostgresUser,
|
||||
utils.MythicConfig.PostgresPassword,
|
||||
utils.MythicConfig.PostgresHost,
|
||||
utils.MythicConfig.PostgresPort,
|
||||
utils.MythicConfig.PostgresDB),
|
||||
utils.MythicConfig.PostgresDB,
|
||||
utils.MythicConfig.PostgresSSLMode),
|
||||
)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to connect to database", "host", utils.MythicConfig.PostgresHost, "port", utils.MythicConfig.PostgresPort)
|
||||
|
||||
@@ -4847,8 +4847,6 @@ ALTER TABLE ONLY public.wrappedpayloadtypes
|
||||
ALTER TABLE ONLY public.wrappedpayloadtypes
|
||||
ADD CONSTRAINT wrappedpayloadtypes_wrapper_id_fkey FOREIGN KEY (wrapper_id) REFERENCES public.payloadtype(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE EXTENSION pg_stat_statements SCHEMA public;
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
@@ -165,35 +165,6 @@ func HashUserPassword(databaseOperator databaseStructs.Operator, password string
|
||||
return fmt.Sprintf("%x", hashBytes)
|
||||
}
|
||||
|
||||
func ResolveAllOperationsMessage(message string, operationID int) {
|
||||
/*
|
||||
Resolve a message in all operation's event logs if operationID is 0, otherwise just resolve it to the specific operation.
|
||||
*/
|
||||
operations := []databaseStructs.Operation{}
|
||||
if err := DB.Select(&operations, `SELECT id FROM operation WHERE complete=false`); err != nil {
|
||||
logging.LogError(err, "Failed to get operations for ResolveAllOperationsMessage", "data", message)
|
||||
return
|
||||
}
|
||||
for _, operation := range operations {
|
||||
if operationID == 0 || operation.ID == operationID {
|
||||
// this is the operation we're interested in
|
||||
updateObject := databaseStructs.Operationeventlog{
|
||||
Message: message,
|
||||
OperationID: operationID,
|
||||
Level: MESSAGE_LEVEL_INFO,
|
||||
}
|
||||
if operationID == 0 {
|
||||
updateObject.OperationID = operation.ID
|
||||
}
|
||||
if _, err := DB.NamedExec(`UPDATE operationeventlog SET
|
||||
resolved=true
|
||||
WHERE warning=true AND resolved=false AND deleted=false AND message=:message AND operation_id=:operation_id`, updateObject); err != nil {
|
||||
logging.LogError(err, "Failed to resolve message")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func AssignNewOperatorAllBrowserScripts(userID int) {
|
||||
browserscripts := []databaseStructs.Browserscript{}
|
||||
if err := DB.Select(&browserscripts, `SELECT
|
||||
|
||||
@@ -300,7 +300,7 @@ func checkContainerStatus() {
|
||||
0, fmt.Sprintf("%s_container_down", container), database.MESSAGE_LEVEL_INFO, true)
|
||||
go updateDownContainerBuildingPayloads(container)
|
||||
} else {
|
||||
go database.ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
|
||||
go ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
|
||||
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(container)
|
||||
}
|
||||
} else {
|
||||
@@ -332,7 +332,7 @@ func checkContainerStatus() {
|
||||
getDownContainerMessage(container),
|
||||
0, fmt.Sprintf("%s_container_down", container), database.MESSAGE_LEVEL_INFO, true)
|
||||
} else {
|
||||
go database.ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
|
||||
go ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
|
||||
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(container)
|
||||
}
|
||||
} else {
|
||||
@@ -363,7 +363,7 @@ func checkContainerStatus() {
|
||||
getDownContainerMessage(container),
|
||||
0, fmt.Sprintf("%s_container_down", container), database.MESSAGE_LEVEL_INFO, true)
|
||||
} else {
|
||||
go database.ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
|
||||
go ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
|
||||
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(container)
|
||||
}
|
||||
} else {
|
||||
@@ -395,7 +395,7 @@ func checkContainerStatus() {
|
||||
getDownContainerMessage(container),
|
||||
0, fmt.Sprintf("%s_container_down", container), database.MESSAGE_LEVEL_INFO, true)
|
||||
} else {
|
||||
go database.ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
|
||||
go ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
|
||||
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(container)
|
||||
}
|
||||
} else {
|
||||
@@ -426,7 +426,7 @@ func checkContainerStatus() {
|
||||
getDownContainerMessage(container),
|
||||
0, fmt.Sprintf("%s_container_down", container), database.MESSAGE_LEVEL_INFO, true)
|
||||
} else {
|
||||
go database.ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
|
||||
go ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
|
||||
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(container)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -86,8 +86,9 @@ var pushC2StreamingDisconnectNotification = make(chan int, 100)
|
||||
|
||||
func Initialize() {
|
||||
RabbitMQConnection.channelMutexMap = make(map[string]*channelMutex)
|
||||
go invalidateAllSpectatorAPITokens()
|
||||
go InvalidateOperationEventLogCacheMap()
|
||||
invalidateAllNonUserAPITokens()
|
||||
InvalidateOperationEventLogCacheMap()
|
||||
go listenForOperationsMessages()
|
||||
go listenForWriteDownloadChunkToLocalDisk()
|
||||
go listenForFileBrowserData()
|
||||
go listenForAsyncAgentMessagePostResponseContent()
|
||||
@@ -109,10 +110,10 @@ func Initialize() {
|
||||
go listenForEvents()
|
||||
go initializeEventGroupCronSchedulesOnStart()
|
||||
// start listening for new messages from push c2 profiles, needs gRPC initialized first
|
||||
go listenForPushConnectDisconnectMessages()
|
||||
go processAgentMessageFromPushC2()
|
||||
go interceptProxyDataToAgentForPushC2()
|
||||
go checkIfActiveCallbacksAreAliveForever()
|
||||
go listenForPushConnectDisconnectMessages()
|
||||
go func() {
|
||||
// wait 20s for things to stabilize a bit, then send a startup message
|
||||
time.Sleep(time.Second * 30)
|
||||
@@ -171,9 +172,9 @@ func emitStartupMessages() {
|
||||
|
||||
}
|
||||
|
||||
func invalidateAllSpectatorAPITokens() {
|
||||
func invalidateAllNonUserAPITokens() {
|
||||
_, err := database.DB.Exec(`UPDATE apitokens
|
||||
SET deleted=true, active=true WHERE token_type=$1 OR token_type=$2 OR token_type=$3`,
|
||||
SET deleted=true, active=false WHERE token_type=$1 OR token_type=$2 OR token_type=$3`,
|
||||
mythicjwt.AUTH_METHOD_GRAPHQL_SPECTATOR, mythicjwt.AUTH_METHOD_TASK, mythicjwt.AUTH_METHOD_EVENT)
|
||||
if err != nil {
|
||||
logging.LogError(err, "failed to mark all spectator tokens as deleted and inactive")
|
||||
|
||||
@@ -191,7 +191,7 @@ func c2Sync(in C2SyncMessage) error {
|
||||
return err
|
||||
}
|
||||
go SendAllOperationsMessage(fmt.Sprintf("Successfully synced %s with container version %s", c2Profile.Name, in.ContainerVersion), 0, "debug", database.MESSAGE_LEVEL_DEBUG, false)
|
||||
go database.ResolveAllOperationsMessage(getDownContainerMessage(c2Profile.Name), 0)
|
||||
go ResolveAllOperationsMessage(getDownContainerMessage(c2Profile.Name), 0)
|
||||
go autoStartC2Profile(c2Profile, false)
|
||||
if newProfile {
|
||||
go reSyncPayloadTypes()
|
||||
@@ -251,6 +251,7 @@ func updateC2Parameters(in C2SyncMessage, c2Profile databaseStructs.C2profile) e
|
||||
logging.LogError(err, "Failed to fetch c2 parameters for c2 profile when syncing")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
found := false
|
||||
if err = rows.StructScan(&databaseParameter); err != nil {
|
||||
|
||||
@@ -134,7 +134,7 @@ func consumingServicesSync(in ConsumingContainerSyncMessage) error {
|
||||
}
|
||||
go SendAllOperationsMessage(fmt.Sprintf("Successfully synced %s with container version %s",
|
||||
consumingContainer.Name, in.ContainerVersion), 0, "debug", database.MESSAGE_LEVEL_DEBUG, false)
|
||||
go database.ResolveAllOperationsMessage(getDownContainerMessage(consumingContainer.Name), 0)
|
||||
go ResolveAllOperationsMessage(getDownContainerMessage(consumingContainer.Name), 0)
|
||||
checkContainerStatusAddConsumingContainerChannel <- consumingContainer
|
||||
// update eventgroup consumingcontainer mappings
|
||||
eventing.UpdateEventGroupConsumingContainersMappingByConsumingContainer(consumingContainer)
|
||||
|
||||
@@ -190,7 +190,7 @@ func customBrowserSync(in CustomBrowserSyncMessage) error {
|
||||
return err
|
||||
}
|
||||
go SendAllOperationsMessage(fmt.Sprintf("Successfully synced %s with container version %s", customSyncBrowser.Name, in.ContainerVersion), 0, "debug", database.MESSAGE_LEVEL_DEBUG, false)
|
||||
go database.ResolveAllOperationsMessage(getDownContainerMessage(customSyncBrowser.Name), 0)
|
||||
go ResolveAllOperationsMessage(getDownContainerMessage(customSyncBrowser.Name), 0)
|
||||
|
||||
checkContainerStatusAddCustomBrowserChannel <- customSyncBrowser
|
||||
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(customSyncBrowser.Name)
|
||||
|
||||
@@ -78,6 +78,7 @@ func MythicRPCArtifactSearch(input MythicRPCArtifactSearchMessage) MythicRPCArti
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
result := MythicRPCArtifactSearchArtifactData{}
|
||||
searchResult := databaseStructs.Taskartifact{}
|
||||
|
||||
@@ -66,6 +66,7 @@ func MythicRPCCallbackEdgeSearch(input MythicRPCCallbackEdgeSearchMessage) Mythi
|
||||
return response
|
||||
}
|
||||
searchResults := databaseStructs.Callbackgraphedge{}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
result := MythicRPCCallbackEdgeSearchMessageResult{}
|
||||
err = rows.StructScan(&searchResults)
|
||||
|
||||
@@ -181,6 +181,7 @@ func MythicRPCCallbackSearch(input MythicRPCCallbackSearchMessage) MythicRPCCall
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
result := MythicRPCCallbackSearchMessageResult{}
|
||||
err = rows.StructScan(&searchResults)
|
||||
|
||||
@@ -137,6 +137,7 @@ func MythicRPCCustomBrowserSearch(input MythicRPCCustomBrowserSearchMessage) Myt
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
searchResult := databaseStructs.MythicTree{}
|
||||
err = rows.StructScan(&searchResult)
|
||||
|
||||
@@ -151,6 +151,7 @@ func associateBuildParametersWithPayload(databasePayload databaseStructs.Payload
|
||||
logging.LogError(err, "Failed to get build parameters from database when trying to build payload")
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
err = rows.StructScan(&databaseBuildParameter)
|
||||
if err != nil {
|
||||
@@ -298,6 +299,7 @@ func associateC2ProfilesWithPayload(databasePayload databaseStructs.Payload, c2P
|
||||
logging.LogError(err, "Failed to get c2 parameters from database when trying to build payload")
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
err = rows.StructScan(&databaseC2ProfileParameter)
|
||||
if err != nil {
|
||||
|
||||
@@ -110,6 +110,7 @@ func MythicRPCProcessSearch(input MythicRPCProcessSearchMessage) MythicRPCProces
|
||||
response.Error = err.Error()
|
||||
return response
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
searchResult := databaseStructs.MythicTree{}
|
||||
if err = rows.StructScan(&searchResult); err != nil {
|
||||
|
||||
@@ -157,6 +157,7 @@ func updateLoadedCommandsFromPayloadBuild(databasePayload databaseStructs.Payloa
|
||||
logging.LogError(err, "Failed to get payload commands when trying to update loaded commands for payload build response")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&databasePayloadCommand); err != nil {
|
||||
logging.LogError(err, "Failed to get row from payloadcommand when trying to process payload build response")
|
||||
|
||||
@@ -450,7 +450,7 @@ func payloadTypeSync(in PayloadTypeSyncMessage) error {
|
||||
InvalidateAllCachedUUIDInfo()
|
||||
}
|
||||
go SendAllOperationsMessage(fmt.Sprintf("Successfully synced %s with container version %s", payloadtype.Name, in.ContainerVersion), 0, "debug", database.MESSAGE_LEVEL_DEBUG, false)
|
||||
go database.ResolveAllOperationsMessage(getDownContainerMessage(payloadtype.Name), 0)
|
||||
go ResolveAllOperationsMessage(getDownContainerMessage(payloadtype.Name), 0)
|
||||
checkContainerStatusAddPtChannel <- payloadtype
|
||||
if !in.ForcedSync {
|
||||
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(payloadtype.Name)
|
||||
@@ -479,6 +479,7 @@ func updatePayloadTypeBuildParameters(in PayloadTypeSyncMessage, payloadtype dat
|
||||
logging.LogError(err, "Failed to fetch build parameters for payloadtype when syncing")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
found := false
|
||||
if err = rows.StructScan(&databaseParameter); err != nil {
|
||||
@@ -624,6 +625,7 @@ func updatePayloadTypeC2Profiles(in PayloadTypeSyncMessage, payloadtype database
|
||||
logging.LogError(err, "Failed to get payloadtypec2profile from database")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&databaseC2Profile); err != nil {
|
||||
logging.LogError(err, "Failed to get row from payloadtypec2profile")
|
||||
@@ -684,6 +686,7 @@ func updatePayloadTypeWrappers(in PayloadTypeSyncMessage, payloadtype databaseSt
|
||||
logging.LogError(err, "Failed to get wrappedpayloadtypes from database")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&databaseWrapper); err != nil {
|
||||
logging.LogError(err, "Failed to get row from wrappedpayloadtypes for importing new payloadtype")
|
||||
@@ -715,6 +718,7 @@ func updatePayloadTypeWrappers(in PayloadTypeSyncMessage, payloadtype databaseSt
|
||||
logging.LogError(err, "Failed to get wrappedpayloadtypes from database")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&databaseWrapper); err != nil {
|
||||
logging.LogError(err, "Failed to get row from wrappedpayloadtypes for importing new payloadtype")
|
||||
@@ -815,6 +819,7 @@ func updatePayloadTypeCommands(in PayloadTypeSyncMessage, payloadtype databaseSt
|
||||
logging.LogError(err, "Failed to fetch commands for payloadtype when syncing")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&databaseCommand); err != nil {
|
||||
logging.LogError(err, "Failed to parse command into structure when syncing payloadtype")
|
||||
@@ -981,6 +986,7 @@ func updatePayloadTypeCommandParameters(in PayloadTypeSyncMessage, payloadtype d
|
||||
logging.LogError(err, "Failed to fetch command parameters for command when syning")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&databaseParameter); err != nil {
|
||||
logging.LogError(err, "Failed to parse commandparameter into structure when syncing command")
|
||||
@@ -1142,6 +1148,7 @@ func updatePayloadTypeCommandBrowserScripts(in PayloadTypeSyncMessage, syncComma
|
||||
return err
|
||||
} else {
|
||||
found := false
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&databaseBrowserScript); err != nil {
|
||||
logging.LogError(err, "Failed to parse browserscript into structure when syncing command")
|
||||
@@ -1212,6 +1219,7 @@ func addBrowserScriptToAllOperators(browserscript databaseStructs.Browserscript)
|
||||
logging.LogError(err, "Failed to get operators")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&operator); err != nil {
|
||||
logging.LogError(err, "Failed to parse operator into structure")
|
||||
@@ -1241,6 +1249,7 @@ func updateBrowserScriptForAllOperators(browserscript databaseStructs.Browserscr
|
||||
logging.LogError(err, "Failed to get operators")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&operatorScript); err != nil {
|
||||
logging.LogError(err, "Failed to parse browserscript into structure for updating scripts")
|
||||
@@ -1278,6 +1287,7 @@ func removeBrowserScriptFromAllOperators(browserscript databaseStructs.Browsersc
|
||||
logging.LogError(err, "Failed to get browserscripts for command", "command", browserscript.CommandID)
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&operatorScript); err != nil {
|
||||
logging.LogError(err, "Failed to parse operator into structure")
|
||||
@@ -1307,6 +1317,7 @@ func updatePayloadTypeCommandMitreAttack(in PayloadTypeSyncMessage, syncCommand
|
||||
logging.LogError(err, "Failed to get attackcommand from database")
|
||||
return err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
if err = rows.StructScan(&databaseMitreAttack); err != nil {
|
||||
logging.LogError(err, "Failed to parse mitre att&ck into structure")
|
||||
|
||||
@@ -56,7 +56,7 @@ func processTrSyncMessages(msg amqp.Delivery) interface{} {
|
||||
// successfully synced
|
||||
response.Success = true
|
||||
go SendAllOperationsMessage(fmt.Sprintf("Successfully synced %s with container version %s", trSyncMsg.Name, trSyncMsg.ContainerVersion), 0, "debug", database.MESSAGE_LEVEL_DEBUG, false)
|
||||
go database.ResolveAllOperationsMessage(getDownContainerMessage(trSyncMsg.Name), 0)
|
||||
go ResolveAllOperationsMessage(getDownContainerMessage(trSyncMsg.Name), 0)
|
||||
logging.LogDebug("Successfully synced", "service", trSyncMsg.Name)
|
||||
}
|
||||
return response
|
||||
|
||||
@@ -81,6 +81,7 @@ type PTTaskMessageTaskData struct {
|
||||
Status string `json:"status"`
|
||||
OriginalParams string `json:"original_params"`
|
||||
DisplayParams string `json:"display_params"`
|
||||
MythicParsedParams string `json:"mythic_parsed_params"`
|
||||
Comment string `json:"comment"`
|
||||
Stdout string `json:"stdout"`
|
||||
Stderr string `json:"stderr"`
|
||||
|
||||
+217
-111
@@ -1,6 +1,7 @@
|
||||
package rabbitmq
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -1088,6 +1089,7 @@ func GetTaskMessageTaskInformation(taskID int) PTTaskMessageTaskData {
|
||||
Status: databaseTask.Status,
|
||||
OriginalParams: databaseTask.OriginalParams,
|
||||
DisplayParams: databaseTask.DisplayParams,
|
||||
MythicParsedParams: databaseTask.MythicParsedParams,
|
||||
Comment: databaseTask.Comment,
|
||||
Stdout: databaseTask.Stdout,
|
||||
Stderr: databaseTask.Stderr,
|
||||
@@ -1245,6 +1247,8 @@ func GetSaveFilePath() (string, string, error) {
|
||||
|
||||
var operationsMap = make(map[int]databaseStructs.Operation)
|
||||
var operationsMapMutex sync.RWMutex
|
||||
var unresolvedErrors = make(map[int]map[string]map[string]int)
|
||||
var lastUpdateTime = time.Now()
|
||||
|
||||
func InvalidateOperationEventLogCacheMap() {
|
||||
operationsMapMutex.Lock()
|
||||
@@ -1260,123 +1264,225 @@ func InvalidateOperationEventLogCacheMap() {
|
||||
operationsMap[operation.ID] = operation
|
||||
}
|
||||
}
|
||||
func SendAllOperationsMessage(message string, operationID int, source string, messageLevel database.MESSAGE_TYPE, warning bool) {
|
||||
var operations []databaseStructs.Operation
|
||||
operationsMapMutex.RLock()
|
||||
if operationID == 0 {
|
||||
for _, operation := range operationsMap {
|
||||
operations = append(operations, operation)
|
||||
}
|
||||
} else {
|
||||
operation, ok := operationsMap[operationID]
|
||||
if !ok {
|
||||
operationsMapMutex.RUnlock()
|
||||
logging.LogError(nil, "unknown operation id, can't send message", "operation_id", operationID)
|
||||
return
|
||||
}
|
||||
operations = append(operations, operation)
|
||||
func checkUnresolvedError(operationID int, source string, level string) int {
|
||||
if _, ok := unresolvedErrors[operationID]; !ok {
|
||||
return 0
|
||||
}
|
||||
operationsMapMutex.RUnlock()
|
||||
sourceString := source
|
||||
if sourceString == "" {
|
||||
sourceString = uuid.NewString()
|
||||
if _, ok := unresolvedErrors[operationID][source]; !ok {
|
||||
return 0
|
||||
}
|
||||
for _, operation := range operations {
|
||||
if operationID == 0 || operation.ID == operationID {
|
||||
// this is the operation we're interested in
|
||||
if messageLevel == "warning" || warning {
|
||||
existingMessage := databaseStructs.Operationeventlog{}
|
||||
err := database.DB.Get(&existingMessage, `
|
||||
SELECT id, count, "message", source, "level" FROM operationeventlog WHERE
|
||||
warning=true and source=$1 and operation_id=$2 and resolved=false and deleted=false and "level"=$3
|
||||
`, sourceString, operation.ID, messageLevel)
|
||||
if !errors.Is(err, sql.ErrNoRows) && err != nil {
|
||||
logging.LogError(err, "Failed to query existing event log message")
|
||||
continue
|
||||
}
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
if messageLevel == "warning" {
|
||||
messageLevel = database.MESSAGE_LEVEL_INFO
|
||||
}
|
||||
newMessage := databaseStructs.Operationeventlog{
|
||||
Source: sourceString,
|
||||
Level: messageLevel,
|
||||
Warning: warning,
|
||||
Message: message,
|
||||
OperationID: operation.ID,
|
||||
Count: 1,
|
||||
}
|
||||
_, err = database.DB.NamedExec(`INSERT INTO operationeventlog
|
||||
(source, "level", "message", operation_id, count, warning)
|
||||
VALUES
|
||||
(:source, :level, :message, :operation_id, :count, :warning)`, newMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to create new operationeventlog message")
|
||||
continue
|
||||
}
|
||||
go RabbitMQConnection.EmitWebhookMessage(WebhookMessage{
|
||||
OperationID: operation.ID,
|
||||
OperationName: operation.Name,
|
||||
OperationWebhook: operation.Webhook,
|
||||
OperationChannel: operation.Channel,
|
||||
OperatorUsername: "",
|
||||
Action: WEBHOOK_TYPE_ALERT,
|
||||
Data: map[string]interface{}{
|
||||
"message": newMessage.Message,
|
||||
"source": newMessage.Source,
|
||||
"count": newMessage.Count,
|
||||
"timestamp": time.Now().UTC(),
|
||||
},
|
||||
})
|
||||
EventingChannel <- EventNotification{
|
||||
Trigger: eventing.TriggerAlert,
|
||||
OperationID: operation.ID,
|
||||
Outputs: map[string]interface{}{
|
||||
"alert": newMessage.Message,
|
||||
},
|
||||
}
|
||||
continue
|
||||
}
|
||||
// err was nil, so we did get a matching existing message
|
||||
existingMessage.Count += 1
|
||||
_, err = database.DB.NamedExec(`UPDATE operationeventlog SET
|
||||
"count"=:count
|
||||
WHERE id=:id`, existingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to increase count on operationeventlog")
|
||||
continue
|
||||
}
|
||||
if _, ok := unresolvedErrors[operationID][source][level]; !ok {
|
||||
return 0
|
||||
}
|
||||
return unresolvedErrors[operationID][source][level]
|
||||
}
|
||||
func addUnresolvedError(operationID int, source string, level string, messageID int) {
|
||||
if _, ok := unresolvedErrors[operationID]; !ok {
|
||||
unresolvedErrors[operationID] = make(map[string]map[string]int)
|
||||
}
|
||||
if _, ok := unresolvedErrors[operationID][source]; !ok {
|
||||
unresolvedErrors[operationID][source] = make(map[string]int)
|
||||
}
|
||||
unresolvedErrors[operationID][source][level] = messageID
|
||||
}
|
||||
func removeUnresolvedError(operationID int) {
|
||||
delete(unresolvedErrors, operationID)
|
||||
}
|
||||
|
||||
go RabbitMQConnection.EmitWebhookMessage(WebhookMessage{
|
||||
OperationID: operation.ID,
|
||||
OperationName: operation.Name,
|
||||
OperationWebhook: operation.Webhook,
|
||||
OperationChannel: operation.Channel,
|
||||
OperatorUsername: "",
|
||||
Action: WEBHOOK_TYPE_ALERT,
|
||||
Data: map[string]interface{}{
|
||||
"message": existingMessage.Message,
|
||||
"source": existingMessage.Source,
|
||||
"count": existingMessage.Count,
|
||||
"timestamp": time.Now().UTC(),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
newMessage := databaseStructs.Operationeventlog{
|
||||
Source: sourceString,
|
||||
Level: messageLevel,
|
||||
Warning: warning,
|
||||
Message: message,
|
||||
OperationID: operation.ID,
|
||||
Count: 1,
|
||||
type operationMessage struct {
|
||||
action string
|
||||
message string
|
||||
operationID int
|
||||
source string
|
||||
messageLevel database.MESSAGE_TYPE
|
||||
warning bool
|
||||
}
|
||||
|
||||
var operationMessageChannel = make(chan operationMessage, 100)
|
||||
|
||||
func listenForOperationsMessages() {
|
||||
timer := time.NewTimer(time.Second * 10)
|
||||
for {
|
||||
select {
|
||||
case msg := <-operationMessageChannel:
|
||||
/*
|
||||
Resolve a message in all operation's event logs if operationID is 0, otherwise just resolve it to the specific operation.
|
||||
*/
|
||||
var operations []databaseStructs.Operation
|
||||
operationsMapMutex.RLock()
|
||||
if msg.operationID == 0 {
|
||||
for _, operation := range operationsMap {
|
||||
operations = append(operations, operation)
|
||||
}
|
||||
if _, err := database.DB.NamedExec(`INSERT INTO operationeventlog
|
||||
(source, "level", "message", operation_id, count, warning)
|
||||
VALUES
|
||||
(:source, :level, :message, :operation_id, :count, :warning)`, newMessage); err != nil {
|
||||
logging.LogError(err, "Failed to create new operationeventlog message")
|
||||
} else {
|
||||
operation, ok := operationsMap[msg.operationID]
|
||||
if !ok {
|
||||
operationsMapMutex.RUnlock()
|
||||
logging.LogError(nil, "unknown operation id, can't send message", "operation_id", msg.operationID)
|
||||
continue
|
||||
}
|
||||
operations = append(operations, operation)
|
||||
}
|
||||
operationsMapMutex.RUnlock()
|
||||
switch msg.action {
|
||||
case "send":
|
||||
sourceString := msg.source
|
||||
if sourceString == "" {
|
||||
sourceString = uuid.NewString()
|
||||
}
|
||||
// hitting concurrency issues where like-messages aren't getting collapsed like they should
|
||||
var err error
|
||||
for _, operation := range operations {
|
||||
if msg.operationID == 0 || operation.ID == msg.operationID {
|
||||
// this is the operation we're interested in
|
||||
if msg.messageLevel == "warning" || msg.warning {
|
||||
existingMessage := databaseStructs.Operationeventlog{}
|
||||
existingMessage.ID = checkUnresolvedError(operation.ID, msg.source, msg.messageLevel)
|
||||
if existingMessage.ID == 0 {
|
||||
err = database.DB.Get(&existingMessage, `
|
||||
SELECT id, count, "message", source, "level" FROM operationeventlog WHERE
|
||||
warning=true and source=$1 and operation_id=$2 and resolved=false and deleted=false and "level"=$3
|
||||
`, sourceString, operation.ID, msg.messageLevel)
|
||||
if !errors.Is(err, sql.ErrNoRows) && err != nil {
|
||||
logging.LogError(err, "Failed to query existing event log message")
|
||||
continue
|
||||
}
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
var utf8Message = strings.ToValidUTF8(string(bytes.ReplaceAll([]byte(msg.message), []byte{0}, []byte{})), "*")
|
||||
if msg.messageLevel == "warning" {
|
||||
msg.messageLevel = database.MESSAGE_LEVEL_INFO
|
||||
}
|
||||
newMessage := databaseStructs.Operationeventlog{
|
||||
Source: sourceString,
|
||||
Level: msg.messageLevel,
|
||||
Warning: msg.warning,
|
||||
Message: utf8Message,
|
||||
OperationID: operation.ID,
|
||||
Count: 1,
|
||||
}
|
||||
_, err = database.DB.NamedExec(`INSERT INTO operationeventlog
|
||||
(source, "level", "message", operation_id, count, warning)
|
||||
VALUES
|
||||
(:source, :level, :message, :operation_id, :count, :warning)`, newMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to create new operationeventlog message")
|
||||
continue
|
||||
}
|
||||
go RabbitMQConnection.EmitWebhookMessage(WebhookMessage{
|
||||
OperationID: operation.ID,
|
||||
OperationName: operation.Name,
|
||||
OperationWebhook: operation.Webhook,
|
||||
OperationChannel: operation.Channel,
|
||||
OperatorUsername: "",
|
||||
Action: WEBHOOK_TYPE_ALERT,
|
||||
Data: map[string]interface{}{
|
||||
"message": newMessage.Message,
|
||||
"source": newMessage.Source,
|
||||
"count": newMessage.Count,
|
||||
"timestamp": time.Now().UTC(),
|
||||
},
|
||||
})
|
||||
EventingChannel <- EventNotification{
|
||||
Trigger: eventing.TriggerAlert,
|
||||
OperationID: operation.ID,
|
||||
Outputs: map[string]interface{}{
|
||||
"alert": newMessage.Message,
|
||||
},
|
||||
}
|
||||
addUnresolvedError(operation.ID, msg.source, msg.messageLevel, existingMessage.ID)
|
||||
continue
|
||||
}
|
||||
addUnresolvedError(operation.ID, msg.source, msg.messageLevel, existingMessage.ID)
|
||||
}
|
||||
if time.Now().Sub(lastUpdateTime).Seconds() > 1 {
|
||||
lastUpdateTime = time.Now()
|
||||
// don't bother trying to update if it's less than once a second
|
||||
_, err = database.DB.NamedExec(`UPDATE operationeventlog SET
|
||||
"count"="count" + 1 WHERE id=:id`, existingMessage)
|
||||
if err != nil {
|
||||
logging.LogError(err, "Failed to increase count on operationeventlog")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
go RabbitMQConnection.EmitWebhookMessage(WebhookMessage{
|
||||
OperationID: operation.ID,
|
||||
OperationName: operation.Name,
|
||||
OperationWebhook: operation.Webhook,
|
||||
OperationChannel: operation.Channel,
|
||||
OperatorUsername: "",
|
||||
Action: WEBHOOK_TYPE_ALERT,
|
||||
Data: map[string]interface{}{
|
||||
"message": existingMessage.Message,
|
||||
"source": existingMessage.Source,
|
||||
"count": existingMessage.Count,
|
||||
"timestamp": time.Now().UTC(),
|
||||
},
|
||||
})
|
||||
|
||||
*/
|
||||
continue
|
||||
}
|
||||
var utf8Message = strings.ToValidUTF8(string(bytes.ReplaceAll([]byte(msg.message), []byte{0}, []byte{})), "*")
|
||||
newMessage := databaseStructs.Operationeventlog{
|
||||
Source: sourceString,
|
||||
Level: msg.messageLevel,
|
||||
Warning: msg.warning,
|
||||
Message: utf8Message,
|
||||
OperationID: operation.ID,
|
||||
Count: 1,
|
||||
}
|
||||
if _, err := database.DB.NamedExec(`INSERT INTO operationeventlog
|
||||
(source, "level", "message", operation_id, count, warning)
|
||||
VALUES
|
||||
(:source, :level, :message, :operation_id, :count, :warning)`, newMessage); err != nil {
|
||||
logging.LogError(err, "Failed to create new operationeventlog message")
|
||||
}
|
||||
}
|
||||
}
|
||||
case "remove":
|
||||
for _, operation := range operations {
|
||||
if msg.operationID == 0 || operation.ID == msg.operationID {
|
||||
// this is the operation we're interested in
|
||||
updateObject := databaseStructs.Operationeventlog{
|
||||
Message: msg.message,
|
||||
OperationID: msg.operationID,
|
||||
Level: database.MESSAGE_LEVEL_INFO,
|
||||
}
|
||||
if msg.operationID == 0 {
|
||||
updateObject.OperationID = operation.ID
|
||||
}
|
||||
if _, err := database.DB.NamedExec(`UPDATE operationeventlog SET
|
||||
resolved=true WHERE warning=true AND resolved=false AND deleted=false AND message=:message AND operation_id=:operation_id`, updateObject); err != nil {
|
||||
logging.LogError(err, "Failed to resolve message")
|
||||
}
|
||||
removeUnresolvedError(operation.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
case <-timer.C:
|
||||
if len(unresolvedErrors) > 0 {
|
||||
unresolvedErrors = make(map[int]map[string]map[string]int)
|
||||
}
|
||||
timer.Reset(30 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
func SendAllOperationsMessage(message string, operationID int, source string, messageLevel database.MESSAGE_TYPE, warning bool) {
|
||||
operationMessageChannel <- operationMessage{
|
||||
action: "send",
|
||||
message: message,
|
||||
source: source,
|
||||
operationID: operationID,
|
||||
messageLevel: messageLevel,
|
||||
warning: warning,
|
||||
}
|
||||
}
|
||||
func ResolveAllOperationsMessage(message string, operationID int) {
|
||||
operationMessageChannel <- operationMessage{
|
||||
action: "remove",
|
||||
operationID: operationID,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,15 @@ import (
|
||||
"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"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
func emitTaskLog(taskId int) {
|
||||
task := databaseStructs.Task{}
|
||||
if err := database.DB.Get(&task, `SELECT
|
||||
task.operation_id,
|
||||
operation.name "operation.name",
|
||||
operator.username "operator.username"
|
||||
FROM task
|
||||
@@ -26,6 +29,7 @@ func emitTaskLog(taskId int) {
|
||||
Timestamp: time.Now().UTC(),
|
||||
Action: LOG_TYPE_TASK,
|
||||
Data: data,
|
||||
ServerName: utils.MythicConfig.GlobalServerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -48,6 +52,7 @@ func emitPayloadLog(payloadId int) {
|
||||
Timestamp: time.Now().UTC(),
|
||||
Action: LOG_TYPE_PAYLOAD,
|
||||
Data: payload,
|
||||
ServerName: utils.MythicConfig.GlobalServerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -71,6 +76,7 @@ func emitKeylogLog(keylogId int) {
|
||||
Timestamp: time.Now().UTC(),
|
||||
Action: LOG_TYPE_KEYLOG,
|
||||
Data: keylog,
|
||||
ServerName: utils.MythicConfig.GlobalServerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -93,6 +99,7 @@ func EmitCredentialLog(credentialId int) {
|
||||
Timestamp: time.Now().UTC(),
|
||||
Action: LOG_TYPE_CREDENTIAL,
|
||||
Data: credential,
|
||||
ServerName: utils.MythicConfig.GlobalServerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -116,6 +123,7 @@ func emitArtifactLog(artifactId int) {
|
||||
Timestamp: time.Now().UTC(),
|
||||
Action: LOG_TYPE_ARTIFACT,
|
||||
Data: artifact,
|
||||
ServerName: utils.MythicConfig.GlobalServerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -138,6 +146,7 @@ func EmitFileLog(fileId int) {
|
||||
Timestamp: time.Now().UTC(),
|
||||
Action: LOG_TYPE_FILE,
|
||||
Data: file,
|
||||
ServerName: utils.MythicConfig.GlobalServerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -160,6 +169,7 @@ func emitCallbackLog(callbackId int) {
|
||||
Timestamp: time.Now().UTC(),
|
||||
Action: LOG_TYPE_CALLBACK,
|
||||
Data: callback,
|
||||
ServerName: utils.MythicConfig.GlobalServerName,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -192,6 +202,7 @@ func emitResponseLog(responseId int) {
|
||||
"task_display_id": response.Task.DisplayID,
|
||||
"timestamp": response.Timestamp,
|
||||
},
|
||||
ServerName: utils.MythicConfig.GlobalServerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const mythicServerVersion = "3.4.31"
|
||||
const mythicServerVersion = "3.4.36"
|
||||
|
||||
type Config struct {
|
||||
// server configuration
|
||||
@@ -47,6 +47,8 @@ type Config struct {
|
||||
PostgresDB string
|
||||
PostgresUser string
|
||||
PostgresPassword string
|
||||
PostgresSSLMode string
|
||||
PostgresDebug bool
|
||||
|
||||
// jwt configuration
|
||||
JWTSecret []byte
|
||||
@@ -78,6 +80,8 @@ func Initialize() {
|
||||
mythicEnv.SetDefault("postgres_db", "mythic_db")
|
||||
mythicEnv.SetDefault("postgres_user", "mythic_user")
|
||||
mythicEnv.SetDefault("postgres_password", "")
|
||||
mythicEnv.SetDefault("postgres_sslmode", "disable")
|
||||
mythicEnv.SetDefault("postgres_debug", false)
|
||||
// rabbitmq configuration
|
||||
mythicEnv.SetDefault("rabbitmq_host", "mythic_rabbitmq")
|
||||
mythicEnv.SetDefault("rabbitmq_port", 5672)
|
||||
@@ -180,6 +184,8 @@ func setConfigFromEnv(mythicEnv *viper.Viper) {
|
||||
MythicConfig.PostgresDB = mythicEnv.GetString("postgres_db")
|
||||
MythicConfig.PostgresUser = mythicEnv.GetString("postgres_user")
|
||||
MythicConfig.PostgresPassword = mythicEnv.GetString("postgres_password")
|
||||
MythicConfig.PostgresSSLMode = mythicEnv.GetString("postgres_sslmode")
|
||||
MythicConfig.PostgresDebug = mythicEnv.GetBool("postgres_debug")
|
||||
// rabbitmq configuration
|
||||
MythicConfig.RabbitmqHost = mythicEnv.GetString("rabbitmq_host")
|
||||
MythicConfig.RabbitmqPort = mythicEnv.GetUint("rabbitmq_port")
|
||||
|
||||
@@ -37,9 +37,16 @@ func C2ProfileConfigCheckWebhook(c *gin.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
ginOperatorOperation, ok := c.Get("operatorOperation")
|
||||
if !ok {
|
||||
logging.LogError(nil, "Failed to get operatorOperation information for CreateC2ParameterInstanceWebhook")
|
||||
c.JSON(http.StatusOK, gin.H{"status": "error", "error": "Failed to get current operation. Is it set?"})
|
||||
return
|
||||
}
|
||||
operatorOperation := ginOperatorOperation.(*databaseStructs.Operatoroperation)
|
||||
// get the associated database information
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1`, input.Input.PayloadUUID); err != nil {
|
||||
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1 AND operation_id=$2`, input.Input.PayloadUUID, operatorOperation.CurrentOperation.ID); err != nil {
|
||||
logging.LogError(err, "Failed to find payload when doing a C2ProfileConfigCheckWebhook")
|
||||
}
|
||||
c2profileParameterInstances := []databaseStructs.C2profileparametersinstance{}
|
||||
|
||||
@@ -37,9 +37,16 @@ func C2ProfileGetIOCWebhook(c *gin.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
ginOperatorOperation, ok := c.Get("operatorOperation")
|
||||
if !ok {
|
||||
logging.LogError(nil, "Failed to get operatorOperation information for CreateC2ParameterInstanceWebhook")
|
||||
c.JSON(http.StatusOK, gin.H{"status": "error", "error": "Failed to get current operation. Is it set?"})
|
||||
return
|
||||
}
|
||||
operatorOperation := ginOperatorOperation.(*databaseStructs.Operatoroperation)
|
||||
// get the associated database information
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1`, input.Input.PayloadUUID); err != nil {
|
||||
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1 AND operation_id=$2`, input.Input.PayloadUUID, operatorOperation.CurrentOperation.ID); err != nil {
|
||||
logging.LogError(err, "Failed to find payload when doing a C2ProfileConfigCheckWebhook")
|
||||
}
|
||||
c2profileParameterInstances := []databaseStructs.C2profileparametersinstance{}
|
||||
|
||||
@@ -37,9 +37,16 @@ func C2ProfileRedirectRulesWebhook(c *gin.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
ginOperatorOperation, ok := c.Get("operatorOperation")
|
||||
if !ok {
|
||||
logging.LogError(nil, "Failed to get operatorOperation information for CreateC2ParameterInstanceWebhook")
|
||||
c.JSON(http.StatusOK, gin.H{"status": "error", "error": "Failed to get current operation. Is it set?"})
|
||||
return
|
||||
}
|
||||
operatorOperation := ginOperatorOperation.(*databaseStructs.Operatoroperation)
|
||||
// get the associated database information
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1`, input.Input.PayloadUUID); err != nil {
|
||||
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1 AND operation_id=$2`, input.Input.PayloadUUID, operatorOperation.CurrentOperation.ID); err != nil {
|
||||
logging.LogError(err, "Failed to find payload when doing a C2ProfileRedirectRulesWebhook")
|
||||
}
|
||||
c2profileParameterInstances := []databaseStructs.C2profileparametersinstance{}
|
||||
|
||||
@@ -37,9 +37,16 @@ func C2ProfileSampleMessageWebhook(c *gin.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
ginOperatorOperation, ok := c.Get("operatorOperation")
|
||||
if !ok {
|
||||
logging.LogError(nil, "Failed to get operatorOperation information for CreateC2ParameterInstanceWebhook")
|
||||
c.JSON(http.StatusOK, gin.H{"status": "error", "error": "Failed to get current operation. Is it set?"})
|
||||
return
|
||||
}
|
||||
operatorOperation := ginOperatorOperation.(*databaseStructs.Operatoroperation)
|
||||
// get the associated database information
|
||||
payload := databaseStructs.Payload{}
|
||||
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1`, input.Input.PayloadUUID); err != nil {
|
||||
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1 AND operation_id=$2`, input.Input.PayloadUUID, operatorOperation.CurrentOperation.ID); err != nil {
|
||||
logging.LogError(err, "Failed to find payload when doing a C2ProfileConfigCheckWebhook")
|
||||
}
|
||||
c2profileParameterInstances := []databaseStructs.C2profileparametersinstance{}
|
||||
|
||||
@@ -89,6 +89,7 @@ func GenerateAPITokenWebhook(c *gin.Context) {
|
||||
Status: "error",
|
||||
Error: "only admins can generate API tokens for bot accounts",
|
||||
})
|
||||
return
|
||||
}
|
||||
userID = targetOperator.ID
|
||||
defaultName = fmt.Sprintf("generated by %s", issuingUser.Username)
|
||||
|
||||
@@ -161,6 +161,7 @@ func setRoutes(r *gin.Engine) {
|
||||
// the /metadata endpoint responds with raw text of the metadata for an IDP to fetch if it can reach Mythic
|
||||
r.GET("/auth_metadata/:containerName/:IDPName/metadata", webcontroller.GetAuthContainerMetadataIDPEndpoint)
|
||||
r.GET("/auth_redirect/:containerName/:IDPName", webcontroller.GetAuthContainerRedirect)
|
||||
r.GET("/auth_acs/:containerName/:IDPName", webcontroller.ProcessIDPResponse)
|
||||
r.POST("/auth_acs/:containerName/:IDPName", webcontroller.ProcessIDPResponse)
|
||||
// unauthenticated file download based on file UUID
|
||||
// this is for payload hosting and payload containers to fetch files via web
|
||||
@@ -217,7 +218,7 @@ func setRoutes(r *gin.Engine) {
|
||||
allOperationMembers.Use(authentication.RBACMiddlewareAll())
|
||||
{
|
||||
// generic all installed services
|
||||
allOperationMembers.POST("eventing_import_automatic_webhook", webcontroller.EventingImportAutomaticWebhook)
|
||||
|
||||
// payloadtype / c2profile
|
||||
allOperationMembers.POST("c2profile_status_webhook", webcontroller.C2ProfileStatusWebhook)
|
||||
// payload
|
||||
@@ -297,6 +298,7 @@ func setRoutes(r *gin.Engine) {
|
||||
noSpectators.POST("send_external_webhook", webcontroller.SendExternalWebhookWebhook)
|
||||
noSpectators.POST("create_operation_event_log", webcontroller.CreateOperationEventLog)
|
||||
// eventing webhooks
|
||||
noSpectators.POST("eventing_import_automatic_webhook", webcontroller.EventingImportAutomaticWebhook)
|
||||
noSpectators.POST("eventing_import_webhook", webcontroller.EventingImportWebhook)
|
||||
noSpectators.POST("eventing_trigger_manual_webhook", webcontroller.EventingTriggerManualWebhook)
|
||||
noSpectators.POST("eventing_trigger_keyword_webhook", webcontroller.EventingTriggerKeywordWebhook)
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/new/static/css/main.262ce320.css",
|
||||
"main.js": "/new/static/js/main.64e362e7.js",
|
||||
"main.js": "/new/static/js/main.6df06080.js",
|
||||
"sql-wasm.wasm": "/new/sql-wasm-7f26ab750fcdafabbe18c6525a66a0bd.wasm",
|
||||
"static/media/mythic-red.png": "/new/static/media/mythic-red.203468a4e5240d239aa0.png",
|
||||
"static/media/graphql.png": "/new/static/media/graphql.8f15978b39b0870a9f0e.png",
|
||||
"static/media/Mythic_Logo.svg": "/new/static/media/Mythic_Logo.6842c911bebe36d6f83fc7ced4a2cd99.svg",
|
||||
"index.html": "/new/index.html",
|
||||
"main.262ce320.css.map": "/new/static/css/main.262ce320.css.map",
|
||||
"main.64e362e7.js.map": "/new/static/js/main.64e362e7.js.map"
|
||||
"main.6df06080.js.map": "/new/static/js/main.6df06080.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.262ce320.css",
|
||||
"static/js/main.64e362e7.js"
|
||||
"static/js/main.6df06080.js"
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/new/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="apple-touch-icon" href="/new/logo192.png"/><link rel="manifest" href="/new/manifest.json"/><title>Mythic</title><script defer="defer" src="/new/static/js/main.64e362e7.js"></script><link href="/new/static/css/main.262ce320.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/new/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="apple-touch-icon" href="/new/logo192.png"/><link rel="manifest" href="/new/manifest.json"/><title>Mythic</title><script defer="defer" src="/new/static/js/main.6df06080.js"></script><link href="/new/static/css/main.262ce320.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
/*! For license information please see main.64e362e7.js.LICENSE.txt */
|
||||
(()=>{var e={6603(e,t,n){e=n.nmd(e),function(){var e=function(){return this}();e||"undefined"==typeof window||(e=window);var t=function(e,n,r){"string"===typeof e?(2==arguments.length&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)):t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace())};t.modules={},t.payloads={};var n=function(e,t,n){if("string"===typeof t){var i=o(e,t);if(void 0!=i)return n&&n(),i}else if("[object Array]"===Object.prototype.toString.call(t)){for(var a=[],s=0,l=t.length;s<l;++s){var c=o(e,t[s]);if(void 0==c&&r.original)return;a.push(c)}return n&&n.apply(null,a)||!0}},r=function(e,t){var i=n("",e,t);return void 0==i&&r.original?r.original.apply(this,arguments):i},i=function(e,t){if(-1!==t.indexOf("!")){var n=t.split("!");return i(e,n[0])+"!"+i(e,n[1])}if("."==t.charAt(0))for(t=e.split("/").slice(0,-1).join("/")+"/"+t;-1!==t.indexOf(".")&&r!=t;){var r=t;t=t.replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}return t},o=function(e,r){r=i(e,r);var o=t.modules[r];if(!o){if("function"===typeof(o=t.payloads[r])){var a={},s={id:r,uri:"",exports:a,packaged:!0};a=o((function(e,t){return n(r,e,t)}),a,s)||s.exports,t.modules[r]=a,delete t.payloads[r]}o=t.modules[r]=a||o}return o};!function(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]),i.define&&i.define.packaged||(t.original=i.define,i.define=t,i.define.packaged=!0),i.require&&i.require.packaged||(r.original=i.require,i.require=r,i.require.packaged=!0)}("ace")}(),ace.define("ace/lib/es6-shim",["require","exports","module"],(function(e,t,n){function r(e,t,n){Object.defineProperty(e,t,{value:n,enumerable:!1,writable:!0,configurable:!0})}String.prototype.startsWith||r(String.prototype,"startsWith",(function(e,t){return t=t||0,this.lastIndexOf(e,t)===t})),String.prototype.endsWith||r(String.prototype,"endsWith",(function(e,t){var n=this;(void 0===t||t>n.length)&&(t=n.length),t-=e.length;var r=n.indexOf(e,t);return-1!==r&&r===t})),String.prototype.repeat||r(String.prototype,"repeat",(function(e){for(var t="",n=this;e>0;)1&e&&(t+=n),(e>>=1)&&(n+=n);return t})),String.prototype.includes||r(String.prototype,"includes",(function(e,t){return-1!=this.indexOf(e,t)})),Object.assign||(Object.assign=function(e){if(void 0===e||null===e)throw new TypeError("Cannot convert undefined or null to object");for(var t=Object(e),n=1;n<arguments.length;n++){var r=arguments[n];void 0!==r&&null!==r&&Object.keys(r).forEach((function(e){t[e]=r[e]}))}return t}),Object.values||(Object.values=function(e){return Object.keys(e).map((function(t){return e[t]}))}),Array.prototype.find||r(Array.prototype,"find",(function(e){for(var t=this.length,n=arguments[1],r=0;r<t;r++){var i=this[r];if(e.call(n,i,r,this))return i}})),Array.prototype.findIndex||r(Array.prototype,"findIndex",(function(e){for(var t=this.length,n=arguments[1],r=0;r<t;r++){var i=this[r];if(e.call(n,i,r,this))return r}})),Array.prototype.includes||r(Array.prototype,"includes",(function(e,t){return-1!=this.indexOf(e,t)})),Array.prototype.fill||r(Array.prototype,"fill",(function(e){for(var t=this,n=t.length>>>0,r=arguments[1]|0,i=r<0?Math.max(n+r,0):Math.min(r,n),o=arguments[2],a=void 0===o?n:o|0,s=a<0?Math.max(n+a,0):Math.min(a,n);i<s;)t[i]=e,i++;return t})),Array.of||r(Array,"of",(function(){return Array.prototype.slice.call(arguments)}))})),ace.define("ace/lib/fixoldbrowsers",["require","exports","module","ace/lib/es6-shim"],(function(e,t,n){"use strict";e("./es6-shim")})),ace.define("ace/lib/deep_copy",["require","exports","module"],(function(e,t,n){t.deepCopy=function e(t){if("object"!==typeof t||!t)return t;var n;if(Array.isArray(t)){n=[];for(var r=0;r<t.length;r++)n[r]=e(t[r]);return n}if("[object Object]"!==Object.prototype.toString.call(t))return t;for(var r in n={},t)n[r]=e(t[r]);return n}})),ace.define("ace/lib/lang",["require","exports","module","ace/lib/deep_copy"],(function(e,t,n){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){for(var n="";t>0;)1&t&&(n+=e),(t>>=1)&&(e+=e);return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){for(var t=[],n=0,r=e.length;n<r;n++)e[n]&&"object"==typeof e[n]?t[n]=this.copyObject(e[n]):t[n]=e[n];return t},t.deepCopy=e("./deep_copy").deepCopy,t.arrayToMap=function(e){for(var t={},n=0;n<e.length;n++)t[e[n]]=1;return t},t.createMap=function(e){var t=Object.create(null);for(var n in e)t[n]=e[n];return t},t.arrayRemove=function(e,t){for(var n=0;n<=e.length;n++)t===e[n]&&e.splice(n,1)},t.escapeRegExp=function(e){return e.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},t.escapeHTML=function(e){return(""+e).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<")},t.getMatchOffsets=function(e,t){var n=[];return eLine truncated
|
||||
//# sourceMappingURL=main.64e362e7.js.map
|
||||
/*! For license information please see main.6df06080.js.LICENSE.txt */
|
||||
(()=>{var e={6603(e,t,n){e=n.nmd(e),function(){var e=function(){return this}();e||"undefined"==typeof window||(e=window);var t=function(e,n,r){"string"===typeof e?(2==arguments.length&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)):t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace())};t.modules={},t.payloads={};var n=function(e,t,n){if("string"===typeof t){var i=o(e,t);if(void 0!=i)return n&&n(),i}else if("[object Array]"===Object.prototype.toString.call(t)){for(var a=[],s=0,l=t.length;s<l;++s){var c=o(e,t[s]);if(void 0==c&&r.original)return;a.push(c)}return n&&n.apply(null,a)||!0}},r=function(e,t){var i=n("",e,t);return void 0==i&&r.original?r.original.apply(this,arguments):i},i=function(e,t){if(-1!==t.indexOf("!")){var n=t.split("!");return i(e,n[0])+"!"+i(e,n[1])}if("."==t.charAt(0))for(t=e.split("/").slice(0,-1).join("/")+"/"+t;-1!==t.indexOf(".")&&r!=t;){var r=t;t=t.replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}return t},o=function(e,r){r=i(e,r);var o=t.modules[r];if(!o){if("function"===typeof(o=t.payloads[r])){var a={},s={id:r,uri:"",exports:a,packaged:!0};a=o((function(e,t){return n(r,e,t)}),a,s)||s.exports,t.modules[r]=a,delete t.payloads[r]}o=t.modules[r]=a||o}return o};!function(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]),i.define&&i.define.packaged||(t.original=i.define,i.define=t,i.define.packaged=!0),i.require&&i.require.packaged||(r.original=i.require,i.require=r,i.require.packaged=!0)}("ace")}(),ace.define("ace/lib/es6-shim",["require","exports","module"],(function(e,t,n){function r(e,t,n){Object.defineProperty(e,t,{value:n,enumerable:!1,writable:!0,configurable:!0})}String.prototype.startsWith||r(String.prototype,"startsWith",(function(e,t){return t=t||0,this.lastIndexOf(e,t)===t})),String.prototype.endsWith||r(String.prototype,"endsWith",(function(e,t){var n=this;(void 0===t||t>n.length)&&(t=n.length),t-=e.length;var r=n.indexOf(e,t);return-1!==r&&r===t})),String.prototype.repeat||r(String.prototype,"repeat",(function(e){for(var t="",n=this;e>0;)1&e&&(t+=n),(e>>=1)&&(n+=n);return t})),String.prototype.includes||r(String.prototype,"includes",(function(e,t){return-1!=this.indexOf(e,t)})),Object.assign||(Object.assign=function(e){if(void 0===e||null===e)throw new TypeError("Cannot convert undefined or null to object");for(var t=Object(e),n=1;n<arguments.length;n++){var r=arguments[n];void 0!==r&&null!==r&&Object.keys(r).forEach((function(e){t[e]=r[e]}))}return t}),Object.values||(Object.values=function(e){return Object.keys(e).map((function(t){return e[t]}))}),Array.prototype.find||r(Array.prototype,"find",(function(e){for(var t=this.length,n=arguments[1],r=0;r<t;r++){var i=this[r];if(e.call(n,i,r,this))return i}})),Array.prototype.findIndex||r(Array.prototype,"findIndex",(function(e){for(var t=this.length,n=arguments[1],r=0;r<t;r++){var i=this[r];if(e.call(n,i,r,this))return r}})),Array.prototype.includes||r(Array.prototype,"includes",(function(e,t){return-1!=this.indexOf(e,t)})),Array.prototype.fill||r(Array.prototype,"fill",(function(e){for(var t=this,n=t.length>>>0,r=arguments[1]|0,i=r<0?Math.max(n+r,0):Math.min(r,n),o=arguments[2],a=void 0===o?n:o|0,s=a<0?Math.max(n+a,0):Math.min(a,n);i<s;)t[i]=e,i++;return t})),Array.of||r(Array,"of",(function(){return Array.prototype.slice.call(arguments)}))})),ace.define("ace/lib/fixoldbrowsers",["require","exports","module","ace/lib/es6-shim"],(function(e,t,n){"use strict";e("./es6-shim")})),ace.define("ace/lib/deep_copy",["require","exports","module"],(function(e,t,n){t.deepCopy=function e(t){if("object"!==typeof t||!t)return t;var n;if(Array.isArray(t)){n=[];for(var r=0;r<t.length;r++)n[r]=e(t[r]);return n}if("[object Object]"!==Object.prototype.toString.call(t))return t;for(var r in n={},t)n[r]=e(t[r]);return n}})),ace.define("ace/lib/lang",["require","exports","module","ace/lib/deep_copy"],(function(e,t,n){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){for(var n="";t>0;)1&t&&(n+=e),(t>>=1)&&(e+=e);return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){for(var t=[],n=0,r=e.length;n<r;n++)e[n]&&"object"==typeof e[n]?t[n]=this.copyObject(e[n]):t[n]=e[n];return t},t.deepCopy=e("./deep_copy").deepCopy,t.arrayToMap=function(e){for(var t={},n=0;n<e.length;n++)t[e[n]]=1;return t},t.createMap=function(e){var t=Object.create(null);for(var n in e)t[n]=e[n];return t},t.arrayRemove=function(e,t){for(var n=0;n<=e.length;n++)t===e[n]&&e.splice(n,1)},t.escapeRegExp=function(e){return e.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},t.escapeHTML=function(e){return(""+e).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<")},t.getMatchOffsets=function(e,t){var n=[];return eLine truncated
|
||||
//# sourceMappingURL=main.6df06080.js.map
|
||||
File renamed without changes.
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"static/js/main.64e362e7.js","mappings":"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 truncated
|
||||
{"version":3,"file":"static/js/main.6df06080.js","mappings":"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 truncated
|
||||
@@ -6,5 +6,5 @@ RUN chmod +x /health_check.sh
|
||||
RUN apk add openssl
|
||||
RUN openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/private/mythic-ssl.key -out /etc/ssl/private/mythic-cert.crt -sha256 -days 3650 -nodes -subj "/O=Mythic/OU=Mythic/CN=Mythic"
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=60s --retries=5 --start-period=5s \
|
||||
HEALTHCHECK --interval=30s --timeout=10s --retries=5 --start-period=5s \
|
||||
CMD /health_check.sh || exit 1
|
||||
@@ -3,5 +3,5 @@ COPY ["pg_hba.conf", "/var/lib/postgresql/pg_hba.conf"]
|
||||
COPY ["configuration.sh", "/docker-entrypoint-initdb.d/configuration.sh"]
|
||||
COPY ["postgres.conf", "/etc/postgresql.conf"]
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=30s --retries=5 --start-period=5s \
|
||||
CMD pg_isready -d mythic_db -U mythic_user || exit 1
|
||||
HEALTHCHECK --interval=30s --timeout=10s --retries=5 --start-period=5s \
|
||||
CMD pg_isready -d ${POSTGRES_DB:-mythic_db} -U ${POSTGRES_USER:-mythic_user} -p ${POSTGRES_PORT:-5432} || exit 1
|
||||
@@ -3,7 +3,7 @@ ADD rabbitmq.conf /etc/rabbitmq/rabbitmq.conf
|
||||
ADD generate_config.sh /generate_config.sh
|
||||
ADD rabbitmq.conf /tmp/base_rabbitmq.conf
|
||||
|
||||
HEALTHCHECK --interval=60s --timeout=30s --retries=5 --start-period=5s \
|
||||
HEALTHCHECK --interval=30s --timeout=10s --retries=5 --start-period=5s \
|
||||
CMD rabbitmq-diagnostics -q check_port_connectivity || exit 1
|
||||
|
||||
CMD /bin/sh -c "chmod +x /generate_config.sh && /generate_config.sh && rabbitmq-server"
|
||||
|
||||
Reference in new issue
Block a user