feat(mitmproxy): Integrate threat detection with CrowdSec for auto-banning

- Change analytics addon to write threats to /data/threats.log (bind-mounted to host)
- Add CrowdSec acquisition config to read from /srv/mitmproxy/threats.log
- Add parser for mitmproxy JSON threat logs with source_ip in Meta
- Add scenarios for web attacks, scanners, SSRF, and CVE exploits
- Update RPCD to read alerts from host-visible path without lxc-attach

This enables automatic IP banning when mitmproxy detects:
- SQL injection, XSS, command injection (capacity: 3, ban: 15m)
- Path traversal, XXE, LDAP injection, Log4Shell
- Aggressive web scanning (capacity: 10, ban: 10m)
- SSRF attempts from external IPs (capacity: 5, ban: 10m)
- Known CVE exploits (immediate trigger, ban: 30m)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-31 18:49:14 +01:00
parent 88e6d04f4e
commit 7b67b0329a
5 changed files with 142 additions and 13 deletions

View File

@ -339,16 +339,16 @@ do_stop() { [ -x /etc/init.d/mitmproxy ] && /etc/init.d/mitmproxy stop >/dev/nul
do_restart() { [ -x /etc/init.d/mitmproxy ] && /etc/init.d/mitmproxy restart >/dev/null 2>&1; echo '{"success":true}'; }
get_alerts() {
# Read alerts from container's JSONL log file
# The analytics addon writes one JSON object per line to /var/log/crowdsec/secubox-mitm.log
local log_file="/var/log/crowdsec/secubox-mitm.log"
# Read alerts from host-visible JSONL log file
# The analytics addon writes to /data/threats.log inside container
# which is bind-mounted to /srv/mitmproxy/threats.log on host
local log_file="/srv/mitmproxy/threats.log"
local max_alerts=50
local alerts_json="[]"
# Try to get last N alerts from LXC container and convert JSONL to JSON array
if command -v lxc-attach >/dev/null 2>&1; then
# Read last N lines, wrap in JSON array
local lines=$(lxc-attach -n "$LXC_NAME" -- tail -n "$max_alerts" "$log_file" 2>/dev/null)
# Read last N lines from the host-accessible log file
if [ -f "$log_file" ]; then
local lines=$(tail -n "$max_alerts" "$log_file" 2>/dev/null)
if [ -n "$lines" ]; then
# Convert JSONL to JSON array: join lines with commas, wrap in brackets
alerts_json=$(echo "$lines" | awk '
@ -401,12 +401,11 @@ EOFJ
}
clear_alerts() {
# Clear alerts in container
if command -v lxc-attach >/dev/null 2>&1; then
lxc-attach -n "$LXC_NAME" -- sh -c 'echo "[]" > /tmp/secubox-mitm-alerts.json' 2>/dev/null
fi
# Clear the host-visible threats log file
local log_file="/srv/mitmproxy/threats.log"
> "$log_file" 2>/dev/null
# Also clear on host
# Also clear the legacy alerts file
echo "[]" > /tmp/secubox-mitm-alerts.json 2>/dev/null
echo '{"success":true,"message":"Alerts cleared"}'

View File

@ -0,0 +1,10 @@
# CrowdSec acquisition for mitmproxy threat logs
# Monitors threats detected by SecuBox mitmproxy analytics addon
# The analytics addon runs inside LXC container and writes to /data/threats.log
# which is bind-mounted to /srv/mitmproxy/threats.log on the host
source: file
filenames:
- /srv/mitmproxy/threats.log
labels:
type: mitmproxy

View File

@ -0,0 +1,53 @@
# CrowdSec parser for SecuBox mitmproxy threat logs
# Parses JSON threat events from mitmproxy analytics addon
onsuccess: next_stage
name: secubox/mitmproxy-threats
description: "Parse SecuBox mitmproxy threat detection logs (JSON)"
filter: "evt.Line.Labels.type == 'mitmproxy'"
statics:
- parsed: source_ip
expression: JsonExtract(evt.Line.Raw, "source_ip")
- parsed: timestamp
expression: JsonExtract(evt.Line.Raw, "timestamp")
- parsed: request
expression: JsonExtract(evt.Line.Raw, "request")
- parsed: host
expression: JsonExtract(evt.Line.Raw, "host")
- parsed: user_agent
expression: JsonExtract(evt.Line.Raw, "user_agent")
- parsed: threat_type
expression: JsonExtract(evt.Line.Raw, "type")
- parsed: pattern
expression: JsonExtract(evt.Line.Raw, "pattern")
- parsed: category
expression: JsonExtract(evt.Line.Raw, "category")
- parsed: severity
expression: JsonExtract(evt.Line.Raw, "severity")
- parsed: cve
expression: JsonExtract(evt.Line.Raw, "cve")
- parsed: response_code
expression: JsonExtract(evt.Line.Raw, "response_code")
- parsed: is_bot
expression: JsonExtract(evt.Line.Raw, "is_bot")
- parsed: country
expression: JsonExtract(evt.Line.Raw, "country")
- meta: log_type
value: mitmproxy_threat
- meta: service
value: mitmproxy
- meta: source_ip
expression: JsonExtract(evt.Line.Raw, "source_ip")
---
# Filter for critical/high severity threats only (to avoid noise)
onsuccess: next_stage
name: secubox/mitmproxy-high-severity
description: "Filter high severity mitmproxy threats for banning"
filter: "evt.Meta.log_type == 'mitmproxy_threat' && evt.Parsed.severity in ['critical', 'high']"
statics:
- meta: threat_severity
expression: evt.Parsed.severity
- meta: threat_type
expression: evt.Parsed.threat_type
- meta: attack_pattern
expression: evt.Parsed.pattern

View File

@ -0,0 +1,65 @@
# CrowdSec scenario for SecuBox mitmproxy threat detection
# Triggers bans for detected attacks (SQLi, XSS, command injection, etc.)
type: leaky
name: secubox/mitmproxy-attack
description: "Detect web attacks via mitmproxy (SQLi, XSS, command injection, SSRF)"
filter: |
evt.Meta.log_type == 'mitmproxy_threat' &&
evt.Parsed.severity in ['critical', 'high'] &&
evt.Parsed.pattern in ['sql_injection', 'xss', 'command_injection', 'path_traversal', 'xxe', 'ldap_injection', 'log4shell']
groupby: evt.Meta.source_ip
capacity: 3
leakspeed: 60s
blackhole: 15m
labels:
service: mitmproxy
type: web_attack
remediation: true
---
# Detect aggressive scanning/probing
type: leaky
name: secubox/mitmproxy-scanner
description: "Detect aggressive web scanning via mitmproxy"
filter: |
evt.Meta.log_type == 'mitmproxy_threat' &&
evt.Parsed.pattern in ['admin_scanner', 'config_scan', 'backup_scan', 'env_scan']
groupby: evt.Meta.source_ip
capacity: 10
leakspeed: 30s
blackhole: 10m
labels:
service: mitmproxy
type: web_scan
remediation: true
---
# Detect SSRF attempts (more lenient - internal IPs might be legitimate)
type: leaky
name: secubox/mitmproxy-ssrf
description: "Detect SSRF attempts via mitmproxy"
filter: |
evt.Meta.log_type == 'mitmproxy_threat' &&
evt.Parsed.pattern == 'ssrf' &&
evt.Parsed.country != 'LOCAL'
groupby: evt.Meta.source_ip
capacity: 5
leakspeed: 60s
blackhole: 10m
labels:
service: mitmproxy
type: ssrf
remediation: true
---
# Detect known CVE exploitation attempts (immediate ban)
type: trigger
name: secubox/mitmproxy-cve
description: "Detect CVE exploitation attempts via mitmproxy"
filter: |
evt.Meta.log_type == 'mitmproxy_threat' &&
evt.Parsed.cve != '' &&
evt.Parsed.severity == 'critical'
blackhole: 30m
labels:
service: mitmproxy
type: cve_exploit
remediation: true

View File

@ -19,7 +19,9 @@ from pathlib import Path
# GeoIP database path (MaxMind GeoLite2)
GEOIP_DB = "/srv/mitmproxy/GeoLite2-Country.mmdb"
LOG_FILE = "/var/log/secubox-access.log"
CROWDSEC_LOG = "/var/log/crowdsec/secubox-mitm.log"
# CrowdSec log - uses /data which is bind-mounted to /srv/mitmproxy on host
# This allows CrowdSec on the host to read threat logs from the container
CROWDSEC_LOG = "/data/threats.log"
ALERTS_FILE = "/tmp/secubox-mitm-alerts.json"
STATS_FILE = "/tmp/secubox-mitm-stats.json"