29 KiB
Cazalla - Mythic C2 Agent
📋 Table of Contents
- Overview
- Features
- Installation
- Supported Commands
- SOCKS Proxy Support
- Communication Protocol
- Configuration
- Process Browser Integration
- Artifacts Support
- 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
- Channel Encryption: Optional AES-256-CBC encryption with HMAC-SHA256 for secure communication
- Process Browser Support: Integrated with Mythic's Process Browser for unified process management across multiple callbacks
- 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.
🔨 Customizing Cazalla
If you've forked Cazalla or made local changes and want to use your customized version, you need to configure Mythic to use local builds instead of remote images.
Making Changes Work
By default, Mythic may use pre-built remote images for faster deployment. However, if you've made local changes to the agent code, translator, or any Python files, you need to configure Mythic to use your local build context.
Configure Build Variables
After installing Cazalla, Mythic will add these variables to your ~/Mythic/.env file:
# Use local build context instead of remote image
CAZALLA_USE_BUILD_CONTEXT="true"
# Use direct mount instead of Docker volume (recommended for development)
CAZALLA_USE_VOLUME="false"
# Optional: Remote image URL (only used if USE_BUILD_CONTEXT is false)
CAZALLA_REMOTE_IMAGE="ghcr.io/your-org/cazalla:v1.0.0"
Variables Explanation
-
CAZALLA_USE_BUILD_CONTEXT:- Set to
"true"to build from your localDockerfileand include your local changes - Set to
"false"(default) to use the pre-built remote image - Important: You must set this to
"true"if you've modified any files!
- Set to
-
CAZALLA_USE_VOLUME:- Set to
"false"(default) to mount the local folder directly into the container - Set to
"true"to use a Docker volume instead - Direct mount is better for development as changes are immediately visible
- Set to
-
CAZALLA_REMOTE_IMAGE(optional):- Specifies a pre-built remote image URL
- Only used when
CAZALLA_USE_BUILD_CONTEXT="false"
Apply Changes
After modifying these variables, rebuild the container:
cd ~/Mythic
sudo ./mythic-cli build cazalla
Verifying Your Changes
- Make your code changes
- Set
CAZALLA_USE_BUILD_CONTEXT="true"in~/Mythic/.env - Run
sudo ./mythic-cli build cazalla - Check logs:
sudo docker logs cazalla_translator
If changes still don't appear:
- Verify the
Dockerfilecopies your modified files (see line 39:COPY ./ /Mythic/cazalla/) - Check that file paths in the Dockerfile match your directory structure
- Ensure you're editing files in the correct location (inside
Payload_Type/cazalla/)
Example: Customizing Agent Code
# 1. Edit agent C code
vim Payload_Type/cazalla/cazalla/agent_code/cazalla/comandos.c
# 2. Ensure build context is enabled
echo 'CAZALLA_USE_BUILD_CONTEXT="true"' >> ~/Mythic/.env
# 3. Rebuild the container
cd ~/Mythic
sudo ./mythic-cli build cazalla
# 4. Rebuild a payload from the UI
# Your changes will now be included in the compiled agent
For more information, see the Mythic documentation on customizing public agents.
🛠️ 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 with Process Browser support | ps |
Note: The ps command integrates with Mythic's Process Browser, allowing unified process management across multiple callbacks. Process lists are automatically synchronized and viewable from the Process Browser UI.
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.
Channel Encryption (Optional)
When encryption is enabled via the AESPSK build parameter:
- Encryption Format:
[IV:16 bytes][AES-256-CBC Ciphertext][HMAC-SHA256:32 bytes] - Agent Side: Encrypts all outgoing messages before base64 encoding
- Translator Side: Decrypts all incoming messages after base64 decoding
- Key Management: 256-bit AES key embedded at build time, unique per payload
This provides end-to-end encryption independent of the transport layer (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)
- Encryption: Channel encryption settings (optional)
- AESPSK: Enable AES-256-CBC encryption with HMAC-SHA256
- When enabled, all agent ↔ Mythic communication is encrypted end-to-end
- The encryption key is automatically generated by Mythic and embedded in the payload
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
Channel Encryption
Cazalla supports optional AES-256-CBC encryption with HMAC-SHA256 for secure agent-to-Mythic communication. This provides an additional layer of security beyond HTTPS.
How It Works
- Mythic generates a random 256-bit AES key during payload creation
- Key is embedded in the agent binary at build time
- All messages are encrypted using AES-256-CBC mode with a random IV per message
- HMAC-SHA256 is calculated over the IV + ciphertext for integrity verification
- Format:
[IV:16 bytes][Ciphertext:N bytes][HMAC:32 bytes]
Enabling Encryption
Encryption is enabled automatically when building with the AESPSK parameter in Mythic's UI:
- Navigate to Payloads → Create Payload
- Select Cazalla as the payload type
- In the build parameters, configure AESPSK:
- Set
crypto_typetoaes256_hmac - Mythic will automatically generate an
enc_key
- Set
- Click Build
The encryption key is unique per payload and is shared between the agent and translator.
Encryption Status
You can verify encryption is enabled by checking the build output:
- Look for:
✓ Encryption ENABLED: crypto_type=aes256_hmac, enc_key present - The translator logs will show encryption/decryption operations
Security Considerations
- Key Management: Each payload has a unique encryption key managed by Mythic
- IV Randomization: A new random IV is generated for each encrypted message
- HMAC Verification: All encrypted messages include HMAC for integrity validation
- Defense in Depth: Encryption works alongside HTTPS for layered security
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
📊 Process Browser Integration
Cazalla's ps command fully integrates with Mythic's Process Browser feature, providing unified process management across multiple callbacks on the same host.
Features
- Unified Process Lists: All process listings from different callbacks on the same host are aggregated
- Process Hierarchy: View process parent-child relationships when parent process IDs are available
- Rich Process Data: Includes process name, PID, PPID, architecture, user account, and session ID
- Automatic Synchronization: Process lists are automatically synchronized with Mythic's Process Browser UI
Process Browser UI
Access the Process Browser in Mythic:
- Navigate to a callback in the Mythic UI
- Click the PROCESSES tab (next to the CALLBACK tab)
- View all processes from all callbacks on that host
- Use the UI buttons for additional actions (inject, kill, etc.) if implemented
Implementation Details
The ps command implements the process_browser:list supported UI feature:
- Process data is automatically converted to Mythic's Process Browser JSON format
- The translator detects process list responses and formats them accordingly
- Process information includes:
process_id: Process identifier (required)name: Process name (required)parent_process_id: Parent process ID (optional)architecture: Process architecture (x64/x86)user: User account running the processsession_id: Windows session ID
For more information, see the Mythic Process Browser documentation.
🎨 Artifacts Support
Cazalla automatically reports artifacts created during command execution, allowing Mythic to track artifacts like process creation, file writes, and other system modifications.
Features
- Automatic Artifact Detection: Shell commands automatically report Process Create artifacts
- Process Create Tracking: Every
shellcommand execution creates an artifact showing the command line - Artifact Management: Artifacts appear in Mythic's Artifacts page (click the fingerprint icon)
- Extensible: Easy to add artifact reporting for other commands (File Write, Network Connection, etc.)
How It Works
-
Agent Execution: When a command like
shell whoamiis executed, the agent:- Executes the command via
_popen() - Captures the output
- Includes the command string in the response payload
- Executes the command via
-
Translator Detection: The translator detects the command in the response:
- Looks for the artifact marker (
0xFF) in the response - Extracts the command string
- Creates a
Process Createartifact entry
- Looks for the artifact marker (
-
Mythic Integration: The artifact is automatically:
- Added to the response JSON
- Tracked in Mythic's Artifacts page
- Associated with the task and callback
Example Artifact Response
When you execute shell whoami, the response includes:
{
"task_id": "...",
"user_output": "CETP-WIN11-01\\localuser",
"artifacts": [
{
"base_artifact": "Process Create",
"artifact": "whoami",
"needs_cleanup": false,
"resolved": false
}
]
}
Viewing Artifacts
- Navigate to your callback in the Mythic UI
- Click the Artifacts icon (fingerprint) in the top navigation
- View all artifacts created by all commands across all callbacks
- Filter by artifact type, needs cleanup, resolved status, etc.
Supported Artifact Types
Currently supported:
- Process Create: Automatically reported for all
shellcommands - File Write: Automatically reported for:
cp(copy) - reports destination file pathmkdir(create directory) - reports created directory pathrm(delete) - reports deleted file/directory path
Note: The following commands don't require artifacts as they are read-only or don't modify system state:
ls,cd,pwd- read-only file system operationsps- already integrated with Process Browsersleep,exit- don't modify system state
Future support can be added for:
- File Read: When files are downloaded/read (e.g.,
downloadcommand) - File Write: For file upload operations (e.g.,
uploadcommand) - Network Connection: When network operations occur (e.g., for
socksproxy connections) - Registry Modification: When registry keys are modified (e.g.,
reg_set,reg_delete) - Process Inject: When processes are injected into (e.g.,
injectcommand)
Implementation Details
The artifact system uses:
- Agent Side Helper (
paquete.c):addArtifact()function to easily add artifacts to any command response - Agent Side (command handlers): Call
addArtifact(paquete, "Artifact Type", "artifact value")after adding response output - Translator Side (
commands_from_implant.py): Automatically detects, parses, and creates artifact JSON for all artifact types - Helper Function:
create_artifact()in the translator provides a clean API for creating artifacts
Extending Artifacts for Other Commands
To add artifact support to other commands, use the addArtifact() helper function:
-
Include the header in your C file:
#include "paquete.h" -
Add artifact in your command handler:
// After adding the response output addBytes(respuestaTarea, (PBYTE)salida->buffer, salida->length, TRUE); // Add artifact using the helper function addArtifact(respuestaTarea, "Artifact Type", "artifact value"); -
The translator automatically detects and creates the artifact - no changes needed!
Example implementations:
shell: Process Create artifact with commandcp: File Write artifact with destination pathmkdir: File Write artifact with directory pathrm: File Write artifact with deleted path
Best Practices for New Commands
When developing new commands, always consider artifact reporting:
✅ Commands that SHOULD report artifacts:
- Any command that creates, modifies, or deletes files (
cp,mkdir,rm,upload,download) - Any command that executes processes (
shell,execute,inject) - Any command that modifies registry (
reg_set,reg_delete) - Any command that creates network connections (
socks, custom network commands) - Any command that modifies system configuration
❌ Commands that DON'T need artifacts:
- Read-only operations (
ls,cd,pwd,cat,read) - Information gathering commands (
ps,whoami,hostname) - unless they create processes - Commands that only change local state (
sleep,exit)
Rule of thumb: If the command modifies system state or creates forensic evidence, it should report an artifact.
For more information, see the Mythic Artifacts documentation.
🔧 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
│ ├── procesos.c/h # Process listing (with Process Browser support)
│ ├── crypto.c/h # AES-256-CBC encryption with HMAC-SHA256
│ ├── 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
│ ├── ps.py # Process listing (Process Browser enabled)
│ ├── socks.py # SOCKS proxy command
│ └── builder.py # Payload builder (with encryption support)
├── translator/ # Mythic ↔ Agent translation
│ ├── translator.py # Main translator (with encryption support)
│ ├── commands_from_c2.py # Mythic JSON → Binary
│ └── commands_from_implant.py # Binary → Mythic JSON (Process Browser parser)
└── browser_scripts/ # Mythic UI enhancements
├── ls.js # Directory listing formatter
└── ps.js # Process list formatter
Adding New Commands
Important: When developing new commands, always consider whether the command should report artifacts. Commands that modify system state (create processes, write files, modify registry, create network connections, etc.) should include artifact reporting using the addArtifact() helper function. See Artifacts Support for details.
- 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):
Remember to add artifacts if the command modifies system state! After adding the response output, use addArtifact() to report the action:
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);
// IMPORTANT: Add artifact if this command modifies system state
// Examples:
// - If creating/modifying files: addArtifact(respuesta, "File Write", file_path);
// - If creating processes: addArtifact(respuesta, "Process Create", command);
// - If modifying registry: addArtifact(respuesta, "Registry Write", reg_key);
// - If creating network connections: addArtifact(respuesta, "Network Connection", connection_info);
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)
Process Browser Not Showing Processes
Processes not appearing in Process Browser:
- Verify
pscommand executed successfully (check callback output) - Check translator logs:
sudo docker logs cazalla_translator | grep PROCESS_BROWSER - Ensure
supported_ui_features = ["process_browser:list"]is set inps.py - Verify processes are being parsed correctly (translator should show "Parseados X procesos")
- Rebuild translator if changes were made:
sudo ./mythic-cli build cazalla
Processes showing as "UNKNOWN - MISSING DATA":
- This usually indicates processes that couldn't be accessed (protected/system processes)
- Check if the issue persists after rebuilding the agent
- Some processes may legitimately lack certain information (e.g., system processes)
Encryption Issues
Encryption not working:
- Verify
AESPSKparameter is configured in build parameters - Check build output for:
✓ Encryption ENABLED - Review translator logs for encryption/decryption errors:
sudo docker logs cazalla_translator | grep CRYPTO - Ensure encryption key is present in both agent and translator (auto-configured by Mythic)
"HMAC verification failed" errors:
- Usually indicates key mismatch between agent and translator
- Rebuild the payload to regenerate the encryption key
- Check that both agent and translator are using the same payload UUID
📚 References
- Mythic Documentation
- Mythic Process Browser
- Mythic Customizing Public Agents
- 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