14 KiB
Cazalla - Mythic C2 Agent
📋 Table of Contents
- Overview
- Features
- Installation
- Supported Commands
- SOCKS Proxy Support
- Communication Protocol
- Configuration
- Development
- Troubleshooting
- Credits
🎯 Overview
Cazalla is a lightweight Windows implant written in C, designed to work seamlessly with the Mythic C2 Framework. It provides essential post-exploitation capabilities including file system manipulation, process enumeration, command execution, and SOCKS proxy functionality.
This project was inspired by the article How to build your own Mythic agent in C and has been extended with additional features and improvements.
Developed by: Kaseya OFSTeam
✨ Features
- Lightweight & Portable: Minimal dependencies, compiled with MinGW for Windows targets
- Full Mythic Integration: Native support for Mythic's translator pattern and RPC system
- SOCKS Proxy: Route traffic through the implant with built-in SOCKS5 support
- Dynamic Sleep: Automatically adjusts beacon interval based on proxy activity
- Custom Binary Protocol: Efficient binary communication with base64 encoding
- Modular Commands: Easy to extend with new capabilities
📦 Installation
Prerequisites
- Mythic Server (v3.x or later) installed and running
- Clone this repository
Install Cazalla on Mythic
cd ~/Mythic
./mythic-cli install github https://github.com/<your-org>/Cazalla
Or install from local directory:
./mythic-cli install folder /path/to/Cazalla
Build the Agent
Cazalla can be built directly from the Mythic UI:
- Navigate to Payloads → Create Payload
- Select Cazalla as the payload type
- Configure build parameters (C2 profile, sleep interval, etc.)
- Click Build
The agent will be cross-compiled for Windows using MinGW in a Docker container.
🛠️ Supported Commands
File System Operations
| Command | Description | Example |
|---|---|---|
ls |
List directory contents | ls C:\Users |
cd |
Change working directory | cd C:\Windows\System32 |
pwd |
Print current directory | pwd |
cp |
Copy files | cp source.txt dest.txt |
mkdir |
Create directory | mkdir C:\temp\new_folder |
rm |
Delete file or directory | rm C:\temp\file.txt |
Execution & Control
| Command | Description | Example |
|---|---|---|
shell |
Execute shell command | shell whoami |
sleep |
Adjust beacon interval | sleep {"seconds":30,"jitter":0} |
exit |
Terminate the implant | exit |
System Information
| Command | Description | Example |
|---|---|---|
ps |
List running processes | ps |
Network Operations
| Command | Description | Example |
|---|---|---|
socks |
Start/stop SOCKS proxy | socks {"action":"start","port":7002} |
🌐 SOCKS Proxy Support
Cazalla includes full SOCKS5 proxy support, allowing you to route traffic through the implant to reach internal network resources.
How It Works
- Mythic opens a SOCKS port on the server (default:
7002) - Client connects to Mythic's SOCKS port (e.g., via
proxychains) - Mythic forwards SOCKS traffic to the agent with a unique
server_idper connection - Agent establishes the actual connection to the target host
- Bidirectional relay between client ↔ Mythic ↔ Agent ↔ Target
Commands
# Start SOCKS proxy on port 7002
socks {"action":"start","port":7002}
# Stop SOCKS proxy
socks {"action":"stop","port":7002}
Using the Proxy
After starting SOCKS, configure your tools to use the proxy:
With proxychains
# Edit /etc/proxychains.conf
[ProxyList]
socks5 <mythic_server_ip> 7002
# Use with any tool
proxychains curl https://internal-server.local
proxychains nmap -sT 192.168.1.0/24
With curl
curl --socks5 <mythic_server_ip>:7002 https://internal-server.local
With Firefox/Browser
Configure SOCKS proxy in browser settings:
- SOCKS Host:
<mythic_server_ip> - Port:
7002 - SOCKS v5: Enabled
Mythic Configuration for SOCKS
To expose SOCKS ports externally (not just localhost), edit ~/Mythic/.env:
# Allow SOCKS ports to bind on 0.0.0.0 (all interfaces)
MYTHIC_SERVER_DYNAMIC_PORTS_BIND_LOCALHOST_ONLY="false"
# Define dynamic port range for SOCKS
MYTHIC_SERVER_DYNAMIC_PORTS="7000-7010"
# Optional: Use host networking for direct port exposure
MYTHIC_DOCKER_NETWORKING="host"
After editing, restart Mythic:
cd ~/Mythic
sudo ./mythic-cli restart
Verify the port is open:
sudo netstat -tlnp | grep 7002
# Should show: tcp 0.0.0.0:7002 0.0.0.0:* LISTEN
Performance Considerations
- Latency: Proxy speed depends on beacon interval. SOCKS mode automatically reduces sleep to 100ms for responsive connections.
- Throughput: Suitable for interactive sessions and moderate data transfer. Not optimized for large file transfers.
- Concurrent Connections: Supports multiple simultaneous SOCKS connections with independent
server_idtracking.
Technical Details
Binary Protocol (0xF5 SOCKS Block)
Agent and translator communicate SOCKS data using a custom binary block:
[0xF5] [ext_len:4] [server_id:4] [exit:4] [b64_len:4] [base64_data:N]
0xF5: SOCKS block markerext_len: Total extension length (remaining bytes)server_id: Unique connection ID from Mythicexit:1if connection should close,0otherwiseb64_len: Length of base64-encoded payloadbase64_data: Base64-encoded raw bytes
Agent Implementation
- File:
socks_manager.c/socks_manager.h - Commands:
0x60(START_SOCKS),0x61(STOP_SOCKS) - Thread Model: One reader thread per active connection
- Socket Timeout: 60 seconds for idle connections
📡 Communication Protocol
Cazalla uses a custom binary protocol for efficient communication with Mythic.
Message Structure
Agent → Mythic
[UUID:36] [Action:1] [Body:N]
- UUID: 36-byte agent identifier (ASCII)
- Action: 1-byte action code (e.g.,
0x80for GET_TASKING,0x81for POST_RESPONSE) - Body: Variable-length payload
Mythic → Agent
[Action:1] [Body:N]
- Action: 1-byte action code
- Body: Variable-length payload (tasks, SOCKS data, etc.)
Action Codes
| Code | Direction | Description |
|---|---|---|
0x80 |
Agent → Mythic | GET_TASKING (request new tasks) |
0x81 |
Agent → Mythic | POST_RESPONSE (send task output) |
0xF5 |
Bidirectional | SOCKS data block |
Task Structure
Tasks sent to the agent include:
[TaskUUID:36] [CommandID:1] [Parameters:N]
Example command IDs:
0x01: Shell command0x10: List directory0x60: Start SOCKS0x61: Stop SOCKS
Encoding
All binary data is base64-encoded for transport over HTTP/HTTPS.
⚙️ Configuration
Agent Build Parameters
When building from Mythic UI:
- Callback Host: C2 server address
- Callback Port: C2 server port (default: 443)
- User Agent: HTTP User-Agent string
- Sleep: Initial beacon interval in seconds (default: 10)
- Jitter: Random delay percentage (0-100)
Compile-Time Options
Edit Config.h before building:
#define INITIAL_SLEEP_MS 10000 // Default sleep: 10 seconds
#define SOCKS_ACTIVE_SLEEP_MS 100 // Sleep when SOCKS active: 100ms
#define SOCKS_TIMEOUT_SEC 60 // Socket read timeout
Debug Mode
Enable verbose logging by defining DEBUG_SOCKS during compilation:
CFLAGS="-DDEBUG_SOCKS" make
This enables:
[MAINDBG]: Main loop and sleep adjustments[CMDDBG]: Command parsing[SOCKSDBG]: Connection lifecycle[SLEEPDBG]: Dynamic sleep changes
🔧 Development
Project Structure
Cazalla/
├── agent_code/cazalla/ # C source code
│ ├── main.c # Entry point
│ ├── cazalla.c/h # Main loop
│ ├── comandos.c/h # Command handlers
│ ├── paquete.c/h # Packet creation
│ ├── transporte.c/h # HTTP transport (WinHTTP)
│ ├── socks_manager.c/h # SOCKS proxy logic
│ ├── checkin.c/h # Initial check-in
│ └── Makefile # Build configuration
├── agent_functions/ # Python command definitions
│ ├── ls.py, cd.py, pwd.py # File system commands
│ ├── shell.py, sleep.py # Execution control
│ ├── socks.py # SOCKS proxy command
│ └── builder.py # Payload builder
├── translator/ # Mythic ↔ Agent translation
│ ├── translator.py # Main translator
│ ├── commands_from_c2.py # Mythic JSON → Binary
│ └── commands_from_implant.py # Binary → Mythic JSON
└── browser_scripts/ # Mythic UI enhancements
├── ls.js # Directory listing formatter
└── ps.js # Process list formatter
Adding New Commands
- Define command in Python (
agent_functions/my_command.py):
from mythic_container.MythicCommandBase import *
class MyCommandArguments(TaskArguments):
def __init__(self, command_line, **kwargs):
super().__init__(command_line, **kwargs)
self.args = [
CommandParameter(name="arg1", type=ParameterType.String)
]
class MyCommandCommand(CommandBase):
cmd = "my_command"
needs_admin = False
help_cmd = "my_command <arg1>"
description = "Does something useful"
version = 1
argument_class = MyCommandArguments
async def create_go_tasking(self, taskData: PTTaskMessageAllData):
response = PTTaskCreateTaskingMessageResponse(
TaskID=taskData.Task.ID,
Success=True,
)
return response
async def process_response(self, task: PTTaskMessageAllData, response: any):
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
return resp
- Add to translator (
translator/commands_from_c2.py):
elif task_command == "my_command":
task_data.append(0x99) # Command ID
arg1 = task.get("parameters", {}).get("arg1", "")
task_data += len(arg1).to_bytes(4, 'big')
task_data += arg1.encode('utf-8')
- Implement handler in C (
comandos.c):
BOOL myCommandHandler(PAnalizador argumentos) {
SIZE_T tamanoUuid = 36;
PCHAR taskUuid = getString(argumentos, &tamanoUuid);
SIZE_T arg1Size = 0;
PCHAR arg1 = getString(argumentos, &arg1Size);
// Do something with arg1
// Send response
PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);
addString(respuesta, taskUuid, FALSE);
PackageAddFormatPrintf(respuesta, "Command executed: %s\n", arg1);
Analizador* resp = mandarPaquete(respuesta);
liberarPaquete(respuesta);
if (resp) liberarAnalizador(resp);
return TRUE;
}
- Register in command dispatcher (
comandos.c):
case 0x99: // MY_COMMAND
myCommandHandler(argumentos);
break;
🐛 Troubleshooting
Agent Won't Check In
- Verify C2 profile settings (host, port, endpoint)
- Check network connectivity from target to C2 server
- Review Mythic logs:
sudo docker logs mythic_server - Enable debug logging in agent and rebuild
SOCKS Proxy Not Working
Port not accessible:
# Check if port is open
sudo netstat -tlnp | grep 7002
# Ensure Mythic config allows external binding
grep DYNAMIC_PORTS_BIND_LOCALHOST_ONLY ~/Mythic/.env
# Should be "false" for external access
Proxy times out:
- Verify agent is active and not sleeping
- Check translator logs:
sudo docker logs cazalla_translator - Ensure
server_idvalidation insocks_manager.callows Mythic's IDs
Data not flowing:
- Enable
DEBUG_SOCKSand rebuild agent - Check for
0xF5block in agent logs - Verify translator is encoding/decoding SOCKS blocks correctly
Compilation Errors
inet_pton not found (MinGW):
- Use
inet_addrinstead (Windows-native) - Already fixed in current version
Missing headers:
# Install MinGW dependencies in Docker
apt-get update && apt-get install -y mingw-w64
Task Stays in "Processing" State
Commands like exit, socks start/stop:
- Ensure handler sends
POST_RESPONSEwith task UUID - Mark task as completed in
create_go_taskingorprocess_response - For
exit, mark completed immediately (agent terminates)
📚 References
- Mythic Documentation
- Building a Mythic Agent in C
- SOCKS5 Protocol (RFC 1928)
- Mythic Translator Pattern
📄 License
Cazalla is licensed under the BSD 3-Clause License. See LICENSE.md for details.
👥 Credits
Developed by: Kaseya OFSTeam
Special thanks to:
- The Mythic C2 team for the excellent framework
- The SNCF Red Team for the foundational C agent tutorial
- The open-source security community
Made with ☕ and 💻 by the Kaseya OFSTeam