Skip to content

Commit

Permalink
v3.2.20-rc7
Browse files Browse the repository at this point in the history
added more c2 profile inner server restarts in case of file changes
  • Loading branch information
its-a-feature committed Mar 29, 2024
1 parent d7c42e7 commit 1cf8948
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 45 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.2.20-rc7] - 2024-03-29

### Changed

- When payloads are built, files hosted, files written, or agent configurations checked, Mythic now restarts a C2 profile's server in case there were updates
- Added a fix for C2Profile Parameter Type of File

## [3.2.20-rc6] - 2024-03-25

### Changed
Expand Down
1 change: 1 addition & 0 deletions mythic-docker/src/rabbitmq/recv_c2_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const (
C2_PARAMETER_TYPE_DICTIONARY = "Dictionary"
C2_PARAMETER_TYPE_NUMBER = "Number"
C2_PARAMETER_TYPE_TYPED_ARRAY = "TypedArray"
C2_PARAMETER_TYPE_FILE = "File"
)

type C2Parameter struct {
Expand Down
46 changes: 46 additions & 0 deletions mythic-docker/src/rabbitmq/send_c2_rpc_start_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package rabbitmq

import (
"encoding/json"
"github.com/its-a-feature/Mythic/database"

"github.com/its-a-feature/Mythic/logging"
)
Expand All @@ -21,6 +22,51 @@ type C2StartServerMessageResponse struct {
InternalServerRunning bool `json:"server_running"`
}

func RestartC2ServerAfterUpdate(c2ProfileName string, sendNotifications bool) {
go func() {
if sendNotifications {
go SendAllOperationsMessage("Stopping C2 Profile after hosting new file...", 0, "host_file", database.MESSAGE_LEVEL_INFO)
}
stopC2ProfileResponse, err := RabbitMQConnection.SendC2RPCStopServer(C2StopServerMessage{
Name: c2ProfileName,
})
if err != nil {
logging.LogError(err, "Failed to send RPC call to c2 profile in C2HostFileMessageWebhook", "c2_profile", c2ProfileName)
if sendNotifications {
go SendAllOperationsMessage("Failed to stop c2 profile after hosting file", 0, "host_file", database.MESSAGE_LEVEL_WARNING)
}
return
}
if !stopC2ProfileResponse.Success {
if sendNotifications {
go SendAllOperationsMessage(stopC2ProfileResponse.Error, 0, "", database.MESSAGE_LEVEL_WARNING)
}
return
}
if sendNotifications {
go SendAllOperationsMessage("Starting C2 Profile after hosting new file...", 0, "host_file", database.MESSAGE_LEVEL_INFO)
}
startC2ProfileResponse, err := RabbitMQConnection.SendC2RPCStartServer(C2StartServerMessage{
Name: c2ProfileName,
})
if err != nil {
logging.LogError(err, "Failed to send RPC call to c2 profile in C2HostFileMessageWebhook", "c2_profile", c2ProfileName)
if sendNotifications {
go SendAllOperationsMessage("Failed to start c2 profile after hosting file", 0, "", database.MESSAGE_LEVEL_WARNING)
}
return
}
if !startC2ProfileResponse.Success {
if sendNotifications {
go SendAllOperationsMessage(startC2ProfileResponse.Error, 0, "", database.MESSAGE_LEVEL_WARNING)
}
return
}
if sendNotifications {
go SendAllOperationsMessage("Successfully restarted C2 Profile after hosting a file", 0, "host_file", database.MESSAGE_LEVEL_INFO)
}
}()
}
func (r *rabbitMQConnection) SendC2RPCStartServer(startServer C2StartServerMessage) (*C2StartServerMessageResponse, error) {
c2StartServerResponse := C2StartServerMessageResponse{}
exclusiveQueue := true
Expand Down
1 change: 1 addition & 0 deletions mythic-docker/src/rabbitmq/send_pt_payload_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ func SendPayloadBuildMessage(databasePayload databaseStructs.Payload, buildMessa
checksPassed = false
buildOutput += "[-] !!! C2 Configuration check failed !!! \n" + configCheckResponse.Error + "\n"
} else {
go RestartC2ServerAfterUpdate(c2.Name, false)
buildOutput += configCheckResponse.Message + "\n"
}
if !c2.IsP2P {
Expand Down
2 changes: 1 addition & 1 deletion mythic-docker/src/rabbitmq/util_agent_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func recursiveProcessAgentMessage(agentMessageInput AgentMessageRawInput) recurs
} else if agentMessageInput.RawMessage != nil {
base64DecodedMessage = *agentMessageInput.RawMessage
} else {
errorMessage := "Failed toget message\n"
errorMessage := "Failed to get message\n"
errorMessage += fmt.Sprintf("Connection from %s\n", agentMessageInput.RemoteIP)
logging.LogError(err, "Failed to get agent message")
go SendAllOperationsMessage(errorMessage, 0, "agent_message_base64", database.MESSAGE_LEVEL_WARNING)
Expand Down
11 changes: 7 additions & 4 deletions mythic-docker/src/rabbitmq/util_callback_graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package rabbitmq

import (
"database/sql"
"errors"
"fmt"
"github.com/its-a-feature/Mythic/database"
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
Expand Down Expand Up @@ -199,14 +200,16 @@ func (g *cbGraph) AddByAgentIds(source string, destination string, c2profileName
g.Add(sourceCallback, destinationCallback, c2profileName)
g.Add(destinationCallback, sourceCallback, c2profileName)
// can't have a unique constraint with a NULL value, NULL != NULL
if err := database.DB.Get(&edge.ID, `SELECT id FROM callbackgraphedge
err := database.DB.Get(&edge.ID, `SELECT id FROM callbackgraphedge
WHERE operation_id=$1 AND source_id=$2 AND destination_id=$3 AND
c2_profile_id=$4 AND end_timestamp IS NULL`,
edge.OperationID, edge.SourceID, edge.DestinationID, edge.C2ProfileID); err == sql.ErrNoRows {
edge.OperationID, edge.SourceID, edge.DestinationID, edge.C2ProfileID)
if errors.Is(err, sql.ErrNoRows) {
// this specific combination didn't yield any results, so add it
if _, err := database.DB.NamedExec(`INSERT INTO callbackgraphedge
_, err := database.DB.NamedExec(`INSERT INTO callbackgraphedge
(operation_id, source_id, destination_id, c2_profile_id)
VALUES (:operation_id, :source_id, :destination_id, :c2_profile_id)`, edge); err != nil {
VALUES (:operation_id, :source_id, :destination_id, :c2_profile_id)`, edge)
if err != nil {
logging.LogError(err, "Failed to insert new edge for P2P connection")
} else {
logging.LogInfo("added new callbackgraph edge when updating graph by agent ids")
Expand Down
2 changes: 2 additions & 0 deletions mythic-docker/src/rabbitmq/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ func getFinalStringForDatabaseInstanceValueFromDefaultDatabaseString(parameterTy
} else {
return strings.TrimSpace(defaultValue), nil
}
case BUILD_PARAMETER_TYPE_FILE:
fallthrough
case BUILD_PARAMETER_TYPE_STRING:
return strings.TrimSpace(defaultValue), nil
case BUILD_PARAMETER_TYPE_CHOOSE_MULTIPLE:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ func C2ProfileConfigCheckWebhook(c *gin.Context) {
} else {
output += fmt.Sprintf("Configuration Check for %s\n%s\n", c2ProfileName, c2ConfigCheckMessageResponse.Message)
}
go rabbitmq.RestartC2ServerAfterUpdate(c2ProfileName, false)
}
c.JSON(http.StatusOK, GetC2ConfigCheckResponse{
Status: "success",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,35 +82,7 @@ func C2HostFileMessageWebhook(c *gin.Context) {
})
return
}
go func() {
go rabbitmq.SendAllOperationsMessage("Stopping C2 Profile after hosting new file...", 0, "host_file", database.MESSAGE_LEVEL_INFO)
stopC2ProfileResponse, err := rabbitmq.RabbitMQConnection.SendC2RPCStopServer(rabbitmq.C2StopServerMessage{
Name: c2Profile.Name,
})
if err != nil {
logging.LogError(err, "Failed to send RPC call to c2 profile in C2HostFileMessageWebhook", "c2_profile", c2Profile.Name)
go rabbitmq.SendAllOperationsMessage("Failed to stop c2 profile after hosting file", 0, "host_file", database.MESSAGE_LEVEL_WARNING)
return
}
if !stopC2ProfileResponse.Success {
go rabbitmq.SendAllOperationsMessage(stopC2ProfileResponse.Error, 0, "", database.MESSAGE_LEVEL_WARNING)
return
}
go rabbitmq.SendAllOperationsMessage("Starting C2 Profile after hosting new file...", 0, "host_file", database.MESSAGE_LEVEL_INFO)
startC2ProfileResponse, err := rabbitmq.RabbitMQConnection.SendC2RPCStartServer(rabbitmq.C2StartServerMessage{
Name: c2Profile.Name,
})
if err != nil {
logging.LogError(err, "Failed to send RPC call to c2 profile in C2HostFileMessageWebhook", "c2_profile", c2Profile.Name)
go rabbitmq.SendAllOperationsMessage("Failed to start c2 profile after hosting file", 0, "", database.MESSAGE_LEVEL_WARNING)
return
}
if !startC2ProfileResponse.Success {
go rabbitmq.SendAllOperationsMessage(startC2ProfileResponse.Error, 0, "", database.MESSAGE_LEVEL_WARNING)
return
}
go rabbitmq.SendAllOperationsMessage("Successfully restarted C2 Profile after hosting a file", 0, "host_file", database.MESSAGE_LEVEL_INFO)
}()
go rabbitmq.RestartC2ServerAfterUpdate(c2Profile.Name, true)
c.JSON(http.StatusOK, C2HostFileMessageResponse{
Status: "success",
Error: "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,45 +40,51 @@ func C2ProfileWriteFileWebhook(c *gin.Context) {
// get the associated database information
c2profile := databaseStructs.C2profile{}

if err := database.DB.Get(&c2profile, `SELECT "name" FROM c2profile WHERE id=$1`, input.Input.C2ProfileID); err != nil {
err := database.DB.Get(&c2profile, `SELECT "name" FROM c2profile WHERE id=$1`, input.Input.C2ProfileID)
if err != nil {
logging.LogError(err, "Failed to fetch c2 profile from database by ID", "id", input.Input.C2ProfileID)
c.JSON(http.StatusOK, WriteContainerFileResponse{
Status: "error",
Error: "Failed to find C2 Profile",
})
return
// send the RPC request to the container
} else if fileContents, err := base64.StdEncoding.DecodeString(input.Input.Data); err != nil {
}
fileContents, err := base64.StdEncoding.DecodeString(input.Input.Data)
if err != nil {
logging.LogError(err, "Failed to base64 decode file contents")
c.JSON(http.StatusOK, WriteContainerFileResponse{
Status: "error",
Error: "Failed to base64 decode file contents",
})
return
} else if c2ProfileResponse, err := rabbitmq.RabbitMQConnection.SendC2RPCWriteFile(rabbitmq.C2WriteFileMessage{
}
c2ProfileResponse, err := rabbitmq.RabbitMQConnection.SendC2RPCWriteFile(rabbitmq.C2WriteFileMessage{
Filename: input.Input.Filename,
Name: c2profile.Name,
Contents: fileContents,
}); err != nil {
})
if err != nil {
logging.LogError(err, "Failed to send C2ProfileWriteFileWebhook to c2 profile", "c2_profile", c2profile.Name)
c.JSON(http.StatusOK, WriteContainerFileResponse{
Status: "error",
Error: "Failed to send message to C2 Profile container",
})
return
// check the response from the RPC call for success or error
} else if !c2ProfileResponse.Success {
}
if !c2ProfileResponse.Success {
logging.LogError(nil, c2ProfileResponse.Error, "Failed to write file to c2 container")
c.JSON(http.StatusOK, WriteContainerFileResponse{
Status: "error",
Error: c2ProfileResponse.Error,
})
return
} else {

c.JSON(http.StatusOK, WriteContainerFileResponse{
Status: "success",
})
return
}
go rabbitmq.RestartC2ServerAfterUpdate(c2profile.Name, false)
c.JSON(http.StatusOK, WriteContainerFileResponse{
Status: "success",
})
return

}

0 comments on commit 1cf8948

Please sign in to comment.