logger container ready
This commit is contained in:
@@ -34,6 +34,13 @@ ENV MYTHIC_SERVER_PORT="17443"
|
||||
ENV MYTHIC_SERVER_USERNAME="mythic_admin"
|
||||
ENV MYTHIC_SERVER_PASSWORD="mythic_password"
|
||||
|
||||
# --- Variables de entorno para Syslog Logging Container (opcional) ---
|
||||
# El logger lee la configuración desde logger/syslog.conf (recomendado)
|
||||
# También puede leer desde variables de entorno si están configuradas
|
||||
ENV SYSLOG_SERVER=""
|
||||
ENV SYSLOG_PORT="514"
|
||||
ENV SYSLOG_FACILITY="16"
|
||||
|
||||
# --- Copia tu código de translator ---
|
||||
# This COPY ensures local changes are included when USE_BUILD_CONTEXT=true
|
||||
WORKDIR /Mythic/
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
# 📊 Cazalla Logging Container
|
||||
|
||||
Complete logging system for the Cazalla agent that records all Mythic events and sends them to a central syslog server.
|
||||
|
||||
## 📋 Description
|
||||
|
||||
This logging container follows the official Mythic pattern for logging containers. It automatically subscribes to all Mythic events and logs them to:
|
||||
|
||||
1. **Local files**: One file per day at `/tmp/cazalla_mythic_YYYY-MM-DD.log` (inside the container)
|
||||
2. **Central syslog**: Sends all events to a configured rsyslog server (RFC 3164)
|
||||
|
||||
**Advantages:**
|
||||
- ✅ Does not modify the agent (avoids detections)
|
||||
- ✅ Follows the official Mythic pattern
|
||||
- ✅ Runs on the server, not on the target
|
||||
- ✅ Logs ALL events automatically
|
||||
- ✅ Simple configuration via configuration file
|
||||
- ✅ No log rotation (one file per day, all files are kept)
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Mythic │
|
||||
│ Events │
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ CazallaLogger │
|
||||
│ (Logging │
|
||||
│ Container) │
|
||||
└──────┬──────────┘
|
||||
│
|
||||
├──► Local file (/tmp/cazalla_mythic_YYYY-MM-DD.log)
|
||||
│
|
||||
└──► Syslog Server (rsyslog) ──► /var/log/cazalla.log
|
||||
```
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Method 1: Configuration File (Recommended)
|
||||
|
||||
The logger reads configuration from a file inside the container. **You don't need to modify docker-compose.yml**.
|
||||
|
||||
1. **Edit the configuration file:**
|
||||
```bash
|
||||
nano Payload_Type/cazalla/logger/syslog.conf
|
||||
```
|
||||
|
||||
2. **Configure your values:**
|
||||
```bash
|
||||
# Syslog server IP or hostname (leave empty to disable syslog)
|
||||
# Use the host IP (10.8.20.50) or 172.17.0.1 (docker0 bridge) to reach rsyslog on the host
|
||||
SYSLOG_SERVER=10.8.20.50
|
||||
|
||||
# Syslog server port (default: 514)
|
||||
SYSLOG_PORT=514
|
||||
|
||||
# Syslog facility code (0-23, default: 16 = LOCAL0)
|
||||
SYSLOG_FACILITY=16
|
||||
```
|
||||
|
||||
3. **Rebuild and restart the container:**
|
||||
```bash
|
||||
cd /opt/mythic
|
||||
sudo ./mythic-cli build cazalla
|
||||
sudo ./mythic-cli restart cazalla
|
||||
```
|
||||
|
||||
### Method 2: Environment Variables (Alternative)
|
||||
|
||||
If you prefer to use environment variables, you can configure them in Mythic's `.env` file and manually add them to `docker-compose.yml`. However, **Method 1 is simpler**.
|
||||
|
||||
### Configuring rsyslog on the Host
|
||||
|
||||
If you want to receive logs on the Debian host where Mythic runs:
|
||||
|
||||
1. **Install rsyslog** (if not installed):
|
||||
```bash
|
||||
sudo apt-get update && sudo apt-get install -y rsyslog
|
||||
```
|
||||
|
||||
2. **Enable UDP reception** in `/etc/rsyslog.conf`:
|
||||
```bash
|
||||
sudo sed -i 's/#module(load="imudp")/module(load="imudp")/' /etc/rsyslog.conf
|
||||
sudo sed -i 's/#input(type="imudp" port="514")/input(type="imudp" port="514")/' /etc/rsyslog.conf
|
||||
```
|
||||
|
||||
3. **Create rule for Cazalla** in `/etc/rsyslog.d/50-cazalla.conf`:
|
||||
```bash
|
||||
sudo bash -c 'cat > /etc/rsyslog.d/50-cazalla.conf << EOF
|
||||
# Logs from Cazalla Mythic agent
|
||||
# Filter by facility LOCAL0 (16)
|
||||
if \$syslogfacility-text == "local0" then /var/log/cazalla.log
|
||||
& stop
|
||||
EOF
|
||||
'
|
||||
```
|
||||
|
||||
4. **Restart rsyslog:**
|
||||
```bash
|
||||
sudo systemctl restart rsyslog
|
||||
```
|
||||
|
||||
5. **Verify it's listening:**
|
||||
```bash
|
||||
sudo netstat -ulnp | grep 514
|
||||
# You should see: UNCONN ... 0.0.0.0:514 ... rsyslogd
|
||||
```
|
||||
|
||||
## 📊 Events Logged
|
||||
|
||||
The logger subscribes to **ALL** Mythic events:
|
||||
|
||||
| Event | Description | Example |
|
||||
|--------|-------------|---------|
|
||||
| `CALLBACK` | Agent connects/checkin | New callback from target |
|
||||
| `TASK` | Task created/updated | Command executed (`shell`, `ls`, etc.) |
|
||||
| `RESPONSE` | Agent response | Command output |
|
||||
| `CREDENTIAL` | Credential captured | Username/password extracted |
|
||||
| `KEYLOG` | Keystrokes captured | Keylogger data |
|
||||
| `FILE` | File uploaded/downloaded | File upload/download |
|
||||
| `ARTIFACT` | Artifact generated | IOC detected (process created, etc.) |
|
||||
| `PAYLOAD` | Payload created | New payload generated |
|
||||
|
||||
## 📝 Log Format
|
||||
|
||||
### Local File
|
||||
|
||||
Logs are saved in structured JSON format:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "TASK",
|
||||
"timestamp": "2025-11-17T15:18:23.117715674Z",
|
||||
"operation_id": 1,
|
||||
"operation_name": "Operation Chimera",
|
||||
"username": "mythic_admin",
|
||||
"action": "new_task",
|
||||
"server_name": "mythic",
|
||||
"data": {
|
||||
"id": 18,
|
||||
"command_name": "shell",
|
||||
"params": "{\"command\":\"whoami\"}",
|
||||
"status": "success",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Syslog
|
||||
|
||||
Messages are sent to syslog in RFC 3164 format with complete JSON:
|
||||
|
||||
```
|
||||
<134>Nov 17 15:18:23 cazalla cazalla: {"event_type": "TASK", "timestamp": "...", ...}
|
||||
```
|
||||
|
||||
- **Priority**: Calculated based on facility (16 = LOCAL0) + severity
|
||||
- **Severity**: INFO (6) for most, WARNING (4) for artifacts
|
||||
- **Tag**: `cazalla`
|
||||
- **Message**: Complete JSON of the event
|
||||
|
||||
## 🔍 Verification
|
||||
|
||||
### Verify Logger is Active
|
||||
|
||||
```bash
|
||||
# View container logs
|
||||
sudo docker logs cazalla | grep -i "syslog\|Logger initialized"
|
||||
|
||||
# You should see:
|
||||
# INFO:cazalla_logger:Syslog client initialized: 10.8.20.50:514 (facility=16)
|
||||
# INFO:cazalla_logger:Logger initialized: cazalla_logger
|
||||
```
|
||||
|
||||
### Verify Local Logs (inside container)
|
||||
|
||||
```bash
|
||||
# View today's log file
|
||||
sudo docker exec cazalla cat /tmp/cazalla_mythic_$(date +%Y-%m-%d).log | tail -20
|
||||
|
||||
# Or view all log files
|
||||
sudo docker exec cazalla ls -lh /tmp/cazalla_mythic_*.log
|
||||
```
|
||||
|
||||
### Verify Logs in Syslog (host)
|
||||
|
||||
```bash
|
||||
# View logs in real-time
|
||||
sudo tail -f /var/log/cazalla.log
|
||||
|
||||
# View recent events
|
||||
sudo tail -50 /var/log/cazalla.log
|
||||
|
||||
# Count events by type
|
||||
sudo tail -1000 /var/log/cazalla.log | grep -o '"event_type": "[^"]*"' | sort | uniq -c
|
||||
```
|
||||
|
||||
### Verify Events are Being Sent
|
||||
|
||||
```bash
|
||||
# View events in container logs
|
||||
sudo docker logs cazalla | grep -E "(TASK|RESPONSE|CALLBACK)" | tail -10
|
||||
|
||||
# View events in syslog
|
||||
sudo tail -20 /var/log/cazalla.log | grep -E "(TASK|RESPONSE|CALLBACK)"
|
||||
```
|
||||
|
||||
## 🛠️ Troubleshooting
|
||||
|
||||
### Logger Not Initializing
|
||||
|
||||
**Symptom:** You don't see "Logger initialized" in container logs.
|
||||
|
||||
**Solution:**
|
||||
1. Verify the logger is imported in `main.py`:
|
||||
```bash
|
||||
grep -A 5 "from logger" Payload_Type/cazalla/main.py
|
||||
```
|
||||
|
||||
2. Check container logs:
|
||||
```bash
|
||||
sudo docker logs cazalla | grep -i "error\|warning\|logger"
|
||||
```
|
||||
|
||||
### Syslog Not Initializing
|
||||
|
||||
**Symptom:** You see "Syslog not configured" in logs.
|
||||
|
||||
**Solution:**
|
||||
1. Verify the configuration file exists:
|
||||
```bash
|
||||
sudo docker exec cazalla cat /Mythic/logger/syslog.conf
|
||||
```
|
||||
|
||||
2. Verify `SYSLOG_SERVER` has a value (not empty):
|
||||
```bash
|
||||
sudo docker exec cazalla cat /Mythic/logger/syslog.conf | grep SYSLOG_SERVER
|
||||
```
|
||||
|
||||
3. Rebuild the container if you just edited the file:
|
||||
```bash
|
||||
cd /opt/mythic
|
||||
sudo ./mythic-cli build cazalla
|
||||
sudo ./mythic-cli restart cazalla
|
||||
```
|
||||
|
||||
### Messages Not Reaching Syslog
|
||||
|
||||
**Symptom:** Logger initializes but you don't see messages in `/var/log/cazalla.log`.
|
||||
|
||||
**Solution:**
|
||||
1. Verify rsyslog is listening:
|
||||
```bash
|
||||
sudo netstat -ulnp | grep 514
|
||||
```
|
||||
|
||||
2. Verify connectivity from container:
|
||||
```bash
|
||||
# Get host IP (docker0 bridge or real IP)
|
||||
IP_HOST=$(ip addr show docker0 | grep "inet " | awk '{print $2}' | cut -d/ -f1)
|
||||
echo "Host IP: $IP_HOST"
|
||||
|
||||
# Test from container
|
||||
sudo docker exec cazalla python3 -c "
|
||||
import socket
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.sendto(b'test', ('$IP_HOST', 514))
|
||||
print('Message sent')
|
||||
"
|
||||
|
||||
# Verify it arrived
|
||||
sleep 1 && sudo tail -1 /var/log/cazalla.log
|
||||
```
|
||||
|
||||
3. Verify rsyslog configuration:
|
||||
```bash
|
||||
sudo cat /etc/rsyslog.d/50-cazalla.conf
|
||||
sudo systemctl status rsyslog
|
||||
```
|
||||
|
||||
4. Verify firewall:
|
||||
```bash
|
||||
sudo iptables -L -n | grep 514
|
||||
```
|
||||
|
||||
### Logs Are Empty or Incomplete
|
||||
|
||||
**Symptom:** Logs have empty fields (`"operation_id": null`, `"username": ""`).
|
||||
|
||||
**Solution:**
|
||||
This is normal for some events. The logger extracts all available information from the `LoggingMessage` object. If some fields are empty, it's because Mythic doesn't provide them for that specific event.
|
||||
|
||||
### Configuration File Not Copied to Container
|
||||
|
||||
**Symptom:** The `syslog.conf` file doesn't exist in the container.
|
||||
|
||||
**Solution:**
|
||||
1. Verify the file exists locally:
|
||||
```bash
|
||||
ls -la Payload_Type/cazalla/logger/syslog.conf
|
||||
```
|
||||
|
||||
2. Verify `CAZALLA_USE_BUILD_CONTEXT="true"` in `/opt/mythic/.env`:
|
||||
```bash
|
||||
grep CAZALLA_USE_BUILD_CONTEXT /opt/mythic/.env
|
||||
```
|
||||
|
||||
3. Rebuild the container:
|
||||
```bash
|
||||
cd /opt/mythic
|
||||
sudo ./mythic-cli build cazalla
|
||||
```
|
||||
|
||||
## 📚 Syslog Facility Codes
|
||||
|
||||
| Code | Facility | Description |
|
||||
|--------|----------|-------------|
|
||||
| 0 | KERN | Kernel messages |
|
||||
| 1 | USER | User-level messages |
|
||||
| 2 | MAIL | Mail system |
|
||||
| 3 | DAEMON | System daemons |
|
||||
| 4 | AUTH | Security/authorization |
|
||||
| 5 | SYSLOG | Messages generated by syslogd |
|
||||
| 6 | LPR | Line printer subsystem |
|
||||
| 7 | NEWS | Network news subsystem |
|
||||
| 8 | UUCP | UUCP subsystem |
|
||||
| 9 | CRON | Clock daemon |
|
||||
| 10 | AUTHPRIV | Security/authorization (private) |
|
||||
| 11 | FTP | FTP daemon |
|
||||
| **16-23** | **LOCAL0-7** | **Reserved for local use (recommended)** |
|
||||
|
||||
**Recommendation:** Use `16` (LOCAL0) for custom application logs.
|
||||
|
||||
## 🔧 Code Structure
|
||||
|
||||
```
|
||||
Payload_Type/cazalla/logger/
|
||||
├── __init__.py # Exports cazalla_logger
|
||||
├── logger.py # CazallaLogger class + SyslogClient
|
||||
├── syslog.conf # Syslog configuration
|
||||
└── README.md # This documentation
|
||||
```
|
||||
|
||||
### Main Classes
|
||||
|
||||
- **`CazallaLogger`**: Inherits from `mythic_container.LoggingBase.Log`
|
||||
- Automatically registers with Mythic
|
||||
- Implements `new_*` methods for each event type
|
||||
- Formats messages as JSON
|
||||
- Sends to local file and syslog
|
||||
|
||||
- **`SyslogClient`**: UDP client for syslog (RFC 3164)
|
||||
- Formats messages according to RFC 3164
|
||||
- Maps events to syslog severities
|
||||
- Handles errors silently
|
||||
|
||||
## 📖 References
|
||||
|
||||
- [Mythic Logging Container Documentation](https://docs.mythic-c2.net/customizing/3.-consuming-containers/logging)
|
||||
- [RFC 3164 - The BSD syslog Protocol](https://tools.ietf.org/html/rfc3164)
|
||||
- [Mythic Example Containers](https://github.com/MythicMeta/ExampleContainers)
|
||||
|
||||
## 📝 Important Notes
|
||||
|
||||
1. **You don't need to modify docker-compose.yml** - Everything is configured from `syslog.conf`
|
||||
2. **The file is copied to the container** when built, so after editing it you need to rebuild
|
||||
3. **Logs are saved locally** even if syslog is configured
|
||||
4. **One file per day** - No rotation, all files are kept
|
||||
5. **Environment variables take precedence** over the configuration file if both are configured
|
||||
|
||||
---
|
||||
|
||||
**Developed by:** Kaseya OFSTeam
|
||||
@@ -0,0 +1,6 @@
|
||||
# Logger package for Cazalla Mythic agent
|
||||
from .logger import cazalla_logger
|
||||
|
||||
# Export logger instance so it can be imported
|
||||
__all__ = ['cazalla_logger']
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
"""
|
||||
Logging Container for Cazalla Mythic Agent
|
||||
|
||||
This container subscribes to Mythic events following the official Python logging container pattern.
|
||||
Based on: https://docs.mythic-c2.net/customizing/3.-consuming-containers/logging
|
||||
Example: https://github.com/MythicMeta/ExampleContainers/tree/main/Payload_Type/python_services/my_logger
|
||||
|
||||
First implementation: Uses built-in Mythic logger (stdout/file)
|
||||
Future: Will add syslog forwarding
|
||||
"""
|
||||
|
||||
from mythic_container.LoggingBase import *
|
||||
import logging
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import socket
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class CazallaLogger(Log):
|
||||
"""
|
||||
Logging container for Cazalla agent
|
||||
Follows the official Python logging container pattern
|
||||
"""
|
||||
LogToFilePath = "/tmp" # Path for file logging (empty = stdout only)
|
||||
LogLevel = logging.INFO
|
||||
LogMaxSizeInMB = 0 # 0 = no rotation, keep everything
|
||||
LogMaxBackups = 0 # No backups needed (one file per day)
|
||||
name = "cazalla_logger"
|
||||
description = "Cazalla logging container - logs ALL Mythic events to daily files and syslog"
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the logger"""
|
||||
# Set up Python logging
|
||||
self.mylogger = logging.getLogger('cazalla_logger')
|
||||
self.mylogger.setLevel(self.LogLevel)
|
||||
|
||||
# Clear any existing handlers to avoid duplicates
|
||||
self.mylogger.handlers.clear()
|
||||
|
||||
# Always add stdout handler
|
||||
stdout_handler = logging.StreamHandler()
|
||||
stdout_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
stdout_handler.setFormatter(stdout_formatter)
|
||||
self.mylogger.addHandler(stdout_handler)
|
||||
|
||||
# Configure file logging - one file per day (no rotation)
|
||||
if self.LogToFilePath:
|
||||
from pathlib import Path
|
||||
log_dir = Path(self.LogToFilePath)
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create filename with current date: cazalla_mythic_YYYY-MM-DD.log
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
log_file = log_dir / f'cazalla_mythic_{today}.log'
|
||||
|
||||
# Use FileHandler (no rotation) - one file per day
|
||||
file_handler = logging.FileHandler(log_file, mode='a', encoding='utf-8')
|
||||
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
file_handler.setFormatter(file_formatter)
|
||||
self.mylogger.addHandler(file_handler)
|
||||
|
||||
self.log_file_path = str(log_file)
|
||||
else:
|
||||
self.log_file_path = "stdout only"
|
||||
|
||||
# Initialize syslog client if configured
|
||||
# Try to read from config file first, then fall back to environment variables
|
||||
self.syslog_client = None
|
||||
syslog_server, syslog_port, syslog_facility = self._load_syslog_config()
|
||||
|
||||
if syslog_server:
|
||||
try:
|
||||
self.syslog_client = SyslogClient(syslog_server, syslog_port, syslog_facility)
|
||||
self.mylogger.info(f"Syslog client initialized: {syslog_server}:{syslog_port} (facility={syslog_facility})")
|
||||
except Exception as e:
|
||||
self.mylogger.warning(f"Failed to initialize syslog client: {e}")
|
||||
else:
|
||||
self.mylogger.info("Syslog not configured (edit logger/syslog.conf or set SYSLOG_SERVER env var to enable)")
|
||||
|
||||
# Log initialization
|
||||
self.mylogger.info(f"Logger initialized: {self.name}")
|
||||
self.mylogger.info(f"Log file: {self.log_file_path}")
|
||||
self.mylogger.info(f"Log level: {logging.getLevelName(self.LogLevel)}")
|
||||
self.mylogger.info("Subscribed to ALL events: callbacks, tasks, responses, credentials, keylogs, files, artifacts, payloads")
|
||||
self.mylogger.info("Logging mode: ONE FILE PER DAY (no rotation, no deletion)")
|
||||
|
||||
def _load_syslog_config(self):
|
||||
"""
|
||||
Load syslog configuration from config file or environment variables
|
||||
Priority: 1) Config file, 2) Environment variables, 3) Defaults
|
||||
Returns: (server, port, facility)
|
||||
"""
|
||||
# Try to read from config file first
|
||||
# Try multiple possible paths
|
||||
possible_paths = [
|
||||
'/Mythic/logger/syslog.conf', # Absolute path in container
|
||||
os.path.join(os.path.dirname(__file__), 'syslog.conf') if '__file__' in globals() else None,
|
||||
]
|
||||
# Remove None values
|
||||
possible_paths = [p for p in possible_paths if p]
|
||||
|
||||
config_file = None
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
config_file = path
|
||||
break
|
||||
|
||||
# If no config file found, use first path as default
|
||||
if not config_file and possible_paths:
|
||||
config_file = possible_paths[0]
|
||||
|
||||
syslog_server = ""
|
||||
syslog_port = 514
|
||||
syslog_facility = 16
|
||||
|
||||
# Read from config file if it exists
|
||||
if config_file and os.path.exists(config_file):
|
||||
try:
|
||||
with open(config_file, 'r') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
# Skip comments and empty lines
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
# Parse key=value pairs
|
||||
if '=' in line:
|
||||
key, value = line.split('=', 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
if key == 'SYSLOG_SERVER' and value: # Only set if value is not empty
|
||||
syslog_server = value
|
||||
elif key == 'SYSLOG_PORT':
|
||||
try:
|
||||
syslog_port = int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
elif key == 'SYSLOG_FACILITY':
|
||||
try:
|
||||
syslog_facility = int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
except Exception as e:
|
||||
# Can't use self.mylogger here as it might not be initialized yet
|
||||
print(f"Warning: Error reading syslog config file {config_file}: {e}", file=sys.stderr)
|
||||
|
||||
# Override with environment variables if set (env vars take precedence)
|
||||
# Only override if env var is not empty
|
||||
env_syslog_server = os.getenv("SYSLOG_SERVER", "")
|
||||
if env_syslog_server: # Only override if env var is set and not empty
|
||||
syslog_server = env_syslog_server
|
||||
if os.getenv("SYSLOG_PORT"):
|
||||
try:
|
||||
syslog_port = int(os.getenv("SYSLOG_PORT"))
|
||||
except ValueError:
|
||||
pass
|
||||
if os.getenv("SYSLOG_FACILITY"):
|
||||
try:
|
||||
syslog_facility = int(os.getenv("SYSLOG_FACILITY"))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return syslog_server, syslog_port, syslog_facility
|
||||
|
||||
def _format_log_message(self, event_type: str, msg: LoggingMessage) -> str:
|
||||
"""Format log message with all available information"""
|
||||
try:
|
||||
# Extract information from LoggingMessage (fields are capitalized)
|
||||
operation_id = getattr(msg, 'OperationID', None)
|
||||
operation_name = getattr(msg, 'OperationName', None)
|
||||
username = getattr(msg, 'OperatorUsername', None)
|
||||
timestamp = getattr(msg, 'Timestamp', None)
|
||||
action = getattr(msg, 'Action', None)
|
||||
data = getattr(msg, 'Data', {})
|
||||
server_name = getattr(msg, 'ServerName', None)
|
||||
|
||||
# Try to_json() method if available (some versions of LoggingMessage have it)
|
||||
try:
|
||||
if hasattr(msg, 'to_json'):
|
||||
msg_dict = msg.to_json()
|
||||
# Override with to_json data if available
|
||||
operation_id = msg_dict.get('operation_id') or operation_id
|
||||
operation_name = msg_dict.get('operation_name') or operation_name
|
||||
username = msg_dict.get('username') or username
|
||||
timestamp = msg_dict.get('timestamp') or timestamp
|
||||
action = msg_dict.get('action') or action
|
||||
data = msg_dict.get('data', data)
|
||||
server_name = msg_dict.get('server_name') or server_name
|
||||
except:
|
||||
pass
|
||||
|
||||
# Format timestamp
|
||||
if timestamp:
|
||||
if isinstance(timestamp, str):
|
||||
formatted_timestamp = timestamp
|
||||
else:
|
||||
formatted_timestamp = timestamp.isoformat() if hasattr(timestamp, 'isoformat') else str(timestamp)
|
||||
else:
|
||||
formatted_timestamp = datetime.now().isoformat()
|
||||
|
||||
# Format as structured JSON for easy parsing
|
||||
log_data = {
|
||||
"event_type": event_type,
|
||||
"timestamp": formatted_timestamp,
|
||||
"operation_id": operation_id,
|
||||
"operation_name": operation_name,
|
||||
"username": username,
|
||||
"action": action,
|
||||
"server_name": server_name,
|
||||
"data": data if data else {}
|
||||
}
|
||||
|
||||
return json.dumps(log_data, default=str, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
# Fallback: try to convert message to string/dict
|
||||
try:
|
||||
if hasattr(msg, 'to_json'):
|
||||
return json.dumps({"event_type": event_type, "raw_message": msg.to_json()}, default=str)
|
||||
else:
|
||||
return json.dumps({"event_type": event_type, "raw_message": str(msg)}, default=str)
|
||||
except:
|
||||
return f'{{"event_type": "{event_type}", "error": "Failed to format message: {e}", "raw": "{str(msg)}"}}'
|
||||
|
||||
async def new_callback(self, msg: LoggingMessage) -> None:
|
||||
"""Handle new callback events - agent checkin"""
|
||||
log_msg = self._format_log_message("CALLBACK", msg)
|
||||
self.mylogger.info(log_msg)
|
||||
# Send to syslog if configured
|
||||
if self.syslog_client:
|
||||
self.syslog_client.send_syslog("new_callback", log_msg)
|
||||
|
||||
async def new_task(self, msg: LoggingMessage) -> None:
|
||||
"""Handle new task events - command execution"""
|
||||
log_msg = self._format_log_message("TASK", msg)
|
||||
self.mylogger.info(log_msg)
|
||||
# Send to syslog if configured
|
||||
if self.syslog_client:
|
||||
self.syslog_client.send_syslog("new_task", log_msg)
|
||||
|
||||
async def new_response(self, msg: LoggingMessage) -> None:
|
||||
"""Handle new response events - command output"""
|
||||
log_msg = self._format_log_message("RESPONSE", msg)
|
||||
self.mylogger.info(log_msg)
|
||||
# Send to syslog if configured
|
||||
if self.syslog_client:
|
||||
self.syslog_client.send_syslog("new_response", log_msg)
|
||||
|
||||
async def new_credential(self, msg: LoggingMessage) -> None:
|
||||
"""Handle new credential events - captured credentials"""
|
||||
log_msg = self._format_log_message("CREDENTIAL", msg)
|
||||
self.mylogger.info(log_msg)
|
||||
# Send to syslog if configured
|
||||
if self.syslog_client:
|
||||
self.syslog_client.send_syslog("new_credential", log_msg)
|
||||
|
||||
async def new_keylog(self, msg: LoggingMessage) -> None:
|
||||
"""Handle new keylog events - captured keystrokes"""
|
||||
log_msg = self._format_log_message("KEYLOG", msg)
|
||||
self.mylogger.info(log_msg)
|
||||
# Send to syslog if configured
|
||||
if self.syslog_client:
|
||||
self.syslog_client.send_syslog("new_keylog", log_msg)
|
||||
|
||||
async def new_file(self, msg: LoggingMessage) -> None:
|
||||
"""Handle new file events - file uploads/downloads"""
|
||||
log_msg = self._format_log_message("FILE", msg)
|
||||
self.mylogger.info(log_msg)
|
||||
# Send to syslog if configured
|
||||
if self.syslog_client:
|
||||
self.syslog_client.send_syslog("new_file", log_msg)
|
||||
|
||||
async def new_artifact(self, msg: LoggingMessage) -> None:
|
||||
"""Handle new artifact events - IOCs and artifacts"""
|
||||
log_msg = self._format_log_message("ARTIFACT", msg)
|
||||
self.mylogger.warning(log_msg) # Warning level for artifacts
|
||||
# Send to syslog if configured
|
||||
if self.syslog_client:
|
||||
self.syslog_client.send_syslog("new_artifact", log_msg)
|
||||
|
||||
async def new_payload(self, msg: LoggingMessage) -> None:
|
||||
"""Handle new payload events - payload creation"""
|
||||
log_msg = self._format_log_message("PAYLOAD", msg)
|
||||
self.mylogger.info(log_msg)
|
||||
# Send to syslog if configured
|
||||
if self.syslog_client:
|
||||
self.syslog_client.send_syslog("new_payload", log_msg)
|
||||
|
||||
|
||||
class SyslogClient:
|
||||
"""UDP syslog client following RFC 3164 for rsyslog integration"""
|
||||
|
||||
def __init__(self, server: str, port: int = 514, facility: int = 16):
|
||||
self.server = server
|
||||
self.port = port
|
||||
self.facility = facility
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
self.hostname = socket.gethostname()
|
||||
self.tag = "cazalla"
|
||||
|
||||
def _get_timestamp(self) -> str:
|
||||
"""Get current timestamp in RFC 3164 format (Mmm dd HH:MM:SS)"""
|
||||
now = datetime.now()
|
||||
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
|
||||
return f"{months[now.month-1]} {now.day:2d} {now.hour:02d}:{now.minute:02d}:{now.second:02d}"
|
||||
|
||||
def _format_syslog_message(self, severity: int, message: str) -> bytes:
|
||||
"""
|
||||
Format syslog message according to RFC 3164
|
||||
Format: <PRI>TIMESTAMP HOSTNAME TAG: MESSAGE
|
||||
"""
|
||||
priority = (self.facility * 8) + severity
|
||||
timestamp = self._get_timestamp()
|
||||
formatted = f"<{priority}>{timestamp} {self.hostname} {self.tag}: {message}"
|
||||
return formatted.encode('utf-8')
|
||||
|
||||
def send_syslog(self, event_type: str, json_message: str):
|
||||
"""
|
||||
Send message to syslog server
|
||||
|
||||
Args:
|
||||
event_type: Type of event (CALLBACK, TASK, etc.)
|
||||
json_message: JSON formatted message to send
|
||||
"""
|
||||
try:
|
||||
# Map event types to syslog severity
|
||||
severity_map = {
|
||||
"CALLBACK": 6, # INFO
|
||||
"TASK": 6, # INFO
|
||||
"RESPONSE": 6, # INFO
|
||||
"CREDENTIAL": 6, # INFO
|
||||
"KEYLOG": 6, # INFO
|
||||
"FILE": 6, # INFO
|
||||
"ARTIFACT": 4, # WARNING
|
||||
"PAYLOAD": 6, # INFO
|
||||
}
|
||||
severity = severity_map.get(event_type, 6) # Default to INFO
|
||||
|
||||
# Format and send syslog message
|
||||
syslog_msg = self._format_syslog_message(severity, json_message)
|
||||
self.socket.sendto(syslog_msg, (self.server, self.port))
|
||||
except Exception as e:
|
||||
# Don't fail if syslog send fails, just log it
|
||||
print(f"[SYSLOG] Error sending message: {e}", flush=True)
|
||||
|
||||
|
||||
# Register the logger - Mythic will automatically detect and register it
|
||||
# The class name and inheritance from Log is what makes it work
|
||||
cazalla_logger = CazallaLogger()
|
||||
@@ -0,0 +1,14 @@
|
||||
# Syslog Configuration for Cazalla Logging Container
|
||||
# Edit this file with your syslog server details
|
||||
# After editing, restart the container: sudo ./mythic-cli restart cazalla
|
||||
|
||||
# Syslog server IP or hostname (leave empty to disable syslog)
|
||||
# Use the host IP (10.8.20.50) or 172.17.0.1 (docker0 bridge) to reach rsyslog on the host
|
||||
SYSLOG_SERVER=10.8.20.50
|
||||
|
||||
# Syslog server port (default: 514)
|
||||
SYSLOG_PORT=514
|
||||
|
||||
# Syslog facility code (0-23, default: 16 = LOCAL0)
|
||||
SYSLOG_FACILITY=16
|
||||
|
||||
@@ -4,8 +4,23 @@ import asyncio
|
||||
import cazalla
|
||||
#import websocket.mythic.c2_functions.websocket
|
||||
from translator.translator import *
|
||||
#from my_logger import logger
|
||||
|
||||
# Import logging container - following official Python pattern
|
||||
# The logger is automatically registered when the class is instantiated
|
||||
try:
|
||||
from logger import cazalla_logger # Import logging container
|
||||
print("[MAIN] ✓ Logging container imported and registered")
|
||||
except Exception as e:
|
||||
print(f"[MAIN] WARNING: Could not import logging container: {e}")
|
||||
print("[MAIN] Container will continue without logging functionality")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
#from basic_command_augment import *
|
||||
#from basic_eventer.my_eventing import *
|
||||
|
||||
# Start Mythic services
|
||||
# Note: In Go, basic_logger uses MythicServiceLogger service
|
||||
# In Python, the logging registration happens during import above
|
||||
# If logging needs a separate service, we may need to register it here
|
||||
mythic_container.mythic_service.start_and_run_forever()
|
||||
@@ -32,6 +32,7 @@
|
||||
- [Token Support](#-token-support)
|
||||
- [Context Tracking](#-context-tracking)
|
||||
- [OPSEC Checking](#️-opsec-checking)
|
||||
- [Logging & Syslog](#-logging--syslog)
|
||||
- [Development](#development)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Credits](#credits)
|
||||
@@ -1915,6 +1916,63 @@ For more information, see the [Mythic OPSEC Checking documentation](https://docs
|
||||
|
||||
---
|
||||
|
||||
## 📊 Logging & Syslog
|
||||
|
||||
Cazalla includes a complete logging system that records all Mythic events and sends them to a central syslog server.
|
||||
|
||||
### Features
|
||||
|
||||
- ✅ **Logs ALL events** automatically (callbacks, tasks, responses, credentials, keylogs, files, artifacts, payloads)
|
||||
- ✅ **Does not modify the agent** (avoids detections)
|
||||
- ✅ **Runs on the server**, not on the target
|
||||
- ✅ **Structured JSON format** for easy parsing
|
||||
- ✅ **One file per day** (no rotation, all files are kept)
|
||||
- ✅ **Simple configuration** via configuration file
|
||||
|
||||
### Quick Configuration
|
||||
|
||||
1. **Edit the configuration file:**
|
||||
```bash
|
||||
nano Payload_Type/cazalla/logger/syslog.conf
|
||||
```
|
||||
|
||||
2. **Configure the syslog server IP:**
|
||||
```bash
|
||||
SYSLOG_SERVER=10.8.20.50 # Host IP or syslog server IP
|
||||
SYSLOG_PORT=514
|
||||
SYSLOG_FACILITY=16
|
||||
```
|
||||
|
||||
3. **Rebuild and restart:**
|
||||
```bash
|
||||
cd /opt/mythic
|
||||
sudo ./mythic-cli build cazalla
|
||||
sudo ./mythic-cli restart cazalla
|
||||
```
|
||||
|
||||
### Verification
|
||||
|
||||
```bash
|
||||
# Verify logger is active
|
||||
sudo docker logs cazalla | grep -i "syslog\|Logger initialized"
|
||||
|
||||
# View logs in real-time
|
||||
sudo tail -f /var/log/cazalla.log
|
||||
```
|
||||
|
||||
### Log Locations
|
||||
|
||||
- **Syslog (host)**: `/var/log/cazalla.log`
|
||||
- **Local logs (container)**: `/tmp/cazalla_mythic_YYYY-MM-DD.log`
|
||||
|
||||
### Complete Documentation
|
||||
|
||||
For more details on configuration, troubleshooting, and advanced usage, see:
|
||||
- [Logger Documentation](Payload_Type/cazalla/logger/README.md) - Complete technical documentation
|
||||
- [Agent Wiki - Logging](documentation-payload/Cazalla/logging.md) - User guide
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Development
|
||||
|
||||
### Project Structure
|
||||
|
||||
@@ -82,6 +82,7 @@ See the [Getting Started Guide] for installation and setup instructions.
|
||||
- **Getting Started** - Installation and first steps
|
||||
- **Features Overview** - Detailed feature explanations
|
||||
- **OPSEC Guide** - Operational security considerations
|
||||
- **Logging & Syslog** - System logging and syslog configuration
|
||||
- **Usage Examples** - Practical examples and use cases
|
||||
- **Troubleshooting** - Common issues and solutions
|
||||
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
+++
|
||||
title = "Logging & Syslog"
|
||||
chapter = false
|
||||
weight = 50
|
||||
pre = "6. "
|
||||
+++
|
||||
|
||||
# 📊 Logging & Syslog
|
||||
|
||||
Cazalla includes a complete logging system that records all Mythic events and sends them to a central syslog server for analysis and auditing.
|
||||
|
||||
## 🎯 Description
|
||||
|
||||
Cazalla's logging system:
|
||||
|
||||
- ✅ **Logs ALL events** from Mythic automatically
|
||||
- ✅ **Does not modify the agent** (avoids detections)
|
||||
- ✅ **Runs on the server**, not on the target
|
||||
- ✅ **Sends logs to syslog** central for analysis
|
||||
- ✅ **Saves local logs** as backup
|
||||
- ✅ **Structured JSON format** for easy parsing
|
||||
|
||||
## 📋 Logged Events
|
||||
|
||||
The logger automatically captures all these events:
|
||||
|
||||
| Event | Description |
|
||||
|--------|-------------|
|
||||
| **CALLBACK** | When an agent connects or checks in |
|
||||
| **TASK** | When a task is created or updated (commands executed) |
|
||||
| **RESPONSE** | Agent responses (command output) |
|
||||
| **CREDENTIAL** | Captured credentials |
|
||||
| **KEYLOG** | Keystrokes captured by the keylogger |
|
||||
| **FILE** | Files uploaded or downloaded |
|
||||
| **ARTIFACT** | Generated artifacts (IOCs, processes created, etc.) |
|
||||
| **PAYLOAD** | Created payloads |
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Step 1: Configure the Logger
|
||||
|
||||
The logger is configured via a configuration file. **You don't need to modify docker-compose.yml**.
|
||||
|
||||
1. **Edit the configuration file:**
|
||||
```bash
|
||||
nano Payload_Type/cazalla/logger/syslog.conf
|
||||
```
|
||||
|
||||
2. **Configure the syslog server IP:**
|
||||
```bash
|
||||
# If rsyslog is on the same host as Mythic, use the host IP
|
||||
SYSLOG_SERVER=10.8.20.50
|
||||
|
||||
# Or if using Docker bridge
|
||||
SYSLOG_SERVER=172.17.0.1
|
||||
|
||||
# Port (default 514)
|
||||
SYSLOG_PORT=514
|
||||
|
||||
# Syslog facility (16 = LOCAL0, recommended)
|
||||
SYSLOG_FACILITY=16
|
||||
```
|
||||
|
||||
3. **Rebuild and restart the container:**
|
||||
```bash
|
||||
cd /opt/mythic
|
||||
sudo ./mythic-cli build cazalla
|
||||
sudo ./mythic-cli restart cazalla
|
||||
```
|
||||
|
||||
### Step 2: Configure rsyslog on the Host (Optional)
|
||||
|
||||
If you want to receive logs on the same server where Mythic runs:
|
||||
|
||||
1. **Install rsyslog** (if not installed):
|
||||
```bash
|
||||
sudo apt-get update && sudo apt-get install -y rsyslog
|
||||
```
|
||||
|
||||
2. **Enable UDP reception** in `/etc/rsyslog.conf`:
|
||||
```bash
|
||||
sudo sed -i 's/#module(load="imudp")/module(load="imudp")/' /etc/rsyslog.conf
|
||||
sudo sed -i 's/#input(type="imudp" port="514")/input(type="imudp" port="514")/' /etc/rsyslog.conf
|
||||
```
|
||||
|
||||
3. **Create rule for Cazalla** in `/etc/rsyslog.d/50-cazalla.conf`:
|
||||
```bash
|
||||
sudo bash -c 'cat > /etc/rsyslog.d/50-cazalla.conf << EOF
|
||||
# Logs from Cazalla Mythic agent
|
||||
# Filter by facility LOCAL0 (16)
|
||||
if \$syslogfacility-text == "local0" then /var/log/cazalla.log
|
||||
& stop
|
||||
EOF
|
||||
'
|
||||
```
|
||||
|
||||
4. **Restart rsyslog:**
|
||||
```bash
|
||||
sudo systemctl restart rsyslog
|
||||
sudo systemctl enable rsyslog
|
||||
```
|
||||
|
||||
5. **Verify it's listening:**
|
||||
```bash
|
||||
sudo netstat -ulnp | grep 514
|
||||
# You should see: UNCONN ... 0.0.0.0:514 ... rsyslogd
|
||||
```
|
||||
|
||||
## 📊 Verification
|
||||
|
||||
### Verify Logger is Active
|
||||
|
||||
```bash
|
||||
# View container logs
|
||||
sudo docker logs cazalla | grep -i "syslog\|Logger initialized"
|
||||
|
||||
# You should see:
|
||||
# INFO:cazalla_logger:Syslog client initialized: 10.8.20.50:514 (facility=16)
|
||||
# INFO:cazalla_logger:Logger initialized: cazalla_logger
|
||||
```
|
||||
|
||||
### View Logs in Real-Time
|
||||
|
||||
```bash
|
||||
# View logs in syslog (host)
|
||||
sudo tail -f /var/log/cazalla.log
|
||||
|
||||
# View local logs (inside container)
|
||||
sudo docker exec cazalla tail -f /tmp/cazalla_mythic_$(date +%Y-%m-%d).log
|
||||
```
|
||||
|
||||
### View Specific Events
|
||||
|
||||
```bash
|
||||
# View only tasks
|
||||
sudo tail -100 /var/log/cazalla.log | grep '"event_type": "TASK"'
|
||||
|
||||
# View only responses
|
||||
sudo tail -100 /var/log/cazalla.log | grep '"event_type": "RESPONSE"'
|
||||
|
||||
# Count events by type
|
||||
sudo tail -1000 /var/log/cazalla.log | grep -o '"event_type": "[^"]*"' | sort | uniq -c
|
||||
```
|
||||
|
||||
## 📝 Log Format
|
||||
|
||||
Logs are saved in structured JSON format for easy parsing:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "TASK",
|
||||
"timestamp": "2025-11-17T15:18:23.117715674Z",
|
||||
"operation_id": 1,
|
||||
"operation_name": "Operation Chimera",
|
||||
"username": "mythic_admin",
|
||||
"action": "new_task",
|
||||
"server_name": "mythic",
|
||||
"data": {
|
||||
"id": 18,
|
||||
"command_name": "shell",
|
||||
"params": "{\"command\":\"whoami\"}",
|
||||
"status": "success",
|
||||
"callback_id": 2,
|
||||
"operator_username": "mythic_admin",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Log Locations
|
||||
|
||||
- **Syslog (host)**: `/var/log/cazalla.log`
|
||||
- **Local logs (container)**: `/tmp/cazalla_mythic_YYYY-MM-DD.log`
|
||||
- One file per day
|
||||
- No rotation (all files are kept)
|
||||
- Format: `cazalla_mythic_2025-11-17.log`
|
||||
|
||||
## 🔍 Usage Examples
|
||||
|
||||
### Search for a Specific Command
|
||||
|
||||
```bash
|
||||
# Search for all 'whoami' executions
|
||||
sudo grep -i "whoami" /var/log/cazalla.log | grep '"event_type": "TASK"'
|
||||
```
|
||||
|
||||
### View Operator Activity
|
||||
|
||||
```bash
|
||||
# View all events from a user
|
||||
sudo grep '"username": "mythic_admin"' /var/log/cazalla.log
|
||||
```
|
||||
|
||||
### View Captured Credentials
|
||||
|
||||
```bash
|
||||
# View credential events
|
||||
sudo grep '"event_type": "CREDENTIAL"' /var/log/cazalla.log
|
||||
```
|
||||
|
||||
### Analyze Generated Artifacts
|
||||
|
||||
```bash
|
||||
# View all artifacts (detected IOCs)
|
||||
sudo grep '"event_type": "ARTIFACT"' /var/log/cazalla.log
|
||||
```
|
||||
|
||||
## 🛠️ Troubleshooting
|
||||
|
||||
### Logger Doesn't Appear in Mythic GUI
|
||||
|
||||
**Solution:**
|
||||
1. Verify the logger is imported in `main.py`
|
||||
2. Rebuild the container: `sudo ./mythic-cli build cazalla`
|
||||
3. Restart: `sudo ./mythic-cli restart cazalla`
|
||||
|
||||
### Don't See "Syslog client initialized"
|
||||
|
||||
**Solution:**
|
||||
1. Verify `SYSLOG_SERVER` has a value in `syslog.conf`:
|
||||
```bash
|
||||
sudo docker exec cazalla cat /Mythic/logger/syslog.conf | grep SYSLOG_SERVER
|
||||
```
|
||||
2. Rebuild the container if you just edited the file
|
||||
|
||||
### Messages Not Reaching Syslog
|
||||
|
||||
**Solution:**
|
||||
1. Verify rsyslog is listening: `sudo netstat -ulnp | grep 514`
|
||||
2. Verify connectivity from container to host
|
||||
3. Verify rsyslog configuration: `sudo cat /etc/rsyslog.d/50-cazalla.conf`
|
||||
4. Verify firewall: `sudo iptables -L -n | grep 514`
|
||||
|
||||
### Logs Are Empty
|
||||
|
||||
**Solution:**
|
||||
- Logs only appear when there's activity in Mythic
|
||||
- Execute a command on an agent to generate events
|
||||
- Verify the agent is connected and active
|
||||
|
||||
## 📚 Advanced Configuration
|
||||
|
||||
### Change Syslog Facility
|
||||
|
||||
The syslog facility determines how messages are categorized. By default, `16` (LOCAL0) is used.
|
||||
|
||||
To change the facility, edit `syslog.conf`:
|
||||
|
||||
```bash
|
||||
# Use AUTH (4) for security logs
|
||||
SYSLOG_FACILITY=4
|
||||
|
||||
# Or use LOCAL1 (17)
|
||||
SYSLOG_FACILITY=17
|
||||
```
|
||||
|
||||
Then update the rsyslog rule to filter by the new facility.
|
||||
|
||||
### Send to Multiple Syslog Servers
|
||||
|
||||
To send to multiple servers, you would need to modify the logger code or use an intermediate syslog server that forwards messages.
|
||||
|
||||
### SIEM Integration
|
||||
|
||||
The JSON formatted logs can be easily integrated with SIEMs such as:
|
||||
- Splunk
|
||||
- ELK Stack (Elasticsearch, Logstash, Kibana)
|
||||
- Graylog
|
||||
- QRadar
|
||||
|
||||
Simply configure the SIEM to read from `/var/log/cazalla.log` or from the central syslog server.
|
||||
|
||||
## 🔗 References
|
||||
|
||||
- [Logger Technical Documentation](../Payload_Type/cazalla/logger/README.md)
|
||||
- [Mythic Logging Documentation](https://docs.mythic-c2.net/customizing/3.-consuming-containers/logging)
|
||||
- [RFC 3164 - The BSD syslog Protocol](https://tools.ietf.org/html/rfc3164)
|
||||
|
||||
---
|
||||
|
||||
**Note:** The logging system is active by default. You only need to configure the syslog server if you want to centralize logs.
|
||||
Reference in New Issue
Block a user