diff --git a/CHANGELOG.MD b/CHANGELOG.MD index f87d07f8..6300ece7 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -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-rc25] - 2024-11-18 + +### Changed + +- Updated how file hosting works for C2 profiles + - When syncing to Mythic, C2 profiles get updated file hosting based on existing FileHosted tags in Mythic + - When stopping file hosting, Mythic ensures that the C2 profile successfully processed the request before updating the corresponding Tag + ## [3.3.1-rc24] - 2024-11-18 ### Changed diff --git a/VERSION b/VERSION index aea6fcd6..24231bb5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.3.1-rc24 \ No newline at end of file +3.3.1-rc25 \ No newline at end of file diff --git a/mythic-docker/src/VERSION b/mythic-docker/src/VERSION index aea6fcd6..24231bb5 100644 --- a/mythic-docker/src/VERSION +++ b/mythic-docker/src/VERSION @@ -1 +1 @@ -3.3.1-rc24 \ No newline at end of file +3.3.1-rc25 \ No newline at end of file diff --git a/mythic-docker/src/rabbitmq/check_container_status.go b/mythic-docker/src/rabbitmq/check_container_status.go index 50687876..163bd2ba 100644 --- a/mythic-docker/src/rabbitmq/check_container_status.go +++ b/mythic-docker/src/rabbitmq/check_container_status.go @@ -108,7 +108,7 @@ type rabbitmqAPIQuery struct { func createGraphQLSpectatorAPITokenAndSendOnStartMessage(containerName string) { operations := []databaseStructs.Operation{} - err := database.DB.Select(&operations, `SELECT id FROM operation WHERE deleted=false and completed=true`) + err := database.DB.Select(&operations, `SELECT id FROM operation WHERE deleted=false and complete=false`) if err != nil { logging.LogError(err, "Failed to fetch operations") return diff --git a/mythic-docker/src/rabbitmq/recv_c2_sync.go b/mythic-docker/src/rabbitmq/recv_c2_sync.go index 1f6bee68..dbd1b49d 100644 --- a/mythic-docker/src/rabbitmq/recv_c2_sync.go +++ b/mythic-docker/src/rabbitmq/recv_c2_sync.go @@ -9,6 +9,7 @@ import ( "github.com/its-a-feature/Mythic/logging" "github.com/its-a-feature/Mythic/utils" amqp "github.com/rabbitmq/amqp091-go" + "strings" ) // C2_SYNC STRUCTS @@ -311,4 +312,50 @@ func autoStartC2Profile(c2Profile databaseStructs.C2profile) { } } } + autoReHostFiles(c2Profile) +} + +func autoReHostFiles(c2Profile databaseStructs.C2profile) { + fileHostedTagType := databaseStructs.TagType{ + Name: "FileHosted", + } + err := database.DB.Get(&fileHostedTagType, `SELECT id FROM tagtype WHERE name=$1`, fileHostedTagType.Name) + if err != nil { + logging.LogError(err, "failed to get existing tag types") + return + } + currentTags := []databaseStructs.Tag{} + err = database.DB.Select(¤tTags, `SELECT * FROM tag WHERE tagtype_id=$1`, fileHostedTagType.ID) + if err != nil { + logging.LogError(err, "failed to get existing tags for FileHosted tagtype") + return + } + for _, tag := range currentTags { + dataStruct := tag.Data.StructValue() + for key, _ := range dataStruct { + if strings.HasPrefix(key, fmt.Sprintf("%s; ", c2Profile.Name)) { + newTagMap := dataStruct[key].(map[string]interface{}) + c2HostFileResponse, err := RabbitMQConnection.SendC2RPCHostFile(C2HostFileMessage{ + Name: newTagMap["c2_profile"].(string), + FileUUID: newTagMap["agent_file_id"].(string), + HostURL: newTagMap["host_url"].(string), + Remove: false, + }) + if err != nil { + logging.LogError(err, "failed to send host file message to c2 profile") + go SendAllOperationsMessage(fmt.Sprintf( + "%s failed to start hosting file:\n%s", newTagMap["c2_profile"].(string), + err.Error()), tag.Operation, "", database.MESSAGE_LEVEL_WARNING) + continue + } + if !c2HostFileResponse.Success { + logging.LogError(err, "c2 profile failed to start hosting file") + go SendAllOperationsMessage(fmt.Sprintf( + "%s failed to start hosting file:\n%s", newTagMap["c2_profile"].(string), + c2HostFileResponse.Error), tag.Operation, "", database.MESSAGE_LEVEL_WARNING) + continue + } + } + } + } } diff --git a/mythic-docker/src/webserver/controllers/c2profile_host_file_webhook.go b/mythic-docker/src/webserver/controllers/c2profile_host_file_webhook.go index 6efdd854..9daf5400 100644 --- a/mythic-docker/src/webserver/controllers/c2profile_host_file_webhook.go +++ b/mythic-docker/src/webserver/controllers/c2profile_host_file_webhook.go @@ -7,7 +7,6 @@ import ( "github.com/its-a-feature/Mythic/database" databaseStructs "github.com/its-a-feature/Mythic/database/structs" "github.com/its-a-feature/Mythic/logging" - "github.com/its-a-feature/Mythic/rabbitmq" ) type C2HostFileMessageInput struct { @@ -84,27 +83,6 @@ func C2HostFileMessageWebhook(c *gin.Context) { }) return } - c2HostFileResponse, err := rabbitmq.RabbitMQConnection.SendC2RPCHostFile(rabbitmq.C2HostFileMessage{ - Name: c2Profile.Name, - FileUUID: input.Input.FileUUID, - HostURL: input.Input.HostURL, - Remove: input.Input.Remove, - }) - if err != nil { - logging.LogError(err, "Failed to send RPC call to c2 profile in C2ProfileHostFileWebhook", "c2_profile", c2Profile.Name) - c.JSON(http.StatusOK, C2HostFileMessageResponse{ - Status: "error", - Error: "Failed to send RPC message to c2 profile", - }) - return - } - if !c2HostFileResponse.Success { - c.JSON(http.StatusOK, C2HostFileMessageResponse{ - Status: "error", - Error: c2HostFileResponse.Error, - }) - return - } go tagFileAs(hostFile.ID, operatorOperation.CurrentOperator.Username, hostFile.OperationID, tagTypeHostedByC2, map[string]interface{}{ c2Profile.Name + "; " + input.Input.HostURL: map[string]interface{}{ "c2_profile": c2Profile.Name, diff --git a/mythic-docker/src/webserver/controllers/utils.go b/mythic-docker/src/webserver/controllers/utils.go index d01a0faf..74c682d6 100644 --- a/mythic-docker/src/webserver/controllers/utils.go +++ b/mythic-docker/src/webserver/controllers/utils.go @@ -195,12 +195,53 @@ Target Host: %s`, newTagMap["c2_profile"].(string) == oldTagMap["c2_profile"].(string) && (newTagMap["host_url"].(string) == "" || newTagMap["host_url"].(string) == oldTagMap["host_url"].(string)) { + c2HostFileResponse, err := rabbitmq.RabbitMQConnection.SendC2RPCHostFile(rabbitmq.C2HostFileMessage{ + Name: newTagMap["c2_profile"].(string), + FileUUID: newTagMap["agent_file_id"].(string), + HostURL: newTagMap["host_url"].(string), + Remove: remove, + }) + if err != nil { + logging.LogError(err, "failed to send message to container to stop hosting it") + go rabbitmq.SendAllOperationsMessage(fmt.Sprintf( + "%s failed to stop hosting file:\n%s", newTagMap["c2_profile"].(string), + err.Error()), operationID, "", database.MESSAGE_LEVEL_WARNING) + continue + } + if !c2HostFileResponse.Success { + logging.LogError(err, "c2 profile failed to stop hosting file") + go rabbitmq.SendAllOperationsMessage(fmt.Sprintf( + "%s failed to stop hosting file:\n%s", newTagMap["c2_profile"].(string), + c2HostFileResponse.Error), operationID, "", database.MESSAGE_LEVEL_WARNING) + continue + } delete(updateTagData, key) } } } } else { for key, val := range tagData { + newTagMap := val.(map[string]interface{}) + c2HostFileResponse, err := rabbitmq.RabbitMQConnection.SendC2RPCHostFile(rabbitmq.C2HostFileMessage{ + Name: newTagMap["c2_profile"].(string), + FileUUID: newTagMap["agent_file_id"].(string), + HostURL: newTagMap["host_url"].(string), + Remove: remove, + }) + if err != nil { + logging.LogError(err, "failed to send host file message to c2 profile") + go rabbitmq.SendAllOperationsMessage(fmt.Sprintf( + "%s failed to start hosting file:\n%s", newTagMap["c2_profile"].(string), + err.Error()), operationID, "", database.MESSAGE_LEVEL_WARNING) + continue + } + if !c2HostFileResponse.Success { + logging.LogError(err, "c2 profile failed to start hosting file") + go rabbitmq.SendAllOperationsMessage(fmt.Sprintf( + "%s failed to start hosting file:\n%s", newTagMap["c2_profile"].(string), + c2HostFileResponse.Error), operationID, "", database.MESSAGE_LEVEL_WARNING) + continue + } updateTagData[key] = val } }