Cazalla - Basic Mythic Implant in C
Cazalla is a basic Windows implant written in C, designed to interface with the Mythic C2 framework.
This project was developed based on the article How to build your own Mythic agent in C and serves as an initial version of an implant with room for future improvements.
Cazalla is developed by the OFSTeam at Kaseya.
Supported Commands
Commands are organized by category for better clarity and extensibility.
📁 File System Commands
| Command | Description | Status |
|---|---|---|
cd |
Change working directory | ✅ |
ls |
List directory contents | ✅ |
pwd |
Print working directory | ✅ |
cp |
Copy an existing file | ✅ |
mkdir |
Create a new directory | ✅ |
rm |
Delete file/directory | ✅ |
🖥️ Console Commands
| Command | Description | Status |
|---|---|---|
shell |
Execute arbitrary shell command | ✅ |
🔄 Execution Control
| Command | Description | Status |
|---|---|---|
exit |
Exit the implant | ✅ |
sleep |
Modify sleep time | ✅ |
🧠 System Inspection
| Command | Description | Status |
|---|---|---|
ps |
List running processes | ✅ |
🔧 To add a new command, see the Creating New Commands section below.
Install Cazalla
Once Mythic is installed and running, use the following command to install Cazalla:
./mythic-cli install github <repository-url>
Communication Protocol
HEADER - Implant to C2
| Key | Key Len (bytes) | Type |
|---|---|---|
| UUID | 36 | Str (char*) |
| Action | 1 | UInt32 |
HEADER - C2 to Implant
| Key | Key Len (bytes) | Type |
|---|---|---|
| Action | 1 | Int32 |
UUID | BODY
Checkin - Implant to C2
Expected:
{
"action": "checkin",
"uuid": "a21bab2e-462e-49ab-9800-fbedaf53ad15",
"ip": "127.0.0.1",
"os": "win",
"arch": "x64",
"hostname": "PC",
"user": "bob",
"domain": "domain.com",
"pid": 123,
"processname": "malware.exe"
}
| Key | Key Len (bytes) | Type |
|---|---|---|
| UUID | 36 | Str (char*) |
| Size IP | 4 | Uint32 |
| IP | Size IP | Str (char*) |
| Size OS | 4 | Uint32 |
| OS | Size OS | Str (char*) |
| Architecture | 1 | Int |
| Size Hostname | 4 | Uint32 |
| HostName | Size Hostname | Str (char*) |
| Size Username | 4 | Uint32 |
| Username | Size Username | Str (char*) |
| Size Domain | 4 | Uint32 |
| Domain | Size Domain | Str (char*) |
| PID | 4 | Uint32 |
| Size Process | 4 | Uint32 |
| Process Name | Size Process Name | Str (char*) |
| Size ExternIP | 4 | Uint32 |
| Extern IP | Size Extern IP | Str (char*) |
Checkin - C2 to Implant
| Key | Key Len (bytes) | Type |
|---|---|---|
| New UUID | 36 | Str (char*) |
| Status | 1 | Byte |
GetTasking - Implant to C2
Expected:
{
"action": "get_tasking",
"tasking_size": 1
}
| Key | Key Len (bytes) | Type |
|---|---|---|
| Number tasks | 4 | Uint32 |
GetTasking - C2 to Implant
Expected:
{
"action": "get_tasking",
"tasks": [
{
"command": "command name",
"parameters": "command param string",
"timestamp": 1578706611.324671,
"id": "task uuid"
}
]
}
| Key | Key Len (bytes) | Type |
|---|---|---|
| NumberOfTasks | 4 | Uint32 |
| Size Of Task1 | 4 | Uint32 |
| Task1 CMD | 1 | Int |
| Task1 UUID | 36 | Str (char*) |
| Task1 LenPara1 | 4 | Uint32 |
| Task1 Param1 | LenParam1 Task1 | Str(char*) |
Post Response Header - Implant to C2
| Key | Key Len (bytes) | Type |
|---|---|---|
| Number Resp | 4 | Uint32 |
Post Response Header - C2 to Implant
| Key | Key Len (bytes) | Type |
|---|---|---|
| Number Resp | 4 | Uint32 |
Post Response Classic Output Return - Implant to C2
Expected:
{
"action": "post_response",
"responses": [
{
"task_id": "uuid of task",
... response message
}
]
}
| Key | Key Len (bytes) | Type |
|---|---|---|
| UUID Resp 1 | 36 | Str (char*) |
| Size Output R1 | 4 | Uint32 |
| Output R1 | Size Output | Bytes |
| Status R1 | 1 | Int |
Post Response Classic Output Return - C2 to Implant
Expected:
{
"action": "post_response",
"responses": [
{
"task_id": UUID,
"status": "success" or "error",
"error": "error message if it exists"
}
]
}
| Key | Key Len (bytes) | Type |
|---|---|---|
| Status Resp1 | 1 | Int |
Creating New Commands
📦 Mythic Side (Python)
To add a new command in Mythic, create a .py file inside the agent's command folder (e.g., shell.py). A basic structure includes:
- TaskArguments class
- CommandBase class
Example: shell.py
class ShellArguments(TaskArguments):
def __init__(self, command_line, **kwargs):
...
self.args = [
CommandParameter(name="command", type=ParameterType.String, ...)
]
class ShellCommand(CommandBase):
cmd = "shell"
...
async def create_tasking(self, task: MythicTask):
...
async def process_response(self, task, response):
...
🧠 C2 Side (Python - commands_from_c2.py)
Update the commands dictionary with a unique hex code and input type:
commands = {
...
"newcmd": {"hex_code": 0xAB, "input_type": "string"}
}
Update responseTasking to handle serialization of the new command’s parameters.
💻 Implant Side (C)
Update the following files:
comandos.h
#define NEWCMD_CMD 0xAB
BOOL ejecutarNuevoCmd(PAnalizador argumentos);
comandos.c
Add your command logic in handleGetTasking():
else if (tarea == NEWCMD_CMD) {
ejecutarNuevoCmd(analizadorTarea);
}
nuevo_cmd.c and nuevo_cmd.h
Create your new command implementation.
BOOL ejecutarNuevoCmd(PAnalizador argumentos) {
// handle parsing and execution here
}
Don’t forget to add the source file to the build and include its header where needed.
Future Development
Planned improvements:
Improvements to perform ASAP:
- Proxy
https://github.com/jeeschr/basic-proxy-server/blob/master/proxy.c
- BOFexecute
- rportfwd
https://github.com/fidian/tcp-port-forward/blob/master/portforward.c https://www.reddit.com/r/C_Programming/s/bWOTxm6phn
Improvements tier 2:
- Persistence and evasion mechanisms
- Full implementation of pwd
- Additional file system and process control commands
- File upload/download
Made with ☕ and monster by the Kaseya OFSTeam