Mythic Documentation
Version 2.3
Version 2.3
  • Mythic
  • Operators
  • Installation
    • Connecting
    • A note about containers
    • Offline Installation
    • Updating Mythic
  • Internal Documentation
  • Quick Usage
  • Operational Pieces
    • MITRE ATT&CK
    • Operations
    • Browser Scripts
    • Active Callbacks
    • Files
    • Search
    • File Browser
    • Socks Proxy
    • Credentials
    • Comments
    • Tags
    • Expanded Callbacks
    • Screenshots
    • Event Feed
    • API Tokens
  • Message Flow
    • Building Payloads
    • Agent Sends Message
    • File Upload Mythic->Agent
    • File Download Agent->Mythic
    • P2P Messages
    • Operator Submits Tasking
  • Database Schema
  • Understanding Commands
    • Basic Information
    • Parameters
    • MITRE ATT&CK in Commands
  • Payload Types
    • Configuration
    • Containers
  • C2 Profiles
    • C2 Server Utilities
    • Egress vs P2P
    • HTTP
    • dynamicHTTP
    • Save Parameters
  • Reporting
    • Artifacts
    • MITRE ATT&CK
    • Reports
  • Scripting
  • Presentations / Webinars
  • Common Errors
  • Customizing
    • Hooking Features
      • Actions
      • Linking Agents
      • P2P Connections
      • Process_List
      • Artifacts
      • Credentials
      • File Downloads (Agent -> Mythic)
      • File Uploads (Mythic -> Agent)
      • Screenshots
      • Commands
      • Keylog
      • File Browser
      • Tokens
      • Task Status
    • Payload Type Development
      • Translation Containers
      • First Steps
      • Container Syncing
      • Payload Type Info
      • Commands
      • Browser Scripting
      • Dynamic Parameter Values
      • Create_Tasking
      • MythicRPC
      • OPSEC Checking
      • Sub-tasking / Task Callbacks
      • Process Response
      • SOCKS
      • Reverse PortFwd
      • Adding Commands
    • C2 Related Development
      • C2 Profile Code
        • Agent Side Coding
          • Delegates (p2p)
          • Agent Message Format
          • Action: Checkin
          • Action: get_tasking
          • Action: post_response
          • SOCKS
          • RPFWD
        • Server Side Coding
          • OPSEC Checks For C2 Profiles
          • Configuration Checks
          • Redirect Rules
          • C2 Docker Containers
          • C2 Configuration Files
    • Mythic UI Development
  • Common Questions and Answers
    • FAQ / Troubleshooting Tips
    • Change Log
    • Next Release
    • Tip of the Week
  • Updating
    • Mythic 2.1 -> 2.2 Updates
      • Agents 2.1.* -> 2.2.8
        • MythicRPC
    • Mythic 2.2 -> 2.3 Updates
      • Agents 2.2 -> 2.3
    • Mythic 2.3 -> 3.0 Updates
      • Agents 2.3 -> 3.0
Powered by GitBook
On this page
  • Translation Containers
  • Python
  • GoLang
  • Examples:
  • Turning a VM into a Translation Container

Was this helpful?

Export as PDF
  1. Customizing
  2. Payload Type Development

Translation Containers

Translation Containers

If you want to have a different form of communication between Mythic and your agent than the specific JSON messages that Mythic uses, then you'll need a "translation container".

The first thing you'll need to do is specify the name of the container in your associated Payload Type class code. Update the Payload Type's class to include a line like translation_container = "binaryTranslator" . Now we need to create the container.

The process for making a translation container is almost identical to a c2 profile or payload type container, we're simply going to change which classes we instantiate, but the rest of it is the same.

Unlike Payload Type and C2 Profile containers that mainly do everything over RabbitMQ for potentially long-running queues of jobs, Translation containers use gRPC for fast responses.

If a translation_container is specified for your Payload Type, then the three functions defined in the following two examples will be called as Mythic processes requests from your agent.

You then need to get the new container associated with the docker-compose file that Mythic uses, so run sudo ./mythic-cli add binaryTranslator. Now you can start the container with sudo ./mythic-cli start binaryTranslator and you should see the container pop up as a sub heading of your payload container.

Additionally, if you're leveraging a payload type that has mythic_encrypts = False and you're doing any cryptography, then you should use this same process and perform your encryption and decryption routines here. This is why Mythic provides you with the associated keys you generated for encryption, decryption, and which profile you're getting a message from.

Python

For the Python version, we simply instantiate our own subclass of the TranslationContainer class and provide three functions. In our main.py file, simply import the file with this definition and then start the service:

mythic_container.mythic_service.start_and_run_forever()
from mythic_container.TranslationBase import *


class myPythonTranslation(TranslationContainer):
    name = "binaryTranslator"
    description = "python translation service that doesn't change anything"
    author = "@its_a_feature_"

    async def generate_keys(self, inputMsg: TrGenerateEncryptionKeysMessage) -> TrGenerateEncryptionKeysMessageResponse:
        response = TrGenerateEncryptionKeysMessageResponse(Success=True)
        response.DecryptionKey = b""
        response.EncryptionKey = b""
        return response

    async def translate_to_c2_format(self, inputMsg: TrMythicC2ToCustomMessageFormatMessage) -> TrMythicC2ToCustomMessageFormatMessageResponse:
        response = TrMythicC2ToCustomMessageFormatMessageResponse(Success=True)
        response.Message = inputMsg.Message
        return response

    async def translate_from_c2_format(self, inputMsg: TrCustomMessageToMythicC2FormatMessage) -> TrCustomMessageToMythicC2FormatMessageResponse:
        response = TrCustomMessageToMythicC2FormatMessageResponse(Success=True)
        response.Message = inputMsg.Message
        return response

GoLang

For the GoLang side of things, we instantiate an instance of the translationstructs.TranslationContainer struct with our same three functions. For GoLang though, we have an Initialize function to add this struct as a new definition to track.

import (
   "crypto/rand"
   "encoding/json"
   "errors"
   "github.com/MythicMeta/MythicContainer/logging"
   "github.com/MythicMeta/MythicContainer/translationstructs"
)

var myTranslationService = translationstructs.TranslationContainer{
   Name:        "myTranslationService",
   Description: "My test translation service that doesn't actually translate anything",
   Author:      "@its_a_feature_",
   GenerateEncryptionKeys: func(input translationstructs.TrGenerateEncryptionKeysMessage) translationstructs.TrGenerateEncryptionKeysMessageResponse {
      response := translationstructs.TrGenerateEncryptionKeysMessageResponse{
         Success: false,
      }
      if keys, err := GenerateKeysForPayload(input.CryptoParamValue); err != nil {
         logging.LogError(err, "Failed to determine valid crypto type")
         response.Error = err.Error()
         return response
      } else {
         response.Success = true
         response.EncryptionKey = keys.EncKey
         response.DecryptionKey = keys.DecKey
         return response
      }
   },
   TranslateMythicToCustomFormat: func(input translationstructs.TrMythicC2ToCustomMessageFormatMessage) translationstructs.TrMythicC2ToCustomMessageFormatMessageResponse {
      response := translationstructs.TrMythicC2ToCustomMessageFormatMessageResponse{}
      if input.MythicEncrypts {
         // mythic will take our resulting bytes and encrypt them, so just convert to bytes and return
         if jsonBytes, err := json.Marshal(input.Message); err != nil {
            response.Success = false
            response.Error = err.Error()
         } else {
            response.Success = true
            response.Message = jsonBytes
         }
      } else {

      }
      return response
   },
   TranslateCustomToMythicFormat: func(input translationstructs.TrCustomMessageToMythicC2FormatMessage) translationstructs.TrCustomMessageToMythicC2FormatMessageResponse {
      response := translationstructs.TrCustomMessageToMythicC2FormatMessageResponse{}
      outputMap := map[string]interface{}{}
      if input.MythicEncrypts {
         // mythic already decrypted these bytes, so just convert to map and return
         if err := json.Unmarshal(input.Message, &outputMap); err != nil {
            response.Success = false
            response.Error = err.Error()
         } else {
            response.Success = true
            response.Message = outputMap
         }
      } else {
         // we're expected to decrypt the bytes first, then convert them
      }

      return response
   },
}

func GenerateKeysForPayload(cryptoType string) (translationstructs.CryptoKeys, error) {
   switch cryptoType {
   case "aes256_hmac":
      bytes := make([]byte, 32)
      if _, err := rand.Read(bytes); err != nil {
         logging.LogError(err, "Failed to generate new random 32 bytes for aes256 key")
         return translationstructs.CryptoKeys{
            EncKey: nil,
            DecKey: nil,
            Value:  cryptoType,
         }, err
      }
      return translationstructs.CryptoKeys{
         EncKey: &bytes,
         DecKey: &bytes,
         Value:  cryptoType,
      }, nil
   case "none":
      return translationstructs.CryptoKeys{
         EncKey: nil,
         DecKey: nil,
         Value:  cryptoType,
      }, nil
   default:
      return translationstructs.CryptoKeys{
         EncKey: nil,
         DecKey: nil,
         Value:  cryptoType,
      }, errors.New("Unknown crypto type")
   }
}

func Initialize() {
   translationstructs.AllTranslationData.Get("myTranslationService").AddPayloadDefinition(myTranslationService)
}

Then, in our main.go code, we call the Initialize function and start the services:

mytranslatorfunctions.Initialize()
// sync over definitions and listen
MythicContainer.StartAndRunForever([]MythicContainer.MythicServices{
   MythicContainer.MythicServiceTranslationContainer,
})

Examples:

These examples can be found at the MythicMeta organization on GitHub:

Docker doesn't allow you to have capital letters in your image names, and when Mythic builds these containers, it uses the container's name as part of the image name. So, you can't have capital letters in your agent/translation container names. That's why you'll see things like service_wrapper instead of serviceWrapper

Turning a VM into a Translation Container

PreviousPayload Type DevelopmentNextFirst Steps

Last updated 1 year ago

Was this helpful?

Just like with Payload Types, a Translation container doesn't have to be a Dockerized instance. To turn any VM into a translation container just follow the general flow at

ExampleContainers/Payload_Type at main · MythicMeta/ExampleContainersGitHub
Turning a VM into a Mythic container
Logo