From eddbd551416e1fd6ab06fe02f396ab35a7aaf5fa Mon Sep 17 00:00:00 2001 From: its-a-feature Date: Sat, 14 Dec 2024 10:19:22 -0600 Subject: [PATCH] updating MythicRPC commands for Callback Search/Add/Remove --- CHANGELOG.MD | 7 ++ VERSION | 2 +- mythic-docker/src/VERSION | 2 +- .../recv_mythic_rpc_callback_add_command.go | 13 ++++ ...recv_mythic_rpc_callback_remove_command.go | 15 ++++- .../recv_mythic_rpc_callback_search.go | 64 ++++++++++++------- 6 files changed, 76 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 6f509217f..3198d674b 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -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.1-rc32] - 2024-12-14 + +### Changed + +- Updated MythicRPCCallbackSearch to specify a list of payload types +- Updated MythicRPCCallbackAddCommand and MythicRPCCallbackRemoveCommand to take in a list of callback ids + ## [3.3.1-rc31] - 2024-12-13 ### Changed diff --git a/VERSION b/VERSION index 02f2dcf6d..417e00141 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.3.1-rc31 \ No newline at end of file +3.3.1-rc32 \ No newline at end of file diff --git a/mythic-docker/src/VERSION b/mythic-docker/src/VERSION index 02f2dcf6d..417e00141 100644 --- a/mythic-docker/src/VERSION +++ b/mythic-docker/src/VERSION @@ -1 +1 @@ -3.3.1-rc31 \ No newline at end of file +3.3.1-rc32 \ No newline at end of file diff --git a/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_add_command.go b/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_add_command.go index 36899c80c..19646e0e2 100644 --- a/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_add_command.go +++ b/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_add_command.go @@ -15,6 +15,7 @@ type MythicRPCCallbackAddCommandMessage struct { AgentCallbackID string `json:"agent_callback_id"` Commands []string `json:"commands"` // required PayloadType string `json:"payload_type"` + CallbackIDs []int `json:"callback_ids"` } type MythicRPCCallbackAddCommandMessageResponse struct { Success bool `json:"success"` @@ -85,6 +86,18 @@ func MythicRPCCallbackAddCommand(input MythicRPCCallbackAddCommandMessage) Mythi response.Error = "No callback supplied" return response } + if len(input.CallbackIDs) > 0 { + for _, c := range input.CallbackIDs { + err := CallbackAddCommand(c, PayloadTypeID, OperatorID, input.Commands) + if err != nil { + logging.LogError(err, "Failed to add commands to callback") + response.Error = err.Error() + return response + } + } + response.Success = true + return response + } err := CallbackAddCommand(CallbackID, PayloadTypeID, OperatorID, input.Commands) if err != nil { logging.LogError(err, "Failed to add commands to callback") diff --git a/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_remove_command.go b/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_remove_command.go index 948fab97c..e50e8bc14 100644 --- a/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_remove_command.go +++ b/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_remove_command.go @@ -15,6 +15,7 @@ type MythicRPCCallbackRemoveCommandMessage struct { AgentCallbackID string `json:"agent_callback_id"` Commands []string `json:"commands"` // required PayloadType string `json:"payload_type"` + CallbackIDs []int `json:"callback_ids"` } type MythicRPCCallbackRemoveCommandMessageResponse struct { Success bool `json:"success"` @@ -26,7 +27,7 @@ func init() { Exchange: MYTHIC_EXCHANGE, Queue: MYTHIC_RPC_CALLBACK_REMOVE_COMMAND, RoutingKey: MYTHIC_RPC_CALLBACK_REMOVE_COMMAND, - Handler: processMythicRPCCallbackAddCommand, + Handler: processMythicRPCCallbackRemoveCommand, }) } @@ -85,6 +86,18 @@ func MythicRPCCallbackRemoveCommand(input MythicRPCCallbackRemoveCommandMessage) response.Error = "No callback supplied" return response } + if len(input.CallbackIDs) > 0 { + for _, c := range input.CallbackIDs { + err := CallbackRemoveCommand(c, PayloadTypeID, OperatorID, input.Commands) + if err != nil { + logging.LogError(err, "Failed to remove commands to callback") + response.Error = err.Error() + return response + } + } + response.Success = true + return response + } err := CallbackRemoveCommand(CallbackID, PayloadTypeID, OperatorID, input.Commands) if err != nil { logging.LogError(err, "Failed to remove commands to callback") diff --git a/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_search.go b/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_search.go index 56bd5a1ad..c6655ad41 100644 --- a/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_search.go +++ b/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_search.go @@ -3,6 +3,7 @@ package rabbitmq import ( "encoding/json" "github.com/mitchellh/mapstructure" + "slices" "strings" "time" @@ -13,23 +14,24 @@ import ( ) type MythicRPCCallbackSearchMessage struct { - AgentCallbackUUID string `json:"agent_callback_id"` - AgentCallbackID int `json:"callback_id"` - SearchCallbackID *int `json:"search_callback_id"` - SearchCallbackDisplayID *int `json:"search_callback_display_id"` - SearchCallbackUUID *string `json:"search_callback_uuid"` - SearchCallbackUser *string `json:"user,omitempty"` - SearchCallbackHost *string `json:"host,omitempty"` - SearchCallbackPID *int `json:"pid,omitempty"` - SearchCallbackExtraInfo *string `json:"extra_info,omitempty"` - SearchCallbackSleepInfo *string `json:"sleep_info,omitempty"` - SearchCallbackIP *string `json:"ip,omitempty"` - SearchCallbackExternalIP *string `json:"external_ip,omitempty"` - SearchCallbackIntegrityLevel *int `json:"integrity_level,omitempty"` - SearchCallbackOs *string `json:"os,omitempty"` - SearchCallbackDomain *string `json:"domain,omitempty"` - SearchCallbackArchitecture *string `json:"architecture,omitempty"` - SearchCallbackDescription *string `json:"description,omitempty"` + AgentCallbackUUID string `json:"agent_callback_id"` + AgentCallbackID int `json:"callback_id"` + SearchCallbackID *int `json:"search_callback_id"` + SearchCallbackDisplayID *int `json:"search_callback_display_id"` + SearchCallbackUUID *string `json:"search_callback_uuid"` + SearchCallbackUser *string `json:"user,omitempty"` + SearchCallbackHost *string `json:"host,omitempty"` + SearchCallbackPID *int `json:"pid,omitempty"` + SearchCallbackExtraInfo *string `json:"extra_info,omitempty"` + SearchCallbackSleepInfo *string `json:"sleep_info,omitempty"` + SearchCallbackIP *string `json:"ip,omitempty"` + SearchCallbackExternalIP *string `json:"external_ip,omitempty"` + SearchCallbackIntegrityLevel *int `json:"integrity_level,omitempty"` + SearchCallbackOs *string `json:"os,omitempty"` + SearchCallbackDomain *string `json:"domain,omitempty"` + SearchCallbackArchitecture *string `json:"architecture,omitempty"` + SearchCallbackDescription *string `json:"description,omitempty"` + SearchCallbackPayloadTypes *[]string `json:"payload_types,omitempty"` } type MythicRPCCallbackSearchMessageResult struct { ID int `mapstructure:"id" json:"id"` @@ -46,7 +48,9 @@ type MythicRPCCallbackSearchMessageResult struct { Description string `mapstructure:"description" json:"description"` OperatorID int `mapstructure:"operator_id" json:"operator_id"` Active bool `mapstructure:"active" json:"active"` + Dead bool `mapstructure:"dead" json:"dead"` RegisteredPayloadUUID string `mapstructure:"registered_payload_uuid" json:"registered_payload_uuid"` + PayloadType string `mapstructure:"payloadtype" json:"payloadtype"` IntegrityLevel int `mapstructure:"integrity_level" json:"integrity_level"` Locked bool `mapstructure:"locked" json:"locked"` LockedOperatorID int `mapstructure:"locked_operator_id" json:"locked_operator_id"` @@ -106,9 +110,11 @@ func MythicRPCCallbackSearch(input MythicRPCCallbackSearchMessage) MythicRPCCall } searchString := `SELECT callback.*, - payload.uuid "payload.uuid" + payload.uuid "payload.uuid", + payloadtype.name "payload.payloadtype.name" FROM callback JOIN payload on callback.registered_payload_id = payload.id + JOIN payloadtype on payload.payload_type_id = payloadtype.id WHERE callback.operation_id=:operation_id ` // if we're not actually searching for another callback, just set ours if input.SearchCallbackDisplayID != nil || input.SearchCallbackID != nil || input.SearchCallbackUUID != nil { @@ -175,17 +181,27 @@ func MythicRPCCallbackSearch(input MythicRPCCallbackSearchMessage) MythicRPCCall } for rows.Next() { result := MythicRPCCallbackSearchMessageResult{} - if err = rows.StructScan(&searchResults); err != nil { + err = rows.StructScan(&searchResults) + if err != nil { logging.LogError(err, "Failed to get row from callbacks for search") - } else if err = mapstructure.Decode(searchResults, &result); err != nil { + continue + } + err = mapstructure.Decode(searchResults, &result) + if err != nil { logging.LogError(err, "Failed to map callback search results into array") response.Error = err.Error() return response - } else { - result.RegisteredPayloadUUID = searchResults.Payload.UuID - result.LockedOperatorID = int(searchResults.LockedOperatorID.Int64) - response.Results = append(response.Results, result) } + if input.SearchCallbackPayloadTypes != nil && len(*input.SearchCallbackPayloadTypes) > 0 { + if !slices.Contains(*input.SearchCallbackPayloadTypes, searchResults.Payload.Payloadtype.Name) { + continue + } + } + result.RegisteredPayloadUUID = searchResults.Payload.UuID + result.LockedOperatorID = int(searchResults.LockedOperatorID.Int64) + result.PayloadType = searchResults.Payload.Payloadtype.Name + response.Results = append(response.Results, result) + } response.Success = true return response