diff --git a/MythicReactUI/src/components/MythicComponents/MythicAgentSVGIcon.js b/MythicReactUI/src/components/MythicComponents/MythicAgentSVGIcon.js index 71d1377e..b7b30055 100644 --- a/MythicReactUI/src/components/MythicComponents/MythicAgentSVGIcon.js +++ b/MythicReactUI/src/components/MythicComponents/MythicAgentSVGIcon.js @@ -3,6 +3,7 @@ import {MythicStyledTooltip} from "./MythicStyledTooltip"; import WifiIcon from '@mui/icons-material/Wifi'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import { faLink } from '@fortawesome/free-solid-svg-icons'; +import {ImageWithAuth} from "../utilities/ImageWithAuth"; export const MythicAgentSVGIcon = ({payload_type, style, is_p2p}) => { const theme = useTheme(); @@ -11,8 +12,8 @@ export const MythicAgentSVGIcon = ({payload_type, style, is_p2p}) => { } return ( - + {is_p2p === false && - + {is_p2p === false && { diff --git a/MythicReactUI/src/components/pages/Callbacks/C2PathDialog.js b/MythicReactUI/src/components/pages/Callbacks/C2PathDialog.js index 57ae1aab..265c4ba3 100644 --- a/MythicReactUI/src/components/pages/Callbacks/C2PathDialog.js +++ b/MythicReactUI/src/components/pages/Callbacks/C2PathDialog.js @@ -38,6 +38,7 @@ import {getIconName} from "./ResponseDisplayTable"; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import RestartAltIcon from '@mui/icons-material/RestartAlt'; import { MythicAgentSVGIconNoTooltip} from "../../MythicComponents/MythicAgentSVGIcon"; +import {ImageWithAuth} from "../../utilities/ImageWithAuth"; const ITEM_HEIGHT = 48; const ITEM_PADDING_TOP = 8; @@ -399,7 +400,7 @@ function AgentNode({data}) { )) } - {data.img} + {data.label} diff --git a/MythicReactUI/src/components/pages/Callbacks/CallbacksTabsFileBrowserTable.js b/MythicReactUI/src/components/pages/Callbacks/CallbacksTabsFileBrowserTable.js index f351bfc8..41da8b0a 100644 --- a/MythicReactUI/src/components/pages/Callbacks/CallbacksTabsFileBrowserTable.js +++ b/MythicReactUI/src/components/pages/Callbacks/CallbacksTabsFileBrowserTable.js @@ -31,7 +31,7 @@ import SettingsIcon from '@mui/icons-material/Settings'; import { toLocalTime } from '../../utilities/Time'; import {b64DecodeUnicode} from "./ResponseDisplay"; import {faPhotoVideo} from '@fortawesome/free-solid-svg-icons'; -import {PreviewFileMediaDialog} from "../Search/PreviewFileMedia"; +import {PreviewFileMediaDialog} from "../../MythicComponents/PreviewFileMedia"; import RefreshIcon from '@mui/icons-material/Refresh'; import {Dropdown, DropdownMenuItem, DropdownNestedMenuItem} from "../../MythicComponents/MythicNestedMenus"; import {RenderSingleTask} from "../SingleTaskView/SingleTaskView"; diff --git a/MythicReactUI/src/components/pages/Callbacks/DownloadHistoryDialog.js b/MythicReactUI/src/components/pages/Callbacks/DownloadHistoryDialog.js index cc5933c2..b35562a6 100644 --- a/MythicReactUI/src/components/pages/Callbacks/DownloadHistoryDialog.js +++ b/MythicReactUI/src/components/pages/Callbacks/DownloadHistoryDialog.js @@ -10,7 +10,7 @@ import DialogTitle from '@mui/material/DialogTitle'; import {Button, Link, Typography} from '@mui/material'; import {MythicStyledTooltip} from "../../MythicComponents/MythicStyledTooltip"; import {MythicDialog} from "../../MythicComponents/MythicDialog"; -import {PreviewFileMediaDialog} from "../Search/PreviewFileMedia"; +import {PreviewFileMediaDialog} from "../../MythicComponents/PreviewFileMedia"; import {faPhotoVideo} from '@fortawesome/free-solid-svg-icons'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; diff --git a/MythicReactUI/src/components/pages/Callbacks/ResponseDisplayMedia.js b/MythicReactUI/src/components/pages/Callbacks/ResponseDisplayMedia.js index 7128351e..85b971d8 100644 --- a/MythicReactUI/src/components/pages/Callbacks/ResponseDisplayMedia.js +++ b/MythicReactUI/src/components/pages/Callbacks/ResponseDisplayMedia.js @@ -63,6 +63,8 @@ import {Backdrop, CircularProgress} from '@mui/material'; // eslint-disable-next-line import/no-webpack-loader-syntax import sqlWasm from "!!file-loader?name=sql-wasm-[contenthash].wasm!sql.js/dist/sql-wasm.wasm"; import {copyStringToClipboard} from "../../utilities/Clipboard"; +import {FileDownloadButtonWithAuth} from "../../utilities/FileDownloadWithAuth"; +import {ImageWithAuth} from "../../utilities/ImageWithAuth"; export const modeOptions = ["csharp", "golang", "html", "json", "markdown", "ruby", "python", "java", "javascript", "yaml", "toml", "swift", "sql", "rust", "powershell", "pgsql", "perl", "php", "objectivec", @@ -149,10 +151,10 @@ export const ResponseDisplayMedia = ({media, expand, task}) =>{ - + @@ -343,9 +345,9 @@ export const DisplayMedia = ({agent_file_id, filename, expand, task, fileMetaDat if(fileData.display_type === "image"){ return ( <> - - + {openScreenshot && { @@ -455,7 +457,11 @@ const DisplayText = ({agent_file_id, expand, filename, preview, fileMetaData}) = previewFileString({variables: {file_id: agent_file_id}}) }else{ // get entire file - fetch('/direct/view/' + agent_file_id).then((response) => { + fetch('/direct/view/' + agent_file_id, { + headers: { + Authorization: `Bearer ${localStorage.getItem('access_token')}`, + } + }).then((response) => { if(response.status !== 200){ snackActions.warning("Failed to fetch contents from Mythic"); return; @@ -762,7 +768,11 @@ const DisplayDatabase = ({agent_file_id, expand, fileMetaData}) => { React.useEffect( () => { async function initialize(){ const newSQL = await initSQLJS({locateFile: () => sqlWasm}); - fetch('/direct/view/' + agent_file_id).then((response) => { + fetch('/direct/view/' + agent_file_id, { + headers: { + Authorization: `Bearer ${localStorage.getItem('access_token')}` + } + }).then((response) => { if(response.status !== 200){ setLoading(false); snackActions.warning("Failed to fetch contents from Mythic"); diff --git a/MythicReactUI/src/components/pages/Callbacks/ResponseDisplayScreenshotModal.js b/MythicReactUI/src/components/pages/Callbacks/ResponseDisplayScreenshotModal.js index 15e35190..6c9d445e 100644 --- a/MythicReactUI/src/components/pages/Callbacks/ResponseDisplayScreenshotModal.js +++ b/MythicReactUI/src/components/pages/Callbacks/ResponseDisplayScreenshotModal.js @@ -6,6 +6,7 @@ import { MobileStepper } from '@mui/material'; import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight'; import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft'; import {useTheme} from '@mui/material/styles'; +import {ImageWithAuth} from "../../utilities/ImageWithAuth"; export function ResponseDisplayScreenshotModal({onClose, images, startIndex}) { const [zoom, setZoom] = React.useState(false); @@ -25,7 +26,7 @@ export function ResponseDisplayScreenshotModal({onClose, images, startIndex}) { < >
-
diff --git a/MythicReactUI/src/components/pages/Eventing/EventFileManageDialog.js b/MythicReactUI/src/components/pages/Eventing/EventFileManageDialog.js index d1399669..12ee6d97 100644 --- a/MythicReactUI/src/components/pages/Eventing/EventFileManageDialog.js +++ b/MythicReactUI/src/components/pages/Eventing/EventFileManageDialog.js @@ -15,7 +15,7 @@ import {b64DecodeUnicode} from "../Callbacks/ResponseDisplay"; import { updateFileDeleted} from "../Search/FileMetaTable"; import {MythicStyledTooltip} from "../../MythicComponents/MythicStyledTooltip"; import {MythicDialog} from "../../MythicComponents/MythicDialog"; -import {PreviewFileMediaDialog} from "../Search/PreviewFileMedia"; +import {PreviewFileMediaDialog} from "../../MythicComponents/PreviewFileMedia"; import MythicStyledTableCell from "../../MythicComponents/MythicTableCell"; import {faPhotoVideo} from '@fortawesome/free-solid-svg-icons'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; diff --git a/MythicReactUI/src/components/pages/Home/CallbacksCard.js b/MythicReactUI/src/components/pages/Home/CallbacksCard.js index 75759cb2..8fb407e3 100644 --- a/MythicReactUI/src/components/pages/Home/CallbacksCard.js +++ b/MythicReactUI/src/components/pages/Home/CallbacksCard.js @@ -43,7 +43,7 @@ import FormatListBulletedAddIcon from '@mui/icons-material/FormatListBulletedAdd import AddchartIcon from '@mui/icons-material/Addchart'; import PlaylistRemoveIcon from '@mui/icons-material/PlaylistRemove'; import {MythicSelectFromListDialog} from "../../MythicComponents/MythicSelectFromListDialog"; -import {PreviewFileMediaDialog} from "../Search/PreviewFileMedia"; +import {PreviewFileMediaDialog} from "../../MythicComponents/PreviewFileMedia"; import {faPhotoVideo} from '@fortawesome/free-solid-svg-icons'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {Link} from '@mui/material'; @@ -57,6 +57,7 @@ import {copyStringToClipboard} from "../../utilities/Clipboard"; import MythicStyledTableCell from "../../MythicComponents/MythicTableCell"; import {faCopy} from '@fortawesome/free-solid-svg-icons'; import MythicTextField from "../../MythicComponents/MythicTextField"; +import {ImageWithAuth} from "../../utilities/ImageWithAuth"; import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader"; const LeadDashboardQuery = gql` @@ -1534,9 +1535,10 @@ const Top10RecentScreenshotsDashboardElement = ({me, data, editing, removeElemen - onPreviewMedia(e, activeStep)} - src={"/api/v1.4/files/screencaptures/" + files[activeStep] + "?" + now} - style={{height: "200px", cursor: "pointer"}}/> + onPreviewMedia(e, activeStep)} + /> + ); +} \ No newline at end of file diff --git a/MythicReactUI/src/components/utilities/ImageWithAuth.js b/MythicReactUI/src/components/utilities/ImageWithAuth.js new file mode 100644 index 00000000..c374bf23 --- /dev/null +++ b/MythicReactUI/src/components/utilities/ImageWithAuth.js @@ -0,0 +1,31 @@ +import React from 'react'; + +export const ImageWithAuth = ({ src, ...props }) => { + const [imageSrc, setImageSrc] = React.useState(''); + const headers = { + Authorization: `Bearer ${localStorage.getItem('access_token')}`, + MythicSource: "web" + } + React.useEffect(() => { + const fetchImage = async () => { + try { + const response = await fetch(src, { headers }); + const blob = await response.blob(); + const objectUrl = URL.createObjectURL(blob); + setImageSrc(objectUrl); + } catch (error) { + console.error('Error fetching image:', error); + // Handle error (e.g., set a fallback image) + } + }; + fetchImage(); + // Cleanup: revoke the object URL when the component unmounts or src changes + return () => { + if (imageSrc) { + URL.revokeObjectURL(imageSrc); + } + }; + }, [src]); + + return ; +}; \ No newline at end of file diff --git a/mythic-docker/src/authentication/jwt.go b/mythic-docker/src/authentication/jwt.go index f333c4b8..6fce92e2 100644 --- a/mythic-docker/src/authentication/jwt.go +++ b/mythic-docker/src/authentication/jwt.go @@ -20,7 +20,6 @@ var ( ErrBearerInvalidValue = errors.New("Authorization bearer value is invalid") ErrMissingAuthorizationBearerToken = errors.New("Missing Authorization Bearer token") ErrMissingAuthorizationHeader = errors.New("Missing Authorization header") - ErrMissingCookieValue = errors.New("Missing cookie value") ErrMissingJWTToken = errors.New("Missing JWT header") ) @@ -245,42 +244,3 @@ func ExtractAPIToken(c *gin.Context) (string, error) { //logging.LogTrace("got apitoken header", "apitoken", token) return token, nil } - -func CookieTokenValid(c *gin.Context) error { - tokenString, err := ExtractCookieToken(c) - if err != nil { - return err - } - claims := mythicjwt.CustomClaims{} - _, err = jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - logging.LogError(ErrUnexpectedSigningMethod, "signing method", token.Header["alg"]) - return nil, fmt.Errorf("%w: %v", ErrUnexpectedSigningMethod, token.Header["alg"]) - } - return utils.MythicConfig.JWTSecret, nil - }) - if err != nil { - logging.LogError(err, "Failed to parse JWT with claims", "JWT", tokenString) - return err - } - c.Set("user_id", claims.UserID) - operator, err := database.GetUserFromID(claims.UserID) - if err == nil { - c.Set("username", operator.Username) - } - c.Request.Header.Add("Authorization", fmt.Sprintf("Bearer: %s", tokenString)) - c.Request.Header.Add("MythicSource", "cookie") - return nil -} - -func ExtractCookieToken(c *gin.Context) (string, error) { - token, err := c.Cookie("mythic") - if err != nil { - return "", ErrMissingCookieValue - } - if len(token) > 0 { - return token, nil - } - logging.LogDebug("Failed to find cookie value") - return "", ErrMissingCookieValue -} diff --git a/mythic-docker/src/authentication/middleware.go b/mythic-docker/src/authentication/middleware.go index 52874166..aea8e52c 100644 --- a/mythic-docker/src/authentication/middleware.go +++ b/mythic-docker/src/authentication/middleware.go @@ -2,10 +2,11 @@ package authentication import ( "fmt" - "github.com/its-a-feature/Mythic/rabbitmq" "net" "net/http" + "github.com/its-a-feature/Mythic/rabbitmq" + "github.com/gin-gonic/gin" "github.com/its-a-feature/Mythic/database" "github.com/its-a-feature/Mythic/logging" @@ -25,19 +26,6 @@ func JwtAuthMiddleware() gin.HandlerFunc { } } -func CookieAuthMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - err := CookieTokenValid(c) - if err != nil { - c.JSON(http.StatusUnauthorized, gin.H{"message": "Unauthorized"}) - c.Abort() - return - } - c.Request.Header.Add("MythicSource", "cookie") - c.Next() - } -} - func IPBlockMiddleware() gin.HandlerFunc { // verify that xforward-for address is in range from utils.Config.AllowedIPBlocks return func(c *gin.Context) { diff --git a/mythic-docker/src/authentication/mythicjwt/jwt.go b/mythic-docker/src/authentication/mythicjwt/jwt.go index 5b4d27bc..0b9ae9e4 100644 --- a/mythic-docker/src/authentication/mythicjwt/jwt.go +++ b/mythic-docker/src/authentication/mythicjwt/jwt.go @@ -4,14 +4,15 @@ import ( "crypto/rand" "errors" "fmt" - "github.com/golang-jwt/jwt" - databaseStructs "github.com/its-a-feature/Mythic/database/structs" - "github.com/its-a-feature/Mythic/logging" - "github.com/its-a-feature/Mythic/utils" "math/big" "strings" "sync" "time" + + "github.com/golang-jwt/jwt" + databaseStructs "github.com/its-a-feature/Mythic/database/structs" + "github.com/its-a-feature/Mythic/logging" + "github.com/its-a-feature/Mythic/utils" ) type CustomClaims struct { @@ -37,10 +38,10 @@ var ( ErrUnexpectedSigningMethod = errors.New("Unexpected signing method") ) -func generateRandomPassword(pw_length int) (string, error) { +func generateRandomPassword(passwordLength int) (string, error) { chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") var b strings.Builder - for i := 0; i < pw_length; i++ { + for i := 0; i < passwordLength; i++ { nBig, err := rand.Int(rand.Reader, big.NewInt(int64(len(chars)))) if err != nil { logging.LogError(err, "[-] Failed to generate random number for password generation\n") diff --git a/mythic-docker/src/rabbitmq/ContainerAuth.go b/mythic-docker/src/rabbitmq/ContainerAuth.go new file mode 100644 index 00000000..f9814a50 --- /dev/null +++ b/mythic-docker/src/rabbitmq/ContainerAuth.go @@ -0,0 +1,32 @@ +package rabbitmq + +import ( + "errors" + "sync" + + "github.com/its-a-feature/Mythic/utils" +) + +var containerAuthMap = make(map[string]string) +var containerAuthMapLock sync.RWMutex + +func NewContainerAuth(containerName string) string { + containerAuthMapLock.Lock() + defer containerAuthMapLock.Unlock() + if _, ok := containerAuthMap[containerName]; ok { + return containerAuthMap[containerName] + } + containerAuthMap[containerName] = utils.GenerateRandomPassword(36) + return containerAuthMap[containerName] +} + +func GetContainerFromAuth(password string) (string, error) { + containerAuthMapLock.RLock() + defer containerAuthMapLock.RUnlock() + for key, value := range containerAuthMap { + if value == password { + return key, nil + } + } + return "", errors.New("container not found") +} diff --git a/mythic-docker/src/rabbitmq/util_agent_message_actions_post_response.go b/mythic-docker/src/rabbitmq/util_agent_message_actions_post_response.go index 7dbd4da1..261271fd 100644 --- a/mythic-docker/src/rabbitmq/util_agent_message_actions_post_response.go +++ b/mythic-docker/src/rabbitmq/util_agent_message_actions_post_response.go @@ -306,13 +306,22 @@ func listenForAsyncAgentMessagePostResponseContent() { UpdateCachedResponseIntercept() for { msg := <-asyncAgentMessagePostResponseChannel + // force chunking user_output can be useful, but might also affect command's browserscripts + //chunkSize := 1024 * 1024 + //chunks := int(len(msg.Response)/chunkSize) + 1 // 1MB chunks + //for j := 0; j < chunks; j++ { + // currentChunkStart := j * chunkSize + // currentChunkEnd := currentChunkStart + chunkSize + // if currentChunkEnd > len(msg.Response) { + // currentChunkEnd = len(msg.Response) + // } + // chunkBuf := msg.Response[currentChunkStart:currentChunkEnd] responseInterceptMapLock.RLock() if eventGroupID, ok := responseInterceptMapOperationIDToEventGroupID[msg.Task.OperationID]; ok { output := "" responseID := handleAgentMessagePostResponseUserOutput(msg.Task, agentMessagePostResponse{ - TaskID: msg.Task.AgentTaskID, - UserOutput: &output, - SequenceNumber: msg.SequenceNum, + TaskID: msg.Task.AgentTaskID, + UserOutput: &output, }, false) if responseID > 0 { // we have an interception possibility, so send that off for processing @@ -327,12 +336,12 @@ func listenForAsyncAgentMessagePostResponseContent() { } } else { handleAgentMessagePostResponseUserOutput(msg.Task, agentMessagePostResponse{ - TaskID: msg.Task.AgentTaskID, - UserOutput: &msg.Response, - SequenceNumber: msg.SequenceNum, + TaskID: msg.Task.AgentTaskID, + UserOutput: &msg.Response, }, true) } responseInterceptMapLock.RUnlock() + //} } } func handleAgentMessagePostResponse(incoming *map[string]interface{}, uUIDInfo *cachedUUIDInfo) (map[string]interface{}, error) { @@ -473,9 +482,8 @@ func handleAgentMessagePostResponse(incoming *map[string]interface{}, uUIDInfo * // do it in the background - the agent doesn't need the result of this directly //handleAgentMessagePostResponseUserOutput(currentTask, agentResponse, true) asyncAgentMessagePostResponseChannel <- agentAgentMessagePostResponseChannelMessage{ - Task: currentTask, - Response: *agentMessage.Responses[i].UserOutput, - SequenceNum: agentMessage.Responses[i].SequenceNumber, + Task: currentTask, + Response: *agentMessage.Responses[i].UserOutput, } } if agentMessage.Responses[i].Stdout != nil { diff --git a/mythic-docker/src/webserver/controllers/file_direct_download.go b/mythic-docker/src/webserver/controllers/file_direct_download.go index 2bf49f8b..4a54b5b1 100644 --- a/mythic-docker/src/webserver/controllers/file_direct_download.go +++ b/mythic-docker/src/webserver/controllers/file_direct_download.go @@ -24,7 +24,7 @@ func FileDirectDownloadWebhook(c *gin.Context) { filemeta := databaseStructs.Filemeta{} payload := databaseStructs.Payload{} operatorUsername := "unknown" - userID, err := GetUserIDFromGinAllowCookies(c) + userID, err := GetUserIDFromGin(c) if err == nil { user, err := database.GetUserFromID(userID) if err == nil { diff --git a/mythic-docker/src/webserver/controllers/file_direct_view.go b/mythic-docker/src/webserver/controllers/file_direct_view.go index d6f48f2e..12148713 100644 --- a/mythic-docker/src/webserver/controllers/file_direct_view.go +++ b/mythic-docker/src/webserver/controllers/file_direct_view.go @@ -24,7 +24,7 @@ func FileDirectViewWebhook(c *gin.Context) { filemeta := databaseStructs.Filemeta{} payload := databaseStructs.Payload{} operatorUsername := "unknown" - userID, err := GetUserIDFromGinAllowCookies(c) + userID, err := GetUserIDFromGin(c) if err == nil { user, err := database.GetUserFromID(userID) if err == nil { diff --git a/mythic-docker/src/webserver/controllers/login.go b/mythic-docker/src/webserver/controllers/login.go index 4ea7276b..ce410238 100644 --- a/mythic-docker/src/webserver/controllers/login.go +++ b/mythic-docker/src/webserver/controllers/login.go @@ -1,11 +1,11 @@ package webcontroller import ( - "github.com/its-a-feature/Mythic/authentication/mythicjwt" "net/http" - "strings" "time" + "github.com/its-a-feature/Mythic/authentication/mythicjwt" + "github.com/gin-gonic/gin" "github.com/its-a-feature/Mythic/authentication" "github.com/its-a-feature/Mythic/database" @@ -49,9 +49,6 @@ func Login(c *gin.Context) { "view_utc_time": currentOperation.CurrentOperator.ViewUtcTime, "current_utc_time": time.Now().UTC(), } - // setting cookie max age to 2 days - c.SetCookie("mythic", accessToken, 60*60*24*2, "/", strings.Split(c.Request.Host, ":")[0], false, true) - c.SetSameSite(http.SameSiteStrictMode) c.JSON(http.StatusOK, gin.H{"access_token": accessToken, "refresh_token": refreshToken, "user": user}) return @@ -132,8 +129,6 @@ func RefreshJWT(c *gin.Context) { // setting cookie max age to 2 days c.Set("user_id", currentOperation.CurrentOperator.ID) c.Set("username", currentOperation.CurrentOperator.Username) - c.SetCookie("mythic", accessToken, 60*60*24*2, "/", strings.Split(c.Request.Host, ":")[0], false, true) - c.SetSameSite(http.SameSiteStrictMode) c.JSON(http.StatusOK, gin.H{"access_token": accessToken, "refresh_token": refreshToken, "user": user}) return diff --git a/mythic-docker/src/webserver/controllers/utils.go b/mythic-docker/src/webserver/controllers/utils.go index 039d8a8b..775b9b4b 100644 --- a/mythic-docker/src/webserver/controllers/utils.go +++ b/mythic-docker/src/webserver/controllers/utils.go @@ -22,17 +22,6 @@ func GetUserIDFromGin(c *gin.Context) (int, error) { } return customClaims.UserID, nil } -func GetUserIDFromGinAllowCookies(c *gin.Context) (int, error) { - customClaims, err := authentication.GetClaims(c) - if err != nil { - err = authentication.CookieTokenValid(c) - if err != nil { - return 0, err - } - return GetUserIDFromGin(c) - } - return customClaims.UserID, nil -} const ( tagTypePreview = "FilePreviewed" diff --git a/mythic-docker/src/webserver/initialize.go b/mythic-docker/src/webserver/initialize.go index 3da70829..9677eb97 100644 --- a/mythic-docker/src/webserver/initialize.go +++ b/mythic-docker/src/webserver/initialize.go @@ -147,10 +147,10 @@ func setRoutes(r *gin.Engine) { r.POST("/agent_message", webcontroller.AgentMessageWebhook) r.GET("/agent_message", webcontroller.AgentMessageGetWebhook) // healthcheck endpoint - r.GET("/health", webcontroller.HealthCheckSimple) - r.GET("/healthDetailed", webcontroller.HealthCheckDetailed) r.Use(authentication.IPBlockMiddleware()) { + r.GET("/health", webcontroller.HealthCheckSimple) + r.GET("/healthDetailed", webcontroller.HealthCheckDetailed) // login page r.POST("/auth", webcontroller.Login) r.POST("/invite", webcontroller.UseInviteLink) @@ -169,24 +169,7 @@ func setRoutes(r *gin.Engine) { // unauthenticated file upload based on file UUID // this is for payload containers to upload files that are too big for rabbitmq r.POST("/direct/upload/:file_uuid", webcontroller.FileDirectUploadWebhook) - - // create a protected group that allows Cookie values to access things instead of only a JWT header field - // this mainly allows the user through the UI to view agent icons and download files - allowAuthenticatedCookies := r.Group("/") - { - // allow access to getting images for agent icons, still blocked by IP range - allowAuthenticatedCookies.Use(authentication.CookieAuthMiddleware()) - { - allowAuthenticatedCookies.Static("/static", "./static") - allowAuthenticatedCookies.GET("direct/view/:file_uuid", webcontroller.FileDirectViewWebhook) - allOperationMembersWithCookies := allowAuthenticatedCookies.Group("/api/v1.4/") - allOperationMembersWithCookies.Use(authentication.RBACMiddlewareAll()) - { - allOperationMembersWithCookies.GET("files/download/:file_uuid", webcontroller.DownloadFileAuthWebhook) - allOperationMembersWithCookies.GET("files/screencaptures/:file_uuid", webcontroller.DownloadFileAuthWebhook) - } - } - } + // create a protected group that requires valid auth with a JWT to access protected := r.Group("/") protected.Use(authentication.JwtAuthMiddleware()) @@ -213,6 +196,8 @@ func setRoutes(r *gin.Engine) { protected.POST("/api/v1.4/get_global_settings_webhook", webcontroller.GetGlobalSettingWebhook) // a refresh post will contain the access_token and refresh_token protected.POST("/refresh", webcontroller.RefreshJWT) + protected.Static("/static", "./static") + protected.GET("direct/view/:file_uuid", webcontroller.FileDirectViewWebhook) // following require you to have an operation set allOperationMembers := protected.Group("/api/v1.4/") allOperationMembers.Use(authentication.RBACMiddlewareAll()) @@ -231,6 +216,7 @@ func setRoutes(r *gin.Engine) { // file allOperationMembers.POST("download_bulk_webhook", webcontroller.DownloadBulkFilesWebhook) allOperationMembers.POST("preview_file_webhook", webcontroller.PreviewFileWebhook) + allOperationMembers.GET("files/screencaptures/:file_uuid", webcontroller.DownloadFileAuthWebhook) } noSpectators := protected.Group("/api/v1.4/") noSpectators.Use(authentication.RBACMiddlewareNoSpectators())