logging updates and more operation scope checks added to routes

This commit is contained in:
its-a-feature
2026-04-21 08:42:29 -05:00
parent 5da30beb8d
commit 9ef22f0299
14 changed files with 178 additions and 37 deletions
@@ -1,11 +1,12 @@
package database
import (
"strings"
"github.com/google/uuid"
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
"github.com/its-a-feature/Mythic/logging"
"github.com/its-a-feature/Mythic/utils"
"strings"
)
func CreateOperationBotForOperation(newOperation databaseStructs.Operation) {
@@ -35,7 +36,7 @@ func CreateOperationBotForOperation(newOperation databaseStructs.Operation) {
}
err = statement.Get(&newOperator.ID, newOperator)
if err != nil {
logging.LogError(err, "Failed to create new operator", "operator", newOperator)
logging.LogError(err, "Failed to create new operator")
return
}
// add the bot account to the operation
@@ -48,7 +49,7 @@ func CreateOperationBotForOperation(newOperation databaseStructs.Operation) {
(operator_id, operation_id, view_mode)
VALUES (:operator_id, :operation_id, :view_mode)`, newOperatorOperation)
if err != nil {
logging.LogError(err, "Failed to create new operatorOperation mapping", "operatoroperation", newOperatorOperation)
logging.LogError(err, "Failed to create new operatorOperation mapping")
return
}
}
+1 -1
View File
@@ -67,7 +67,7 @@ func Initialize() {
mythicEnv.SetDefault("mythic_docker_networking", "bridge")
mythicEnv.SetDefault("mythic_server_grpc_port", 17444)
mythicEnv.SetDefault("mythic_admin_user", "mythic_admin")
mythicEnv.SetDefault("mythic_admin_password", "mythic_password")
mythicEnv.SetDefault("mythic_admin_password", "")
mythicEnv.SetDefault("allowed_ip_blocks", "0.0.0.0/0")
mythicEnv.SetDefault("debug_level", "warning")
mythicEnv.SetDefault("mythic_server_allow_invite_links", false)
@@ -37,9 +37,29 @@ func C2ProfileConfigCheckWebhook(c *gin.Context) {
})
return
}
userID, err := GetUserIDFromGin(c)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, GetC2ConfigCheckResponse{
Status: "error",
Error: err.Error(),
})
return
}
user, err := database.GetUserFromID(userID)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, GetC2ConfigCheckResponse{
Status: "error",
Error: err.Error(),
})
return
}
// get the associated database information
payload := databaseStructs.Payload{}
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1`, input.Input.PayloadUUID); err != nil {
if err := database.DB.Get(&payload, `SELECT
id FROM payload
WHERE uuid=$1 AND operation_id=$2`, input.Input.PayloadUUID, user.CurrentOperationID.Int64); err != nil {
logging.LogError(err, "Failed to find payload when doing a C2ProfileConfigCheckWebhook")
}
c2profileParameterInstances := []databaseStructs.C2profileparametersinstance{}
@@ -37,9 +37,29 @@ func C2ProfileGetIOCWebhook(c *gin.Context) {
})
return
}
userID, err := GetUserIDFromGin(c)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, GetC2GetIOCResponse{
Status: "error",
Error: err.Error(),
})
return
}
user, err := database.GetUserFromID(userID)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, GetC2GetIOCResponse{
Status: "error",
Error: err.Error(),
})
return
}
// get the associated database information
payload := databaseStructs.Payload{}
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1`, input.Input.PayloadUUID); err != nil {
if err := database.DB.Get(&payload, `SELECT
id FROM payload
WHERE uuid=$1 AND operation_id=$2`, input.Input.PayloadUUID, user.CurrentOperationID.Int64); err != nil {
logging.LogError(err, "Failed to find payload when doing a C2ProfileConfigCheckWebhook")
}
c2profileParameterInstances := []databaseStructs.C2profileparametersinstance{}
@@ -37,9 +37,29 @@ func C2ProfileRedirectRulesWebhook(c *gin.Context) {
})
return
}
userID, err := GetUserIDFromGin(c)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, GetC2RedirectRulesResponse{
Status: "error",
Error: err.Error(),
})
return
}
user, err := database.GetUserFromID(userID)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, GetC2RedirectRulesResponse{
Status: "error",
Error: err.Error(),
})
return
}
// get the associated database information
payload := databaseStructs.Payload{}
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1`, input.Input.PayloadUUID); err != nil {
if err := database.DB.Get(&payload, `SELECT
id FROM payload
WHERE uuid=$1 AND operation_id=$2`, input.Input.PayloadUUID, user.CurrentOperationID.Int64); err != nil {
logging.LogError(err, "Failed to find payload when doing a C2ProfileRedirectRulesWebhook")
}
c2profileParameterInstances := []databaseStructs.C2profileparametersinstance{}
@@ -37,9 +37,29 @@ func C2ProfileSampleMessageWebhook(c *gin.Context) {
})
return
}
userID, err := GetUserIDFromGin(c)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, GetC2SampleMessageResponse{
Status: "error",
Error: err.Error(),
})
return
}
user, err := database.GetUserFromID(userID)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, GetC2SampleMessageResponse{
Status: "error",
Error: err.Error(),
})
return
}
// get the associated database information
payload := databaseStructs.Payload{}
if err := database.DB.Get(&payload, `SELECT id FROM payload WHERE uuid=$1`, input.Input.PayloadUUID); err != nil {
if err := database.DB.Get(&payload, `SELECT
id FROM payload
WHERE uuid=$1 AND operation_id=$2`, input.Input.PayloadUUID, user.CurrentOperationID.Int64); err != nil {
logging.LogError(err, "Failed to find payload when doing a C2ProfileConfigCheckWebhook")
}
c2profileParameterInstances := []databaseStructs.C2profileparametersinstance{}
@@ -1,9 +1,12 @@
package webcontroller
import (
"github.com/its-a-feature/Mythic/rabbitmq"
"net/http"
"github.com/its-a-feature/Mythic/database"
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
"github.com/its-a-feature/Mythic/rabbitmq"
"github.com/gin-gonic/gin"
"github.com/its-a-feature/Mythic/logging"
)
@@ -36,6 +39,26 @@ func CreateCallbackWebhook(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "error", "error": err.Error()})
return
}
userID, err := GetUserIDFromGin(c)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, gin.H{"status": "error", "error": err.Error()})
return
}
user, err := database.GetUserFromID(userID)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, gin.H{"status": "error", "error": err.Error()})
return
}
payload := databaseStructs.Payload{}
if err := database.DB.Get(&payload, `SELECT
id FROM payload
WHERE uuid=$1 AND operation_id=$2`, input.Input.PayloadUUID, user.CurrentOperationID.Int64); err != nil {
logging.LogError(err, "Failed to find payload when doing a CreateCallbackWebhook")
c.JSON(http.StatusOK, gin.H{"status": "error", "error": err.Error()})
return
}
callbackCreateResponse := rabbitmq.MythicRPCCallbackCreate(rabbitmq.MythicRPCCallbackCreateMessage{
PayloadUUID: input.Input.PayloadUUID,
User: input.Input.CallbackConfig.User,
@@ -67,7 +67,9 @@ func ExportCallbackConfigWebhook(c *gin.Context) {
operatorOperation := ginOperatorOperation.(*databaseStructs.Operatoroperation)
callback := databaseStructs.Callback{}
payload := databaseStructs.Payload{}
err = database.DB.Get(&callback, `SELECT * FROM callback WHERE agent_callback_id=$1`, input.Input.AgentCallbackID)
err = database.DB.Get(&callback, `SELECT
* FROM callback
WHERE agent_callback_id=$1 AND operation_id=$2`, input.Input.AgentCallbackID, operatorOperation.CurrentOperation.ID)
if err != nil {
logging.LogError(err, "Failed to get callback from database")
c.JSON(http.StatusOK, ExportCallbackConfigResponse{
@@ -3,25 +3,26 @@ package webcontroller
import (
"errors"
"fmt"
"net/http"
"strings"
"sync"
"github.com/gin-gonic/gin"
"github.com/its-a-feature/Mythic/authentication"
"github.com/its-a-feature/Mythic/authentication/mythicjwt"
"github.com/its-a-feature/Mythic/database"
"github.com/its-a-feature/Mythic/logging"
"net/http"
"strings"
"sync"
)
var (
hasuraClaimsCache = make(map[int]map[string]interface{})
hasuraClaimsCache = make(map[string]map[string]interface{})
hasuraClaimsCacheLock = sync.RWMutex{}
)
func UpdateHasuraClaims(c *gin.Context, invalidateAllOthers bool) error {
if invalidateAllOthers {
hasuraClaimsCacheLock.Lock()
hasuraClaimsCache = make(map[int]map[string]interface{})
hasuraClaimsCache = make(map[string]map[string]interface{})
hasuraClaimsCacheLock.Unlock()
return nil
}
@@ -35,7 +36,7 @@ func UpdateHasuraClaims(c *gin.Context, invalidateAllOthers bool) error {
return err
}
c.Set("GraphQLName", hasuraInput.Request.OperationName)
c.Set("user_id", claims.UserID)
c.Set(authentication.ContextKeyUserID, claims.UserID)
c.Set("hasura_claims", claims)
hasuraClaims := make(map[string]interface{})
//logging.LogTrace("JWT claims", "claims", claims, "user", claims.UserID)
@@ -48,7 +49,7 @@ func UpdateHasuraClaims(c *gin.Context, invalidateAllOthers bool) error {
logging.LogError(err, "Failed to fetch operator based on JWT UserID")
return err
}
c.Set("username", user.Username)
c.Set(authentication.ContextKeyUsername, user.Username)
if !user.CurrentOperationID.Valid {
hasuraClaims["x-hasura-current-operation-id"] = "0"
hasuraClaims["x-hasura-current_operation"] = "null"
@@ -96,7 +97,9 @@ func UpdateHasuraClaims(c *gin.Context, invalidateAllOthers bool) error {
hasuraClaims["x-hasura-current-operation-id"] = fmt.Sprintf("%d", claims.OperationID)
}
hasuraClaimsCacheLock.Lock()
hasuraClaimsCache[claims.UserID] = hasuraClaims
hasuraClaimsCache[fmt.Sprintf("%d-%s-%d-%d",
claims.UserID, claims.AuthMethod, claims.OperationID, claims.APITokensID,
)] = hasuraClaims
hasuraClaimsCacheLock.Unlock()
return nil
}
@@ -124,10 +127,12 @@ func GetHasuraClaims(c *gin.Context) {
return
}
c.Set("GraphQLName", hasuraInput.Request.OperationName)
c.Set("user_id", claims.UserID)
c.Set(authentication.ContextKeyUserID, claims.UserID)
c.Set("hasura_claims", claims)
hasuraClaimsCacheLock.RLock()
hasuraClaims, ok := hasuraClaimsCache[claims.UserID]
hasuraClaims, ok := hasuraClaimsCache[fmt.Sprintf("%d-%s-%d-%d",
claims.UserID, claims.AuthMethod, claims.OperationID, claims.APITokensID,
)]
hasuraClaimsCacheLock.RUnlock()
if ok {
c.JSON(http.StatusOK, hasuraClaims)
@@ -140,7 +145,9 @@ func GetHasuraClaims(c *gin.Context) {
return
}
hasuraClaimsCacheLock.RLock()
hasuraClaims, ok = hasuraClaimsCache[claims.UserID]
hasuraClaims, ok = hasuraClaimsCache[fmt.Sprintf("%d-%s-%d-%d",
claims.UserID, claims.AuthMethod, claims.OperationID, claims.APITokensID,
)]
hasuraClaimsCacheLock.RUnlock()
if ok {
c.JSON(http.StatusOK, hasuraClaims)
@@ -4,6 +4,10 @@ import (
"database/sql"
"errors"
"fmt"
"net/http"
"sync"
"time"
"github.com/gin-gonic/gin"
"github.com/go-viper/mapstructure/v2"
"github.com/google/uuid"
@@ -12,9 +16,6 @@ import (
"github.com/its-a-feature/Mythic/logging"
"github.com/its-a-feature/Mythic/rabbitmq"
"github.com/its-a-feature/Mythic/utils"
"net/http"
"sync"
"time"
)
type CreateInviteLinkMessage struct {
@@ -243,7 +244,7 @@ func UseInviteLink(c *gin.Context) {
}
err = statement.Get(&newOperator.ID, newOperator)
if err != nil {
logging.LogError(err, "Failed to create new operator", "operator", newOperator)
logging.LogError(err, "Failed to create new operator")
c.JSON(http.StatusOK, UseInviteLinkResponse{
Status: "error",
Error: err.Error(),
@@ -1,9 +1,10 @@
package webcontroller
import (
"net/http"
"github.com/its-a-feature/Mythic/database"
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
"net/http"
"github.com/gin-gonic/gin"
"github.com/its-a-feature/Mythic/logging"
@@ -45,7 +46,7 @@ func PayloadTypeDynamicQueryFunctionWebhook(c *gin.Context) {
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, PayloadTypeDynamicQueryFunctionResponse{
Status: rabbitmq.PT_DYNAMIC_QUERY_FUNCTION_STATUS_ERROR,
Status: "error",
Error: err.Error(),
ParameterName: input.Input.ParameterName,
})
@@ -55,7 +56,7 @@ func PayloadTypeDynamicQueryFunctionWebhook(c *gin.Context) {
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, PayloadTypeDynamicQueryFunctionResponse{
Status: rabbitmq.PT_DYNAMIC_QUERY_FUNCTION_STATUS_ERROR,
Status: "error",
Error: err.Error(),
ParameterName: input.Input.ParameterName,
})
@@ -67,7 +68,9 @@ func PayloadTypeDynamicQueryFunctionWebhook(c *gin.Context) {
FROM loadedcommands
JOIN command ON loadedcommands.command_id = command.id
JOIN payloadtype ON command.payload_type_id = payloadtype.id
WHERE callback_id = $1 AND command.cmd=$2 AND payloadtype.name=$3`, input.Input.Callback, input.Input.Command, input.Input.PayloadType)
JOIN callback ON loadedcommands.callback_id = callback.id
WHERE callback_id = $1 AND command.cmd=$2 AND payloadtype.name=$3 AND callback.operation_id=$4`,
input.Input.Callback, input.Input.Command, input.Input.PayloadType, user.CurrentOperationID.Int64)
if err != nil {
logging.LogError(err, "Failed to get command from loaded commands")
c.JSON(http.StatusOK, PayloadTypeDynamicQueryFunctionResponse{
@@ -1,9 +1,10 @@
package webcontroller
import (
"net/http"
"github.com/its-a-feature/Mythic/database"
databaseStructs "github.com/its-a-feature/Mythic/database/structs"
"net/http"
"github.com/gin-gonic/gin"
"github.com/its-a-feature/Mythic/logging"
@@ -39,13 +40,33 @@ func PayloadTypeDynamicTypedArrayParseWebhook(c *gin.Context) {
})
return
}
userID, err := GetUserIDFromGin(c)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, PayloadTypeDynamicTypedArrayParseResponse{
Status: "error",
Error: err.Error(),
})
return
}
user, err := database.GetUserFromID(userID)
if err != nil {
logging.LogError(err, "Failed to get userID from JWT")
c.JSON(http.StatusOK, PayloadTypeDynamicTypedArrayParseResponse{
Status: "error",
Error: err.Error(),
})
return
}
loadedCommand := databaseStructs.Loadedcommands{}
err := database.DB.Get(&loadedCommand, `SELECT
err = database.DB.Get(&loadedCommand, `SELECT
payloadtype.name "command.payloadtype.name"
FROM loadedcommands
JOIN command ON loadedcommands.command_id = command.id
JOIN payloadtype ON command.payload_type_id = payloadtype.id
WHERE callback_id = $1 AND command.cmd=$2 AND payloadtype.name=$3`, input.Input.Callback, input.Input.Command, input.Input.PayloadType)
JOIN callback ON loadedcommands.callback_id = callback.id
WHERE callback_id = $1 AND command.cmd=$2 AND payloadtype.name=$3 AND callback.operation_id=$4`,
input.Input.Callback, input.Input.Command, input.Input.PayloadType, user.CurrentOperationID.Int64)
if err != nil {
logging.LogError(err, "Failed to get command from loaded commands")
c.JSON(http.StatusOK, PayloadTypeDynamicQueryFunctionResponse{
@@ -3,13 +3,14 @@ package webcontroller
import (
"database/sql"
"errors"
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"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/utils"
"net/http"
)
type CreateOperatorInput struct {
@@ -137,7 +138,7 @@ func CreateOperatorWebhook(c *gin.Context) {
}
err = statement.Get(&newOperator.ID, newOperator)
if err != nil {
logging.LogError(err, "Failed to create new operator", "operator", newOperator)
logging.LogError(err, "Failed to create new operator")
c.JSON(http.StatusOK, CreateOperatorResponse{
Status: "error",
Error: err.Error(),
+5 -3
View File
@@ -38,6 +38,10 @@ func Initialize() *gin.Engine {
}
c.AbortWithStatus(http.StatusInternalServerError)
}))
err := r.SetTrustedProxies([]string{"127.0.0.1"})
if err != nil {
logging.LogError(err, "Failed to set trusted proxies")
}
r.RedirectFixedPath = true
r.HandleMethodNotAllowed = true
r.RemoveExtraSlash = true
@@ -87,10 +91,8 @@ func InitializeGinLogger() gin.HandlerFunc {
Request: c.Request,
Keys: c.Keys,
}
// Stop timer
param.TimeStamp = time.Now()
param.Path = path
if !utils.SliceContains(ignorePaths, param.Path) {
//if param.Path != "/graphql/webhook" && !strings.Contains(param.Path, "/agent_message") {
@@ -118,7 +120,7 @@ func InitializeGinLogger() gin.HandlerFunc {
token := tokenStruct.(databaseStructs.Apitokens)
go rabbitmq.SendAllOperationsMessage(fmt.Sprintf("APIToken, %s, for user %s (%s) just used %s",
token.Name, token.Operator.Username, token.Operator.AccountType, graphQLOperationName),
int(token.Operator.CurrentOperationID.Int64), token.TokenValue,
int(token.Operator.CurrentOperationID.Int64), database.MESSAGE_LEVEL_API,
database.MESSAGE_LEVEL_API, false)
}
}