Medusa Agent for Mythic C2

repository·main·Indexed 42 days ago

https://github.com/mythicagents/medusa

A cross-platform Python agent for the Mythic C2 framework, compatible with Python 2.7 and 3.8. Medusa features dynamic capability loading, in-memory module execution, and threaded job management. It supports HTTP and Azure Blob storage C2 profiles, SOCKS5 proxying, and platform-specific tools for macOS and Windows.

Tokens
19.7K
Snippets
67
Records
100
Agent score
52%

What's inside Medusa

  1. Overview of Medusa Agent Features

    main

    Medusa is a cross-platform Python agent compatible with Python 2.7 and 3.8. It is designed for flexibility and OPSEC, featuring:

    • Dynamic Functionality: Supports loading and unloading functionality to minimize the agent's on-disk footprint.
    • SOCKS5 Proxy Support: Compatible across Python 2.7/3.8 and macOS, Windows, and Linux.
    • Encrypted Communications: Supports various encryption methods to secure C2 traffic.
    • Dynamic Extension: Uses Eval() to execute Python code, allowing for runtime functionality extensions.
    • Threaded Execution: Each job runs in a new thread. You can manage these using the jobs and jobkill commands.
  2. How load_module works internally

    main

    When load_module is executed, it performs the following steps:

    1. Chunked Download: It retrieves the module file in chunks via post_response actions, reconstructing the zip file in memory.
    2. In-Memory Repository: The reconstructed zip file is stored in self.moduleRepo under the provided module_name.
    3. Custom Finder Injection: A custom finder (CFinder) is created and appended to sys.meta_path.
    4. In-Memory Import: When a script later calls import <module_name>, the custom finder intercepts the request, reads the source from the in-memory zip, compiles it, and loads it into sys.modules.

    This mechanism bypasses the standard filesystem search, allowing for stealthy or transient module loading.

  3. Obfuscate agent payloads using XOR+Base64

    main

    To avoid producing easily detectable or readable scripts, you can use the build parameters to enable XOR+Base64 obfuscation.

    When this option is enabled, Mythic bundles the agent code and applies XOR encryption with a random key, followed by Base64 encoding. The agent is then decrypted, decoded, and executed via exec() at runtime. This prevents the payload from having a trivial signature that is easily identified by security tools.

  4. How clipboard data retrieval works

    main

    On macOS, the clipboard command utilizes Objective C API calls to access the NSPasteboard.generalPasteboard. Because the macOS clipboard can contain various data types beyond simple text, the command collects all available types and returns them as a JSON object where each key is the data type and the value is the content encoded in Base64.

    If plaintext data is present, it is included in the returned keys. For non-text data, the command retrieves the data for that specific type and encodes it using base64EncodedStringWithOptions(0).

    let pb = $.NSPasteboard.generalPasteboard;
    let types = pb.types.js;
    let clipboard = {};
    for(let i = 0; i < types.length; i++){
        let typejs = types[i].js;
        clipboard[typejs] = pb.dataForType(types[i]);
        if(clipboard[typejs].js !== undefined){
            clipboard[typejs] = clipboard[typejs].base64EncodedStringWithOptions(0).js;
        }else{
            clipboard[typejs] = "";
        }
    }
  5. Implement long-running threaded jobs in Medusa

    main

    Medusa uses threading for job execution. To ensure long-running tasks (like download or upload) can be stopped via jobkill, implement a 'stop check' within your command logic. You can also use self.sendTaskOutputUpdate(task_id, output) to provide continuous updates to the Mythic UI before the task completes.

    # Example of a job that supports jobkill and continuous updates
    def dummyFunction(self, task_id):
        while(True):
            # Check if we've received a stop signal from jobkill
            if [task for task in self.taskings if task["task_id"] == task_id][0]["stopped":
                return "Job stopped."
    
            # Send continuous output back to Mythic
            self.sendTaskOutputUpdate(task_id, "We're still running")
    
            time.sleep(10)
  6. Modifying Base Agent Behavior and Transports

    main

    Medusa uses a core + transport model for agent construction. The payload builder stitches these together by replacing section markers in the core template with content from a transport template.

    Core Logic

    Shared runtime logic (task loop, processing, response handling, etc.) is located in:

    • Payload_Type/medusa/medusa/agent_code/base_agent/base_agent_core.py2
    • Payload_Type/medusa/medusa/agent_code/base_agent/base_agent_core.py3

    Transport Logic

    Networking and configuration specific to a protocol (e.g., HTTP, Azure Blob) are located in transport files. To modify how a specific transport behaves, update the corresponding .py2 and .py3 files in the transport directory.

    Section Markers

    The builder looks for these specific markers in the templates to perform the stitch:

    • TRANSPORT_IMPORTS
    • TRANSPORT_CLASS_FIELDS
    • TRANSPORT_FUNCTIONS
    • TRANSPORT_CONFIG
  7. How the upload command works

    main

    The upload command operates by chunking the file and transferring it from Mythic to the agent via API calls.

    1. The agent requests chunks of the file from the Mythic server.
    2. The agent receives data chunks, decodes them from base64, and writes them sequentially to the specified remote_path on disk.
    3. Once the transfer is complete, the agent reports the final full path back to the Mythic UI for tracking.
  8. Azure Blob build-time configuration process

    main

    When the azure_blob profile is selected during the Medusa build process, the following steps occur:

    1. Medusa calls the generate_config RPC on the azure_blob service to request configuration.
    2. The service returns scoped configuration values.
    3. Medusa embeds the following values into the final payload using the core+transport template assembly process:
      • blob endpoint
      • container name
      • scoped SAS token
  9. How directory structures are preserved in archive mode

    main

    When using mode: "archive", the internal structure of the ZIP file depends on how the input paths are provided:

    • Directory input (e.g., /etc/nginx): Entries are anchored at the parent directory. For example, /etc/nginx/nginx.conf becomes nginx/nginx.conf inside the ZIP.
    • Single file input (e.g., /etc/passwd): Only the filename is stored (e.g., passwd).
    • Explicit list input (e.g., providing ['/etc/passwd', '/home/user/report.txt']): Entries are anchored at the filesystem root, preserving the full path (e.g., etc/passwd, home/user/report.txt).