Skip to content

Commit

Permalink
fixing UI visual bug and starting to address potential database deadlock
Browse files Browse the repository at this point in the history
  • Loading branch information
its-a-feature committed Sep 23, 2024
1 parent e0618a4 commit 605cbf8
Show file tree
Hide file tree
Showing 14 changed files with 64 additions and 57 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ 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-rc11] - 2024-09-23

### Changed

- Updated callback last checkin time update process to not background jobs, hopefully preventing deadlock in some situations

## [3.3.1-rc9] - 2024-09-17

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

### Changed

- Fixed an issue in light mode with the dropdown for the callbacks graph

## [0.2.44] - 2024-09-14

### Changed
Expand Down
52 changes: 21 additions & 31 deletions MythicReactUI/src/components/pages/Callbacks/CallbacksGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@ import {DrawC2PathElementsFlowWithProvider} from './C2PathDialog';
import {Button} from '@mui/material';
import ButtonGroup from '@mui/material/ButtonGroup';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import Paper from '@mui/material/Paper';
import Grow from '@mui/material/Grow';
import Popper from '@mui/material/Popper';
import MenuItem from '@mui/material/MenuItem';
import MenuList from '@mui/material/MenuList';
import ClickAwayListener from '@mui/material/ClickAwayListener';
import {useMutation } from '@apollo/client';
import {hideCallbackMutation, removeEdgeMutation, addEdgeMutation} from './CallbackMutations';
Expand All @@ -24,6 +20,7 @@ import InputLabel from '@mui/material/InputLabel';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import {CallbackGraphEdgesContext, CallbacksContext, OnOpenTabContext} from './CallbacksTop';
import {Dropdown, DropdownMenuItem} from "../../MythicComponents/MythicNestedMenus";

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 1;
Expand Down Expand Up @@ -90,8 +87,8 @@ const GraphViewOptions = ({viewConfig, setViewConfig}) => {
evt.stopPropagation();
setDropdownOpen((prevOpen) => !prevOpen);
};
const handleMenuItemClick = (event, index) => {
options[index].click();
const handleMenuItemClick = (event, click) => {
click();
setDropdownOpen(false);
};
const options = [
Expand Down Expand Up @@ -182,31 +179,24 @@ const GraphViewOptions = ({viewConfig, setViewConfig}) => {
</FormControl>
}

<Popper open={dropdownOpen} anchorEl={dropdownAnchorRef.current} transition role={undefined} style={{zIndex: 200}}>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper style={{backgroundColor: theme.palette.mode === 'dark' ? theme.palette.primary.dark : theme.palette.primary.light, color: "white"}}>
<ClickAwayListener onClickAway={handleClose}>
<MenuList id="split-button-menu">
{options.map((option, index) => (
<MenuItem
key={option.name}
onClick={(event) => handleMenuItemClick(event, index)}
>
{option.name}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
<ClickAwayListener onClickAway={handleClose}>
<Dropdown
isOpen={dropdownAnchorRef.current}
onOpen={setDropdownOpen}
externallyOpen={dropdownOpen}
menu={
options.map((option, index) => (
<DropdownMenuItem
key={option.name}
disabled={option.disabled}
onClick={(event) => handleMenuItemClick(event, option.click)}
>
{option.name}
</DropdownMenuItem>
))
}
/>
</ClickAwayListener>
</div>
)
}
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.44";
export const mythicUIVersion = "0.2.45";

let fetchingNewToken = false;

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.3.1-rc10
3.3.1-rc11
2 changes: 1 addition & 1 deletion mythic-docker/src/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.3.1-rc10
3.3.1-rc11
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ func MythicRPCCallbackCreate(input MythicRPCCallbackCreateMessage) MythicRPCCall
return response
} else {
logging.LogInfo("Created new callbackgraph edge", "c2", input.C2ProfileName, "callback", callback.ID)
callbackGraph.Add(callback, callback, pc2p.C2profile.Name)
callbackGraph.Add(callback, callback, pc2p.C2profile.Name, false)
}
}
for _, c2paraminstance := range payloadC2ProfileParameterInstances {
Expand Down
6 changes: 3 additions & 3 deletions mythic-docker/src/rabbitmq/util_agent_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ func LookupEncryptionData(c2profile string, messageUUID string, updateCheckinTim
// we found an instance of the cache info with c2 profile encryption data
if cachedUUIDInfoMap[messageUUID+c2profile].UUIDType == "callback" {
if updateCheckinTime {
go UpdateCallbackEdgesAndCheckinTime(cachedUUIDInfoMap[messageUUID+c2profile])
UpdateCallbackEdgesAndCheckinTime(cachedUUIDInfoMap[messageUUID+c2profile])
}

}
Expand All @@ -650,7 +650,7 @@ func LookupEncryptionData(c2profile string, messageUUID string, updateCheckinTim
// we found an instance of the cache info with payload encryption data
if cachedUUIDInfoMap[messageUUID].UUIDType == "callback" {
if updateCheckinTime {
go UpdateCallbackEdgesAndCheckinTime(cachedUUIDInfoMap[messageUUID])
UpdateCallbackEdgesAndCheckinTime(cachedUUIDInfoMap[messageUUID])
}

}
Expand Down Expand Up @@ -1119,7 +1119,7 @@ func UpdateCallbackEdgesAndCheckinTime(uuidInfo *cachedUUIDInfo) {
WHERE id=:id`, callback); err != nil {
logging.LogError(err, "Failed to update last_checkin time", "callback", uuidInfo.UUID)
} else {
callbackGraph.Add(callback, callback, uuidInfo.C2ProfileName)
callbackGraph.Add(callback, callback, uuidInfo.C2ProfileName, false)
//callbackGraph.AddByAgentIds(callback.AgentCallbackID, callback.AgentCallbackID, uuidInfo.C2ProfileName)
if uuidInfo.EdgeId == 0 {
if err := database.DB.Get(&uuidInfo.EdgeId, `SELECT id FROM callbackgraphedge
Expand Down
27 changes: 16 additions & 11 deletions mythic-docker/src/rabbitmq/util_callback_graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func (g *cbGraph) getAllChildIDs(callbackId int) []int {
return callbackIDsToUpdate
}
func updateTimes(updatedTime time.Time, callbackIDs []int) {
//logging.LogInfo("updateTimes", "callbacks", callbackIDs)
query, args, err := sqlx.Named(`UPDATE callback SET last_checkin=:last_checkin, active=:active WHERE id IN (:ids)`,
map[string]interface{}{"last_checkin": updatedTime, "ids": callbackIDs, "active": true})
if err != nil {
Expand All @@ -170,7 +171,7 @@ func updateTimes(updatedTime time.Time, callbackIDs []int) {
query = database.DB.Rebind(query)
_, err = database.DB.Exec(query, args...)
if err != nil {
logging.LogError(err, "Failed to update callback time when push one-to-many c2 disconnected")
logging.LogError(err, "Failed to update callback time when push one-to-many c2 disconnected or P2P connection updated")
return
}
}
Expand All @@ -179,10 +180,10 @@ func listenForPushConnectDisconnectMessages() {
select {
case connectCallbackId := <-pushC2StreamingConnectNotification:
callbackIDs := callbackGraph.getAllChildIDs(connectCallbackId)
go updateTimes(time.UnixMicro(0), callbackIDs)
updateTimes(time.UnixMicro(0), callbackIDs)
case disconnectCallbackId := <-pushC2StreamingDisconnectNotification:
callbackIDs := callbackGraph.getAllChildIDs(disconnectCallbackId)
go updateTimes(time.Now().UTC(), callbackIDs)
updateTimes(time.Now().UTC(), callbackIDs)
}
}
}
Expand All @@ -206,13 +207,13 @@ func (g *cbGraph) Initialize() {
} else {
// make our initial adjacency matrix and tree root data
for _, edge := range edges {
g.Add(edge.Source, edge.Destination, edge.C2Profile.Name)
g.Add(edge.Destination, edge.Source, edge.C2Profile.Name)
g.Add(edge.Source, edge.Destination, edge.C2Profile.Name, true)
g.Add(edge.Destination, edge.Source, edge.C2Profile.Name, true)
}
}
BFSCache.cache = make(map[int]map[int][][]cbGraphAdjMatrixEntry)
}
func (g *cbGraph) Add(source databaseStructs.Callback, destination databaseStructs.Callback, c2profileName string) {
func (g *cbGraph) Add(source databaseStructs.Callback, destination databaseStructs.Callback, c2profileName string, initializing bool) {
g.lock.Lock()
if _, ok := g.adjMatrix[source.ID]; !ok {
// add it
Expand All @@ -229,7 +230,8 @@ func (g *cbGraph) Add(source databaseStructs.Callback, destination databaseStruc
if dest.DestinationId == destination.ID && dest.C2ProfileName == c2profileName {
g.lock.Unlock()
//logging.LogDebug("Found existing p2p connection, not adding new one to memory")
if dest.DestinationId == source.ID {
if initializing {
// don't update callback times when initializing, this is when the Mythic server starts up
return
}
updateTime := time.Now().UTC()
Expand All @@ -238,7 +240,10 @@ func (g *cbGraph) Add(source databaseStructs.Callback, destination databaseStruc
}

callbackIDs := g.getAllChildIDs(source.ID)
go updateTimes(updateTime, callbackIDs)
if len(callbackIDs) > 0 {
updateTimes(updateTime, callbackIDs)
}

return
}
}
Expand Down Expand Up @@ -282,8 +287,8 @@ func (g *cbGraph) AddByAgentIds(source string, destination string, c2profileName
edge.OperationID = sourceCallback.OperationID
edge.C2ProfileID = getC2ProfileIdForName(c2profileName)
// only add / talk to database if the in-memory piece gets updated
g.Add(sourceCallback, destinationCallback, c2profileName)
g.Add(destinationCallback, sourceCallback, c2profileName)
g.Add(sourceCallback, destinationCallback, c2profileName, false)
g.Add(destinationCallback, sourceCallback, c2profileName, false)
// can't have a unique constraint with a NULL value, NULL != NULL
err := database.DB.Get(&edge.ID, `SELECT id FROM callbackgraphedge
WHERE operation_id=$1 AND source_id=$2 AND destination_id=$3 AND
Expand Down Expand Up @@ -471,7 +476,7 @@ func AddEdgeById(sourceId int, destinationId int, c2profileName string) error {
return err
}
logging.LogInfo("added new callbackgraph edge in addEdgeById", "c2", c2profileName, "callback", sourceId)
callbackGraph.Add(sourceCallback, destinationCallback, c2profileName)
callbackGraph.Add(sourceCallback, destinationCallback, c2profileName, false)
return nil
}
func getC2ProfileIdForName(c2profileName string) int {
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.602591e6.css",
"main.js": "/new/static/js/main.db8dbd0a.js",
"main.js": "/new/static/js/main.740dc82b.js",
"static/media/mythic-red.png": "/new/static/media/mythic-red.203468a4e5240d239aa0.png",
"static/media/mythic_red_small.svg": "/new/static/media/mythic_red_small.793b41cc7135cdede246661ec232976b.svg",
"index.html": "/new/index.html",
"main.602591e6.css.map": "/new/static/css/main.602591e6.css.map",
"main.db8dbd0a.js.map": "/new/static/js/main.db8dbd0a.js.map"
"main.740dc82b.js.map": "/new/static/js/main.740dc82b.js.map"
},
"entrypoints": [
"static/css/main.602591e6.css",
"static/js/main.db8dbd0a.js"
"static/js/main.740dc82b.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.db8dbd0a.js"></script><link href="/new/static/css/main.602591e6.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.740dc82b.js"></script><link href="/new/static/css/main.602591e6.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 605cbf8

Please sign in to comment.