Skip to content

Commit

Permalink
ui fixes, testProxy feature
Browse files Browse the repository at this point in the history
  • Loading branch information
its-a-feature committed Aug 5, 2024
1 parent c42d91e commit 7ae5995
Show file tree
Hide file tree
Showing 30 changed files with 332 additions and 67 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ 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.3.0-rc14] - 2024-08-05

### Changed

- Updated the login function to return the user's utc time preference
- Added button to show/hide deleted consuming containers

## [3.3.0-rc13] - 2024-08-05

### Changed
Expand Down
9 changes: 9 additions & 0 deletions MythicReactUI/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ 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.2.14] - 2024-08-05

### Changed

- Updated the proxy search page to hide stopped proxies by default
- Updated the proxy search page to have custom button on rpfwd proxies to manually test connectivity (5s timeout)
- Updated table columns and handle drag animations
- Updated task search to default to command + parameters with a separate option for just command

## [0.2.13] - 2024-08-05

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import MoreVertIcon from '@mui/icons-material/MoreVert';

const DraggableHandles = React.forwardRef(({ height, rowHeight, width, minColumnWidth, columnWidths, onStop }, ref) => {
const [isDragging, setIsDragging] = useState(-1);

return (
<div
ref={ref}
Expand All @@ -27,28 +28,21 @@ const DraggableHandles = React.forwardRef(({ height, rowHeight, width, minColumn
top: 0,
bottom: 0,
}}
defaultPosition={{x: 0, y:0}}
position={{ x: 0, y: 0 }}
defaultPosition={{x: 0, y: 0}}
position={{x: 0, y: 0}}
onStart={() => {
setIsDragging(i);
}}
onStop={(e, data) => {
setIsDragging(-1);
onStop(data.x, i);
}}>
<MoreVertIcon
className={classes.draggableHandlesClickArea}
style={{
left: leftOffset + columnWidths[i] - 1 - 8,
height: rowHeight,
}}>
<div
className={classes.draggableHandlesIndicator}
<MoreVertIcon
className={isDragging === i ? classes.draggableHandlesClickAreaSelected : classes.draggableHandlesClickArea}
style={{
display: isDragging === i ? 'block' : 'none',
}}
/>
</MoreVertIcon>
left: leftOffset + columnWidths[i] - 1 - 7,
}}>
</MoreVertIcon>
</Draggable>
);
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export const classes = {
cellInner: `${PREFIX}-cellInner`,
draggableHandlesContainer: `${PREFIX}-draggableHandlesContainer`,
draggableHandlesClickArea: `${PREFIX}-draggableHandlesClickArea`,
draggableHandlesClickAreaSelected: `${PREFIX}-draggableHandlesClickAreaSelected`,
draggableHandlesIndicator: `${PREFIX}-draggableHandlesIndicator`,
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ export const taskingDataFragment = gql`
cmd
supported_ui_features
id
payloadtype {
name
}
}
command_name
opsec_pre_blocked
Expand Down
2 changes: 2 additions & 0 deletions MythicReactUI/src/components/pages/Callbacks/TaskDisplay.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ const ColoredTaskLabel = ({task, theme, me, taskDivID, onClick, displayChildren,
{initialShowIPValue ? `/ ${ipValue} ` : ''}
{initialShowCallbackGroupsValue ? `/ ${task.callback.mythictree_groups.join(', ')} ` : ''}
{" / "}
{task?.command?.payloadtype?.name}
{" / "}
<TaskStatusDisplay task={task} theme={theme}/>
{task.comment !== "" ? (
<div className={classes.column}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {MythicDialog} from "../../MythicComponents/MythicDialog";
import AttachFileIcon from '@mui/icons-material/AttachFile';
import {ConsumingServicesGetIDPMetadataDialog} from "./ConsumingServicesGetIDPMetadataDialog";
import {C2ProfileListFilesDialog} from "../PayloadTypesC2Profiles/C2ProfileListFilesDialog";
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import VisibilityIcon from '@mui/icons-material/Visibility';

const testWebhookMutation = gql`
mutation testWebhookWorks($service_type: String!){
Expand Down Expand Up @@ -52,6 +54,7 @@ const logging_events = ["new_artifact","new_callback", "new_credential","new_fil

export function ConsumingServicesTable({services}) {
const theme = useTheme();
const [showDeleted, setShowDeleted] = React.useState(false);
const [testWebhook] = useMutation(testWebhookMutation, {
onCompleted: data => {
if (data.consumingServicesTestWebhook.status === "success") {
Expand Down Expand Up @@ -165,6 +168,16 @@ export function ConsumingServicesTable({services}) {
<Typography variant="h3" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Containers Consuming Events
</Typography>
{showDeleted ? (
<MythicStyledTooltip title={"Hide Deleted Services"} style={{float: "right"}}>
<IconButton size="small" style={{float: "right", marginTop: "5px"}} variant="contained" onClick={() => setShowDeleted(!showDeleted)}><VisibilityIcon /></IconButton>
</MythicStyledTooltip>

) : (
<MythicStyledTooltip title={"Show Deleted Services"} style={{float: "right"}}>
<IconButton size="small" style={{float: "right", marginTop: "5px"}} variant="contained" onClick={() => setShowDeleted(!showDeleted)} ><VisibilityOffIcon /></IconButton>
</MythicStyledTooltip>
)}
</Paper>
<div style={{display: "flex", flexGrow: 1}}>
<TableContainer className="mythicElement">
Expand All @@ -181,6 +194,7 @@ export function ConsumingServicesTable({services}) {
</TableHead>
<TableBody>
{webhooks.map( (w, index) => (
(showDeleted || !w.deleted) &&
<TableRow key={w.id} hover>
<MythicTableCell>
{w.deleted ? (
Expand Down Expand Up @@ -242,6 +256,7 @@ export function ConsumingServicesTable({services}) {
</TableRow>
))}
{logging.map(w => (
(showDeleted || !w.deleted) &&
<TableRow key={w.id} hover>
<MythicTableCell>
{w.deleted ? (
Expand Down Expand Up @@ -303,6 +318,7 @@ export function ConsumingServicesTable({services}) {
</TableRow>
))}
{eventing.map( (w, index) => (
(showDeleted || !w.deleted) &&
<TableRow key={w.id} hover>
<MythicTableCell>
{w.deleted ? (
Expand Down Expand Up @@ -369,6 +385,7 @@ export function ConsumingServicesTable({services}) {
</TableRow>
))}
{auth.map( (w, index) => (
(showDeleted || !w.deleted) &&
<TableRow key={w.id} hover>
<MythicTableCell>
{w.deleted ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import IconButton from '@mui/material/IconButton';
import BuildIcon from '@mui/icons-material/Build';
import TableRow from '@mui/material/TableRow';
import MythicTableCell from "../../MythicComponents/MythicTableCell";
import SendTimeExtensionTwoToneIcon from '@mui/icons-material/SendTimeExtensionTwoTone';
import {gql, useMutation} from '@apollo/client';
import { snackActions } from '../../utilities/Snackbar';
import {MythicStyledTooltip} from "../../MythicComponents/MythicStyledTooltip";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function CallbackSearchTable(props){
<TableCell >Host</TableCell>
<TableCell >Last Checkin</TableCell>
<TableCell >Description</TableCell>
<TableCell style={{width: "16rem"}}>IP</TableCell>
<TableCell >IP</TableCell>
<TableCell style={{width: "5rem"}}>ID</TableCell>
<TableCell style={{width: "60px"}}>Agent</TableCell>
<TableCell style={{width: "3rem"}}></TableCell>
Expand Down Expand Up @@ -117,8 +117,8 @@ function CallbackSearchTableRow(props){
<Typography variant="body2" style={{wordBreak: "break-all", display: "inline-block"}}>{props.description}</Typography>
</MythicStyledTableCell>
<MythicStyledTableCell style={{whiteSpace: "pre"}}>
{ips.slice(0,5).join("\n")}
{ips.length > 5 ? "\n..." : null}
{ips.slice(0,1).join("\n")}
{ips.length > 1 ? "\n..." : null}
</MythicStyledTableCell>
<MythicStyledTableCell>
<Link style={{wordBreak: "break-all"}} color="textPrimary" underline="always" target="_blank"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import RestoreFromTrashIcon from '@mui/icons-material/RestoreFromTrash';
import {getStringSize} from '../Callbacks/ResponseDisplayTable';
import {MythicStyledTooltip} from "../../MythicComponents/MythicStyledTooltip";
import {adjustOutput} from "../Eventing/EventGroupInstancesTable";
import SpeedIcon from '@mui/icons-material/Speed';

const toggleProxy = gql`
mutation ToggleProxyMutation($callbackport_id: Int!, $action: String!){
Expand All @@ -26,8 +27,16 @@ mutation ToggleProxyMutation($callbackport_id: Int!, $action: String!){
}
}
`;
const testProxyMutation = gql`
mutation TestProxyMutation($callbackport_id: Int!){
testProxy(callbackport_id: $callbackport_id){
status
error
}
}
`;

export function SocksSearchTable(props){
export function ProxySearchTable(props){
const [callbacks, setCallbacks] = React.useState([]);
useEffect( () => {
setCallbacks([...props.callbacks]);
Expand All @@ -50,9 +59,7 @@ export function SocksSearchTable(props){
<TableHead>
<TableRow>
<TableCell style={{width: "1rem"}}></TableCell>
<TableCell >User</TableCell>
<TableCell >Host</TableCell>
<TableCell >Description</TableCell>
<TableCell >User@Host</TableCell>
<TableCell style={{width: "9rem"}}>Callback / Task</TableCell>
<TableCell style={{width: "6rem"}}>Local Port</TableCell>
<TableCell >Remote Connection</TableCell>
Expand All @@ -63,12 +70,14 @@ export function SocksSearchTable(props){
</TableCell>
<TableCell style={{width: "7rem"}}>Proxy Type</TableCell>
<TableCell style={{width: "9rem"}}>Last Updated</TableCell>
<TableCell style={{width: "4rem"}}></TableCell>
</TableRow>
</TableHead>
<TableBody>

{callbacks.map( (op) => (
<CallbackSearchTableRow
(props.showDeleted || !op.deleted) &&
<ProxySearchTableRow
key={"cred" + op.id}
onEditDeleted={onEditDeleted}
{...op}
Expand All @@ -80,7 +89,7 @@ export function SocksSearchTable(props){
)
}

function CallbackSearchTableRow(props){
function ProxySearchTableRow(props){
const theme = useTheme();
const [openDeleteDialog, setOpenDeleteDialog] = React.useState(false);
const confirmDialogText = "This does not issue any start/stop command to the agent. This only opens/closes ports that Mythic controls. For rpfwd, this will not open/close that port on the remote host - you need to issue a task to your agent to do that.";
Expand All @@ -102,6 +111,18 @@ function CallbackSearchTableRow(props){
snackActions.error("Operation not allowed");
}
});
const [testProxy] = useMutation(testProxyMutation, {
onCompleted: (data) => {
if (data.testProxy.status === "success"){
snackActions.success("Initiating connection test");
} else {
snackActions.error(data.testProxy.error);
}
},
onError: (data) => {
snackActions.error("Operation not allowed");
}
});
const onAcceptDelete = () => {
let action = "start";
if(props.deleted){
Expand All @@ -111,6 +132,9 @@ function CallbackSearchTableRow(props){
}
updateDeleted({variables: {callbackport_id: props.id, action: action}})
}
const onTestProxy = () => {
testProxy({variables: {callbackport_id: props.id}});
}
return (
<React.Fragment>
<TableRow hover>
Expand All @@ -131,10 +155,9 @@ function CallbackSearchTableRow(props){
</Tooltip>
)} </TableCell>
<TableCell>
<Typography variant="body2" style={{wordBreak: "break-all"}}>{props.callback.user}</Typography>
</TableCell>
<TableCell>{props.callback.host}</TableCell>
<TableCell >
<Typography variant="body2" style={{wordBreak: "break-all"}}>
<b>{props.callback.user}</b>@<i>{props.callback.host}</i>
</Typography>
<Typography variant="body2" style={{wordBreak: "break-all", display: "inline-block"}}>{props.callback.description}</Typography>
</TableCell>
<TableCell>
Expand Down Expand Up @@ -178,6 +201,16 @@ function CallbackSearchTableRow(props){
{props.updated_at + "Z"}
</Moment>
</TableCell>
<TableCell>
{props.remote_port !== 0 &&
<MythicStyledTooltip title={"Test Remote Connection"} >
<IconButton color={"success"}
onClick={onTestProxy}>
<SpeedIcon />
</IconButton>
</MythicStyledTooltip>
}
</TableCell>
</TableRow>
</React.Fragment>
)
Expand Down
2 changes: 1 addition & 1 deletion MythicReactUI/src/components/pages/Search/Search.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {SearchTabKeylogsLabel, SearchTabKeylogsPanel} from './SearchTabKeylog';
import {SearchTabTokensLabel, SearchTabTokensPanel} from './SearchTabTokens';
import {SearchTabCallbacksLabel, SearchTabCallbacksPanel} from './SearchTabCallbacks';
import {SearchTabArtifactsLabel, SearchTabArtifactsPanel} from './SearchTabArtifacts';
import {SearchTabSocksLabel, SearchTabSocksPanel} from './SearchTabSocks';
import {SearchTabSocksLabel, SearchTabSocksPanel} from './SearchTabProxies';
import {SearchTabProcessesLabel, SearchTabProcessPanel} from "./SearchTabProcesses";
import {SearchTabTagsLabel, SearchTabTagsPanel} from "./SearchTabTags";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import {MythicTabPanel, MythicSearchTabLabel} from '../../MythicComponents/Mythi
import React, { useEffect } from 'react';
import { gql, useSubscription} from '@apollo/client';
import { snackActions } from '../../utilities/Snackbar';
import {SocksSearchTable} from './SocksSearchTable';
import {ProxySearchTable} from './ProxySearchTable';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faSocks} from '@fortawesome/free-solid-svg-icons';
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import VisibilityIcon from '@mui/icons-material/Visibility';
import {MythicStyledTooltip} from "../../MythicComponents/MythicStyledTooltip";
import {IconButton} from '@mui/material';

const callbackPortsSub = gql`
subscription portsSub{
Expand Down Expand Up @@ -48,6 +52,7 @@ export function SearchTabSocksLabel(props){

export const SearchTabSocksPanel = (props) =>{
const [callbackData, setCallbackData] = React.useState([]);
const [showDeleted, setShowDeleted] = React.useState(false);
useSubscription(callbackPortsSub, {
fetchPolicy: "no-cache",
onData: ({data}) => {
Expand Down Expand Up @@ -75,9 +80,21 @@ export const SearchTabSocksPanel = (props) =>{

return (
<MythicTabPanel {...props} >
<div>
{showDeleted ? (
<MythicStyledTooltip title={"Hide Stopped Proxies"} style={{float: "right"}}>
<IconButton size="small" style={{float: "right", marginTop: "5px"}} variant="contained" onClick={() => setShowDeleted(!showDeleted)}><VisibilityIcon /></IconButton>
</MythicStyledTooltip>

) : (
<MythicStyledTooltip title={"Show Stopped Proxies"} style={{float: "right"}}>
<IconButton size="small" style={{float: "right", marginTop: "5px"}} variant="contained" onClick={() => setShowDeleted(!showDeleted)} ><VisibilityOffIcon /></IconButton>
</MythicStyledTooltip>
)}
</div>
<div style={{overflowY: "auto", height: "100%", display: "flex", flexDirection: "column"}}>
{callbackData.length > 0 ? (
<SocksSearchTable callbacks={callbackData} />) : (
<ProxySearchTable callbacks={callbackData} showDeleted={showDeleted} />) : (
<div style={{display: "flex", justifyContent: "center", alignItems: "center", position: "absolute", left: "50%", top: "50%"}}>No Search Results</div>
)}
</div>
Expand Down
Loading

0 comments on commit 7ae5995

Please sign in to comment.