Skip to content

Commit

Permalink
mythic-cli updates, MythicRPC updates, and mythic ui tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
its-a-feature committed Sep 4, 2024
1 parent 937fcbd commit c8a1437
Show file tree
Hide file tree
Showing 51 changed files with 816 additions and 439 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ 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.1-rc6] - 2024-09-04

### Changed

- Added new tag for FileHosted to indicate and track that a file was hosted through a C2 Profile
- Updated the file download process to check if there's an alert notification requested as part of FileHosting and sends the alert
- Updated MythicRPCCallbackEncrypt and MythicRPCCallbackDecrypt to support Payload/Staging UUIDs and C2Profile information

## [3.3.1-rc5] - 2024-09-02

### Changed
Expand Down
8 changes: 8 additions & 0 deletions MythicReactUI/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ 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.40] - 2024-09-04

### Changed

- Added a button to edit tags while looking at them
- Edited the HostFiles dialog to have an option for marking a file to be alerted or not on download
- Added a size field to the payloads detailed dialog table

## [0.2.39] - 2024-08-30

### Changed
Expand Down
38 changes: 33 additions & 5 deletions MythicReactUI/src/components/MythicComponents/MythicTag.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ query getSingleTag($tag_id: Int!){
url
id
data
apitokens_id
credential_id
filemeta_id
keylog_id
mythictree_id
operation_id
response_id
task_id
taskartifact_id
tagtype {
name
description
Expand Down Expand Up @@ -180,7 +189,7 @@ const StringTagDataEntry = ({name, value}) => {
return (
<Link href={capturePieces[2]} color="textPrimary" target={"_blank"} >{capturePieces[1]}</Link>
)
} else if(value.startsWith("http")){
} else if(value.startsWith("http:") || value.startsWith("https:")){
return (
<>
{"Click for: "}
Expand All @@ -193,9 +202,27 @@ const StringTagDataEntry = ({name, value}) => {
function ViewTagDialog(props) {
const theme = useTheme();
const [selectedTag, setSelectedTag] = React.useState({});
const [objectInfo, setObjectInfo] = React.useState({object_type: "", object_id: ""});
const {} = useQuery(getSingleTag, {
variables: {tag_id: props.target_object_id},
onCompleted: data => {
if(data.tag_by_pk.apitokens_id !== null){
setObjectInfo({object_type: "apitokens_id", object_id: data.tag_by_pk.apitokens_id});
}else if(data.tag_by_pk.credential_id !== null){
setObjectInfo({object_type: "credential_id", object_id: data.tag_by_pk.credential_id});
}else if(data.tag_by_pk.filemeta_id !== null){
setObjectInfo({object_type: "filemeta_id", object_id: data.tag_by_pk.filemeta_id});
}else if(data.tag_by_pk.keylog_id !== null){
setObjectInfo({object_type: "keylog_id", object_id: data.tag_by_pk.keylog_id});
}else if(data.tag_by_pk.mythictree_id !== null){
setObjectInfo({object_type: "mythictree_id", object_id: data.tag_by_pk.mythictree_id});
}else if(data.tag_by_pk.response_id !== null){
setObjectInfo({object_type: "response_id", object_id: data.tag_by_pk.response_id});
}else if(data.tag_by_pk.task_id !== null){
setObjectInfo({object_type: "task_id", object_id: data.tag_by_pk.task_id});
}else if(data.tag_by_pk.taskartifact_id !== null){
setObjectInfo({object_type: "taskartifact_id", object_id: data.tag_by_pk.taskartifact_id});
}
let newTag = {...data.tag_by_pk};
let tagData = newTag;
try{
Expand Down Expand Up @@ -236,6 +263,7 @@ return (
<TableCell style={{width: "20%"}}>Tag Type</TableCell>
<TableCell style={{display: "inline-flex", flexDirection: "row", width: "100%"}}>
<Chip label={selectedTag?.tagtype?.name||""} size="small" style={{float: "right", backgroundColor:selectedTag?.tagtype?.color||""}} />
<ViewEditTags target_object={objectInfo.object_type} target_object_id={objectInfo.object_id} me={props.me} />
</TableCell>
</TableRow>
<TableRow hover>
Expand Down Expand Up @@ -269,7 +297,7 @@ return (
<StringTagDataEntry name={key} value={selectedTag.data[key]} />
</TableCell>
) : typeof selectedTag.data[key] === "object" ? (
<TableCell>{selectedTag.data[key].toString()}</TableCell>
<TableCell style={{whiteSpace: "pre-wrap"}}>{JSON.stringify(selectedTag.data[key], null, 2)}</TableCell>
) : typeof selectedTag.data[key] === "boolean" ? (
<TableCell>{selectedTag.data[key] ? "True" : "False"}</TableCell>
) :
Expand Down Expand Up @@ -655,11 +683,11 @@ export const ViewEditTags = ({target_object, target_object_id, me}) => {
return(
<React.Fragment>
<IconButton onClick={(e) => toggleTagDialog(e, true)} size="small" style={{display: "inline-block", float: "right"}}><LocalOfferOutlinedIcon /></IconButton>
{openTagDialog ?
(<MythicDialog fullWidth={true} maxWidth="xl" open={openTagDialog}
{openTagDialog &&
<MythicDialog fullWidth={true} maxWidth="xl" open={openTagDialog}
onClose={(e)=>{toggleTagDialog(e, false)}}
innerDialog={<ViewEditTagsDialog me={me} target_object={target_object} target_object_id={target_object_id} onClose={(e)=>{toggleTagDialog(e, false)}} />}
/>) : null}
/>}
</React.Fragment>
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {HostFileDialog} from "./HostFileDialog";
import {MythicStyledTooltip} from "../../MythicComponents/MythicStyledTooltip";
import {MythicFileContext} from "../../MythicComponents/MythicFileContext";
import MenuBookIcon from '@mui/icons-material/MenuBook';
import {TableRowSizeCell} from "../Callbacks/CallbacksTabsFileBrowserTable";

const GET_Payload_Details = gql`
query GetPayloadDetails($payload_id: Int!) {
Expand Down Expand Up @@ -66,6 +67,7 @@ query GetPayloadDetails($payload_id: Int!) {
id
md5
sha1
size
}
payload_build_steps(order_by: {step_number: asc}) {
step_name
Expand Down Expand Up @@ -384,6 +386,12 @@ function DetailedPayloadInnerTable(props){
<TableCell>MD5</TableCell>
<TableCell>{data.payload[0].filemetum.md5}</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Size</TableCell>
<TableCell>
<TableRowSizeCell cellData={data.payload[0].filemetum.size} />
</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Created By</TableCell>
<TableCell>{data.payload[0]?.operator?.username}</TableCell>
Expand Down
17 changes: 14 additions & 3 deletions MythicReactUI/src/components/pages/Payloads/HostFileDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import Input from '@mui/material/Input';
import Switch from '@mui/material/Switch';

const hostFileMutation = gql`
mutation hostFileMutation($c2_id: Int!, $file_uuid: String!, $host_url: String!) {
c2HostFile(c2_id: $c2_id, file_uuid: $file_uuid, host_url: $host_url) {
mutation hostFileMutation($c2_id: Int!, $file_uuid: String!, $host_url: String!, $alert_on_download: Boolean) {
c2HostFile(c2_id: $c2_id, file_uuid: $file_uuid, host_url: $host_url, alert_on_download: $alert_on_download) {
status
error
}
Expand All @@ -36,6 +37,7 @@ export function HostFileDialog(props) {
const [message, setMessage] = useState("");
const [availableC2Profiles, setAvailableC2Profiles] = React.useState([]);
const [selectedC2Profile, setSelectedC2Profile] = React.useState({id: 0});
const [alertOnDownload, setAlertOnDownload] = React.useState(false);
const [hostFile] = useMutation(hostFileMutation, {
onCompleted: (data) => {
if(data.c2HostFile.status === "success"){
Expand Down Expand Up @@ -65,6 +67,9 @@ export function HostFileDialog(props) {
const handleChange = (event) => {
setSelectedC2Profile(event.target.value);
};
const onChangeAlert = (event) => {
setAlertOnDownload(event.target.checked);
}
const submit = () => {
if(message.length === 0){
snackActions.warning("Must supply a hosting path");
Expand All @@ -73,7 +78,7 @@ export function HostFileDialog(props) {
} else if(selectedC2Profile.id === 0){
snackActions.warning("Must select a running, egress C2 Profile to host");
} else {
hostFile({variables: {c2_id: selectedC2Profile.id, file_uuid: props.file_uuid, host_url: message}});
hostFile({variables: {c2_id: selectedC2Profile.id, file_uuid: props.file_uuid, host_url: message, alert_on_download: alertOnDownload}});
}
}

Expand Down Expand Up @@ -112,6 +117,12 @@ export function HostFileDialog(props) {
</MythicTextField>
</MythicTableCell>
</TableRow>
<TableRow hover>
<MythicTableCell>Send alert when file is downloaded?</MythicTableCell>
<MythicTableCell>
<Switch color={"warning"} onChange={onChangeAlert} checked={alertOnDownload}></Switch>
</MythicTableCell>
</TableRow>
</TableBody>
</Table>
</DialogContent>
Expand Down
2 changes: 1 addition & 1 deletion MythicReactUI/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {snackActions} from './components/utilities/Snackbar';
import jwt_decode from 'jwt-decode';
import {meState} from './cache';

export const mythicUIVersion = "0.2.39";
export const mythicUIVersion = "0.2.40";

let fetchingNewToken = false;

Expand Down
13 changes: 13 additions & 0 deletions Mythic_CLI/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## 0.3.5 - 2024-09-03

### Changed

- Dropped support for `docker-compose` script as it causes too many breaking issues in Kali
- Make sure the `compose` plugin is installed with Docker (should be default in modern installs)
- Added support for `--keep-volume` flag with start, build, and install commands
- This allows you to manually override on a per-command basis if you want to keep the volume with an agent/c2 container or not
- By default, if `rebuild_on_start` is true, then volumes will be removed when containers start.
- By default, volumes are removed on explicit `build` commands.
- Added support for tracking an installed service's `install_location`
- `mythic-cli update --all-services` and `./mythic-cli update --services [name] [name]` can check for updated remote_images

## 0.3.4 - 2024-08-27

### Changed
Expand Down
9 changes: 8 additions & 1 deletion Mythic_CLI/src/cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@ var buildCmd = &cobra.Command{

func init() {
rootCmd.AddCommand(buildCmd)
buildCmd.Flags().BoolVarP(
&keepVolume,
"keep-volume",
"",
false,
`Force keep the container's existing volume (if any) when starting the container`,
)
}

func buildContainer(cmd *cobra.Command, args []string) {
if err := internal.ServiceBuild(args); err != nil {
if err := internal.ServiceBuild(args, keepVolume); err != nil {

}
}
6 changes: 3 additions & 3 deletions Mythic_CLI/src/cmd/buildUI.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package cmd

import (
"fmt"
"github.com/MythicMeta/Mythic_CLI/cmd/internal"
"github.com/spf13/cobra"
"log"
)

// configCmd represents the config command
Expand All @@ -21,8 +21,8 @@ func init() {

func buildReactUI(cmd *cobra.Command, args []string) {
if err := internal.DockerBuildReactUI(); err != nil {
fmt.Printf("[-] Failed to build UI\n")
log.Printf("[-] Failed to build UI\n")
} else {
fmt.Printf("[+] Successfully built UI!\n")
log.Printf("[+] Successfully built UI!\n")
}
}
4 changes: 3 additions & 1 deletion Mythic_CLI/src/cmd/config/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,9 @@ Setting this to "true" means that the local Mythic/hasura-docker/Dockerfile is u
This can cause agent and c2 profile containers to have their volumes wiped on each start (and thus deleting any changes).
This also drastically increases the start time for Mythic overall.
This should only be needed if you're doing a bunch of development on Mythic itself.
If you need to rebuild a specific container, you should use './mythic-cli build [container name]' instead to just rebuild that one container`
If you need to rebuild a specific container, you should use './mythic-cli build [container name]' instead to just rebuild that one container.
This will also delete any volumes in use (which will remove things like C2 Profile's config.json updates).
To keep these around when starting or building, use the --keep-volume flag`

// Mythic instance configuration ---------------------------------------------
mythicEnv.SetDefault("mythic_admin_user", "mythic_admin")
Expand Down
2 changes: 1 addition & 1 deletion Mythic_CLI/src/cmd/config/vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ package config

var (
// Version Mythic CLI version
Version = "v0.3.4"
Version = "v0.3.5"
)
24 changes: 18 additions & 6 deletions Mythic_CLI/src/cmd/installFolder.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package cmd

import (
"fmt"
"github.com/MythicMeta/Mythic_CLI/cmd/config"
"github.com/MythicMeta/Mythic_CLI/cmd/internal"
"github.com/spf13/cobra"
"log"
"os"
)

// installCmd represents the config command
var installFolderCmd = &cobra.Command{
Use: "folder path [-f]",
Use: "folder path [-f] [-kv]",
Short: "install services from local folder",
Long: `Run this command to install a properly formatted ExternalAgent folder into Mythic.`,
Run: installFolder,
Expand All @@ -23,15 +24,26 @@ func init() {
"force",
"f",
false,
`Force installing from local folder and don't prompt to overwrite files if an older version is already installed'`,
`Force installing from local folder and don't prompt to overwrite files if an older version is already installed`,
)
installFolderCmd.Flags().BoolVarP(
&keepVolume,
"keep-volume",
"",
false,
`Force keep the container's existing volume (if any) when starting the container`,
)
}

func installFolder(cmd *cobra.Command, args []string) {
if err := internal.InstallFolder(args[0], force); err != nil {
fmt.Printf("[-] Failed to install service: %v\n", err)
localKeepVolume := keepVolume
if !keepVolume {
keepVolume = !config.GetMythicEnv().GetBool("REBUILD_ON_START")
}
if err := internal.InstallFolder(args[0], force, localKeepVolume, ""); err != nil {
log.Printf("[-] Failed to install service: %v\n", err)
os.Exit(1)
} else {
fmt.Printf("[+] Successfully installed service!\n")
log.Printf("[+] Successfully installed service!\n")
}
}
20 changes: 16 additions & 4 deletions Mythic_CLI/src/cmd/installGitHub.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package cmd

import (
"fmt"
"github.com/MythicMeta/Mythic_CLI/cmd/config"
"github.com/MythicMeta/Mythic_CLI/cmd/internal"
"github.com/spf13/cobra"
"log"
"os"
)

Expand Down Expand Up @@ -33,16 +34,27 @@ func init() {
"",
`Install a specific branch from GitHub instead of the main/master branch`,
)
installGitHubCmd.Flags().BoolVarP(
&keepVolume,
"keep-volume",
"",
false,
`Force keep the container's existing volume (if any) when starting the container`,
)
}

func installGitHub(cmd *cobra.Command, args []string) {
if len(args) == 2 {
branch = args[1]
}
if err := internal.InstallService(args[0], branch, force); err != nil {
fmt.Printf("[-] Failed to install service: %v\n", err)
localKeepVolume := keepVolume
if !keepVolume {
keepVolume = !config.GetMythicEnv().GetBool("REBUILD_ON_START")
}
if err := internal.InstallService(args[0], branch, force, localKeepVolume); err != nil {
log.Printf("[-] Failed to install service: %v\n", err)
os.Exit(1)
} else {
fmt.Printf("[+] Successfully installed service!\n")
log.Printf("[+] Successfully installed service!\n")
}
}
Loading

0 comments on commit c8a1437

Please sign in to comment.