Skip to content

Commit

Permalink
v0.0.3.32
Browse files Browse the repository at this point in the history
UI Updates
  • Loading branch information
its-a-feature committed Feb 29, 2024
1 parent 105fd5c commit bfcf5c2
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 37 deletions.
10 changes: 10 additions & 0 deletions MythicReactUI/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ 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.1.75] - 2024-02-29

### Changed

- Added more files to the `media` browserscript and files search page that are represented as "text"
- Updated table's dictionary display buttons to break all words and enable word-wrap
- Updated graphs with more than 50 nodes to be hidden by default and shown after consent
- Updated graph de-selection to be a right-click context menu so that you can't accidentally unselect
- Updated graph selection to allow hiding everything except what's currently selected

## [0.1.74] - 2024-02-28

### Changed
Expand Down
16 changes: 8 additions & 8 deletions MythicReactUI/src/components/MythicComponents/MythicDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,30 +145,30 @@ export function MythicViewJSONAsTableDialog(props) {
}, [props.value, props.leftColumn, props.rightColumn]);
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">{props.title}</DialogTitle>
<DialogTitle id="form-dialog-title" style={{wordBreak: "break-all", maxWidth: "100%"}}>{props.title}</DialogTitle>
<Paper elevation={5} style={{position: "relative"}} variant={"elevation"}>
<TableContainer className="mythicElement">
<Table size="small" style={{"tableLayout": "fixed", "maxWidth": "calc(100vw)", "overflow": "scroll"}}>
<TableHead>
<TableRow>
{headers.map( (header, index) => (
<TableCell key={'header' + index} style={index === 0 ? {width: "30%"} : {}}>{header}</TableCell>
<TableCell key={'header' + index} style={index === 0 ? {width: "30%", wordBreak: "break-all"} : {wordBreak: "break-all"}}>{header}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{tableType === "dictionary" ? (
comment.map( (element, index) => (
<TableRow key={'row' + index} hover>
<TableCell>{element.name}</TableCell>
<TableCell style={{wordBreak: "break-all"}}>{element.name}</TableCell>
{element.new_table ?
(
<TableContainer className="mythicElement">
<Table size="small" style={{"tableLayout": "fixed", "maxWidth": "calc(100vw)", "overflow": "scroll"}}>
<TableHead>
<TableRow>
{element.headers.map( (header, index) => (
<TableCell key={'eheader' + header + index} style={index === 0 ? {width: "30%"} : {}}>{header}</TableCell>
<TableCell key={'eheader' + header + index} style={index === 0 ? {width: "30%", wordBreak: "break-all"} : {wordBreak: "break-all"}}>{header}</TableCell>
))}
</TableRow>
</TableHead>
Expand All @@ -184,7 +184,7 @@ export function MythicViewJSONAsTableDialog(props) {
element.value.map( (e, elementIndex) => (
<TableRow>
{element.headers.map( (header, headerIndex) => (
<TableCell key={'element' + elementIndex + "header" + headerIndex} style={headerIndex === 0 ? {width: "30%"} : {}}>{convertValueToContextValue(header, e[header], props.me)}</TableCell>
<TableCell key={'element' + elementIndex + "header" + headerIndex} style={headerIndex === 0 ? {width: "30%", wordBreak: "break-all"} : {wordBreak: "break-all"}}>{convertValueToContextValue(header, e[header], props.me)}</TableCell>
))}
</TableRow>
))
Expand All @@ -203,7 +203,7 @@ export function MythicViewJSONAsTableDialog(props) {
comment.map( (row, index) => (
<TableRow key={'row' + index} hover>
{Object.keys(row).map( (key) => (
<TableCell key={"row" + index + "cell" + key}>{convertValueToContextValue(key, row[key], props.me)}</TableCell>
<TableCell key={"row" + index + "cell" + key} style={{wordBreak: "break-all"}}>{convertValueToContextValue(key, row[key], props.me)}</TableCell>
))}
</TableRow>
))
Expand Down Expand Up @@ -252,8 +252,8 @@ export function MythicViewObjectPropertiesAsTableDialog(props) {
<TableBody>
{comment.map( (element, index) => (
<TableRow key={'row' + index}>
<TableCell>{element.name}</TableCell>
<TableCell>{convertValueToContextValue(element.name, element.value)}</TableCell>
<TableCell style={{wordBreak: "break-all"}}>{element.name}</TableCell>
<TableCell style={{wordBreak: "break-all"}}>{convertValueToContextValue(element.name, element.value)}</TableCell>
</TableRow>
))}
</TableBody>
Expand Down
5 changes: 3 additions & 2 deletions MythicReactUI/src/components/pages/Callbacks/C2PathDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -1935,7 +1935,7 @@ const DrawBrowserScriptElementsFlow = ({edges, panel, view_config, theme, contex
oldLabelBgStyle: graphEdge[0].oldLabelBgStyle ? graphEdge[0].oldLabelBgStyle : graphEdge[0].labelBgStyle,
labelBgStyle: {
fill: theme.tableHover,
fillOpacity: 0.6,
fillOpacity: 1.0,
},
oldLabelStyle: graphEdge[0].oldLabelStyle ? graphEdge[0].oldLabelStyle : graphEdge[0].labelStyle,
labelStyle: {
Expand Down Expand Up @@ -2004,7 +2004,8 @@ const DrawBrowserScriptElementsFlow = ({edges, panel, view_config, theme, contex
oldLabelBgStyle: graphEdge[0].oldLabelBgStyle ? graphEdge[0].oldLabelBgStyle : graphEdge[0].labelBgStyle,
labelBgStyle: {
fill: theme.tableHover,
fillOpacity: 0.6,
fillOpacity: 1.0,
filter: `drop-shadow (#${theme.palette.info.main} 0px 0px 10px)`
},
oldLabelStyle: graphEdge[0].oldLabelStyle ? graphEdge[0].oldLabelStyle : graphEdge[0].labelStyle,
labelStyle: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import HelpTwoToneIcon from '@mui/icons-material/HelpTwoTone';
import DiamondIcon from '@mui/icons-material/Diamond';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {faSkullCrossbones} from '@fortawesome/free-solid-svg-icons';
import {Typography} from '@mui/material';
import { Button } from '@mui/material';

const getIcons = (img, nodeStyle) => {
if(img === undefined){return null}
Expand Down Expand Up @@ -49,6 +51,7 @@ export const ResponseDisplayGraph = ({graph, task, expand}) =>{
const theme = useTheme();
const [viewAllDataDialog, setViewAllDataDialogOpen] = React.useState(false);
const dictionaryData = React.useRef(null);
const [showGraph, setShowGraph] = React.useState(graph.nodes.length < 50);
const scrollContent = (node, isAppearing) => {
// only auto-scroll if you issued the task
document.getElementById(`scrolltotaskbottom${task.id}`)?.scrollIntoView({
Expand All @@ -72,6 +75,31 @@ export const ResponseDisplayGraph = ({graph, task, expand}) =>{
}
},
]}, []);
if(!showGraph){
return (
<>
<div style={{display: "flex", width: "100%", height: "100%", justifyContent: "center", flexDirection: "column", alignItems: "center"}}>
<Typography variant={"h4"} >
{`Graph Hidden by Default Due to Size: Nodes (${graph.nodes.length}), Edges (${graph.edges.length}) `}
</Typography>
<Button variant={"contained"} color={"error"} onClick={() => {setShowGraph(!showGraph)}}>
{"Show Graph"}
</Button>
</div>
</>
)
}
if(graph.nodes.length === 0){
return (
<>
<div style={{display: "flex", width: "100%", height: "100%", justifyContent: "center", flexDirection: "column", alignItems: "center"}}>
<Typography variant={"h4"} >
{`Empty Graph`}
</Typography>
</div>
</>
)
}
return (
<div style={{height: expand ? "100%" : "400px", width: "100%", position: "relative"}}>
{viewAllDataDialog &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ const ResponseDisplayTableStringCell = ({cellData, rowData}) => {
{cellData?.plaintextHoverText? (
<MythicStyledTooltip title={cellData.plaintextHoverText}>
<pre style={{display: "inline-block"}}>
{cellData?.plaintext}
{cellData?.plaintext?.replaceAll("\n", "")}
</pre>

</MythicStyledTooltip>
) : (
<pre style={{display: "inline-block"}}>
{cellData?.plaintext}
{cellData?.plaintext?.replaceAll("\n", "")}
</pre>
)}
{cellData?.endIcon?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,27 @@ export const ResponseDisplayMedia = ({media, expand, task}) =>{
return <DisplayMedia agent_file_id={media?.agent_file_id || ""} task={task} filename={media?.filename || undefined} expand={expand} />
}
const textExtensionTypes = ["txt", "ps1", "php", "json", "yml", "yaml", "config", "cfg", "go",
"html", "xml", "js", "java", "conf", "cs", "rb", "toml"];
"html", "xml", "js", "java", "conf", "cs", "rb", "toml", "sh", "md", "ini", "py", "kirbi", "bash_profile", "rc",
"local", "gitconfig", "gitignore", "zsh_history", "bash_history", "ps", "psql_history", "lesshst", "gcloudignore",
"pem", "boto", "zsh_profile", "pub", "python_history", "sqlite_history", "viminfo", "zprofile", "zshrc",
"history", "historynew"
];
const knownTextFiles = ["config", "credentials", "known_hosts", "config_default", "id_rsa"];
const imgExtensionTypes = ["png", "jpg", "gif", "jpeg", "pdf"];
const mimeType = (path) => {
if(!path){return undefined}
let extension = path.split(".");
if(extension.length > 0){
extension = extension[extension.length - 1];
if(["png", "jpg", "gif", "jpeg", "pdf"].includes(extension)){
if(imgExtensionTypes.includes(extension.toLowerCase())){
return "object";
}
if(textExtensionTypes.includes(extension)){
if(textExtensionTypes.includes(extension.toLowerCase())){
return "text";
}
return undefined;
} else if(knownTextFiles.includes(path.toLowerCase())){
return "text"
}
return undefined;
}
Expand All @@ -56,7 +64,7 @@ export const DisplayMedia = ({agent_file_id, filename, expand, task}) => {
})
React.useEffect( () => {
let display_type = mimeType(filename);
if(display_type){
if(display_type !== undefined){
setFileData({
display: true,
display_type: display_type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import MythicTextField from '../../MythicComponents/MythicTextField';
import {useQuery, gql, useMutation} from '@apollo/client';
import LinearProgress from '@mui/material/LinearProgress';
import { snackActions } from '../../utilities/Snackbar';
import {MythicConfirmDialog} from "../../MythicComponents/MythicConfirmDialog";
import Typography from '@mui/material/Typography';

const updateDescriptionMutation = gql`
mutation updateDescription ($payload_id: Int!, $description: String) {
Expand All @@ -16,40 +17,89 @@ mutation updateDescription ($payload_id: Int!, $description: String) {
}
}
`;
const updateCallbackDescriptionsMutation = gql`
mutation updateCallbackDescriptions($oldDescription: String!, $newDescription: String!, $payloadID: Int!){
update_callback_many(updates: {where: {description: {_eq: $oldDescription}, registered_payload_id: {_eq: $payloadID}}, _set: {description: $newDescription}}) {
affected_rows
}
}
`;
const getDescriptionQuery = gql`
query getDescriptionQuery ($payload_id: Int!) {
payload_by_pk(id: $payload_id) {
description
id
callbacks {
id
}
}
}
`;

export function PayloadDescriptionDialog(props) {
const [description, setDescription] = useState("");
const { loading, error } = useQuery(getDescriptionQuery, {
const oldDescription = React.useRef();
const hasCallbacks = React.useRef(false);
useQuery(getDescriptionQuery, {
variables: {payload_id: props.payload_id},
onCompleted: data => {
setDescription(data.payload_by_pk.description)
oldDescription.current = data.payload_by_pk.description;
hasCallbacks.current = data.payload_by_pk.callbacks.length > 0;
},
fetchPolicy: "network-only"
});
const [updateDescription] = useMutation(updateDescriptionMutation, {
onCompleted: (data) => {
snackActions.success("Updated Description")
snackActions.success("Updated Payload Description")
}
});
if (loading) {
return <LinearProgress style={{marginTop: "10px"}} />;
}
if (error) {
console.error(error);
return <div>Error!</div>;
const [updateCallbackDescriptions] = useMutation(updateCallbackDescriptionsMutation, {
onCompleted: (data) => {
if(data.update_callback_many.length > 0){
if(data.update_callback_many[0].affected_rows){
snackActions.success("Updated " + data.update_callback_many[0].affected_rows + " callbacks");
}
}

},
onError: (data) => {
console.log(data);
}
});
const [openUpdateAll, setOpenUpdateAll] = React.useState(false);
const updateAllDialogText =<>
<Typography>
Would you like to update the callback description for all callbacks using the old description of this payload? <br/><br/>
If you explicitly set a new description for a callback, that will not be changed.
If you cancel, then any interactive tab for a callback using the old description will no longer show "Callback: X", but will show the old description.
<br/><br/>
Updating all will update all callback's descriptions that matched this payload's old description to the new one. This will preserve the "Callback: X" display for interactive tabs.
</Typography>
</>;
const onAcceptUpdateAll = () => {
setOpenUpdateAll(false);
updateCallbackDescriptions({variables: {
payloadID: props.payload_id,
oldDescription: oldDescription.current,
newDescription: description,
}});
updatePayloadDescription();
// now update all
}
const onCommitSubmit = () => {
const updatePayloadDescription = () => {
setOpenUpdateAll(false);
updateDescription({variables: {payload_id: props.payload_id, description: description}});
props.onClose();
}
const onCommitSubmit = () => {
if(hasCallbacks.current){
setOpenUpdateAll(true);
} else {
updatePayloadDescription();
}

}
const onChange = (name, value, error) => {
setDescription(value);
}
Expand All @@ -68,6 +118,17 @@ export function PayloadDescriptionDialog(props) {
Submit
</Button>
</DialogActions>
{openUpdateAll &&
<MythicConfirmDialog title={"Update Associated Callback's Descriptions?"}
dontCloseOnSubmit={true}
dialogText={updateAllDialogText}
cancelText={"Update Only Payload"}
acceptText={"Update Callbacks"}
acceptColor={"success"}
onClose={updatePayloadDescription}
onSubmit={onAcceptUpdateAll}
open={openUpdateAll}/>
}
</React.Fragment>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import VisibilityIcon from '@mui/icons-material/Visibility';
import MessageIcon from '@mui/icons-material/Message';
import ErrorIcon from '@mui/icons-material/Error';
import CachedIcon from '@mui/icons-material/Cached';
import SettingsIcon from '@mui/icons-material/Settings';
import VerifiedIcon from '@mui/icons-material/Verified';
import AddIcCallIcon from '@mui/icons-material/AddIcCall';
import PhoneMissedIcon from '@mui/icons-material/PhoneMissed';
Expand Down Expand Up @@ -80,7 +79,6 @@ export function PayloadsTableRow(props){
const [openGenerateIOCDialog, setOpenGenerateIOCDialog] = React.useState(false);
const [openGenerateSampleMessageDialog, setOpenGenerateSampleMessageDialog] = React.useState(false);
const dropdownAnchorRef = useRef(null);
const theme = useTheme();
const [triggerRebuild] = useMutation(rebuildPayloadMutation, {
onCompleted: (data) => {
if(data.rebuild_payload.status === "success"){
Expand Down
2 changes: 1 addition & 1 deletion MythicReactUI/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import jwt_decode from 'jwt-decode';
import {meState} from './cache';

export const mythicVersion = "3.2.18-rc9";
export const mythicUIVersion = "0.1.74";
export const mythicUIVersion = "0.1.75";

let fetchingNewToken = false;

Expand Down
6 changes: 3 additions & 3 deletions mythic-react-docker/mythic/public/asset-manifest.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"files": {
"main.css": "/new/static/css/main.00d51b79.css",
"main.js": "/new/static/js/main.efbdc659.js",
"main.js": "/new/static/js/main.1124800f.js",
"static/media/mythic@2x.png": "/new/static/media/mythic@2x.7c5b62b471ac779fd706.png",
"static/media/mythic_red_small.svg": "/new/static/media/mythic_red_small.793b41cc7135cdede246661ec232976b.svg",
"index.html": "/new/index.html",
"main.00d51b79.css.map": "/new/static/css/main.00d51b79.css.map",
"main.efbdc659.js.map": "/new/static/js/main.efbdc659.js.map"
"main.1124800f.js.map": "/new/static/js/main.1124800f.js.map"
},
"entrypoints": [
"static/css/main.00d51b79.css",
"static/js/main.efbdc659.js"
"static/js/main.1124800f.js"
]
}
2 changes: 1 addition & 1 deletion mythic-react-docker/mythic/public/index.html
Original file line number Diff line number Diff line change
@@ -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.efbdc659.js"></script><link href="/new/static/css/main.00d51b79.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.1124800f.js"></script><link href="/new/static/css/main.00d51b79.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

Large diffs are not rendered by default.

Large diffs are not rendered by default.

0 comments on commit bfcf5c2

Please sign in to comment.