-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathmaltrail.conf
More file actions
388 lines (306 loc) · 20.6 KB
/
Copy pathmaltrail.conf
File metadata and controls
388 lines (306 loc) · 20.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# [Server]
# Listen address of (reporting) HTTP server
HTTP_ADDRESS 0.0.0.0
#HTTP_ADDRESS ::
#HTTP_ADDRESS fe80::12c3:7bff:fe6d:cf9b%eno1
# Listen port of (reporting) HTTP server
HTTP_PORT 8338
# Optional location of THIS monitored network (decimal degrees). If set, the reporting UI's attack map draws arcs
# from each source country to this "home" point (air-gapped installs can't auto-locate, hence it's manual/opt-in)
#HOME_LAT 45.8150
#HOME_LON 15.9819
# The reporting UI decorates public IPs with a country flag and an ASN tooltip, which the server
# looks up at RIPEstat (stat.ripe.net) through its /ripe endpoint and caches. Set this on a server
# that must make no outbound requests: /ripe then answers immediately with nothing, and flags fall
# back to the local RIR table shipped with Maltrail. (The lookup used to be done by the browser
# instead, which is why the reporting UI's CSP once had to allow scripts from stat.ripe.net.)
#DISABLE_RIPE_LOOKUPS false
# Use SSL/TLS
USE_SSL false
# SSL/TLS (private key + certificate) PEM file. Generate your own; Maltrail deliberately ships
# no PEM, because a private key in a public repository is a private key everybody already has:
# openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes -subj '/O=Maltrail CA/C=EU'
#SSL_PEM /etc/maltrail/server.pem
# User entries (username:sha256(password):UID:filter_netmask(s))
# Note(s): sha256(password) can be generated on Linux with: echo -n 'password' | sha256sum | cut -d " " -f 1
# UID >= 1000 have only rights to display results (Note: this moment only functionality implemented at the client side)
# filter_netmask(s) is/are used to filter results
USERS
admin:9ab3cd9d67bf49d01f6a2e33d0bd9bc804ddbe6ce1ff5d219c42624851db5dbc:0: # changeme!
# local:9ab3cd9d67bf49d01f6a2e33d0bd9bc804ddbe6ce1ff5d219c42624851db5dbc:1000:192.168.0.0/16 # changeme!
# Mask custom trail names for non-admin users (UID >= 1000)
ENABLE_MASK_CUSTOM true
# Listen address of (log collecting) UDP server
#UDP_ADDRESS 0.0.0.0
#UDP_ADDRESS ::
#UDP_ADDRESS fe80::12c3:7bff:fe6d:cf9b%eno1
# Listen port of (log collecting) UDP server
#UDP_PORT 8337
# Should server do the trail updates too (to support UPDATE_SERVER directive in [Sensor] parameters)
USE_SERVER_UPDATE_TRAILS false
# Concurrent HTTP request handlers. Connections beyond this get 503 rather than a thread, so a
# flood costs the server a bounded amount of memory instead of an unbounded one. Each handler
# reserves 1MB of stack, so this is also roughly the interface's worst-case thread memory in MB.
#MAX_REQUEST_THREADS 100
# How many of those handlers may be held open by live event streams (/live) at once - one per
# dashboard tab with Live on. Kept below MAX_REQUEST_THREADS so streams cannot starve ordinary
# requests. A refused stream falls back to polling in the browser, so nothing breaks; raise it for
# a console with many simultaneous viewers.
#MAX_LIVE_STREAMS 30
# Seconds a retro-hunt (/hunt) may spend scanning before it returns what it has. A day the budget
# cuts short is reported separately and is NOT counted as a complete day. Raise it when event logs
# live on slow or network storage.
#HUNT_TIME_BUDGET 10.0
# Aliases used in client's web browser interface to describe the src_ip and/or dst_ip column entries
#IP_ALIASES
# 8.8.8.8:google
# 8.8.4.4:google
# Option to change the top-left logo with a custom image/text
#HEADER_LOGO <img src="https://i.imgur.com/JxYJ5yX.png" style="width: 26px"> XYZ
# Regular expression to be used in external /fail2ban calls for extraction of attacker source IPs
FAIL2BAN_REGEX attacker|reputation|potential[^"]*(web scan|directory traversal|injection|remote code|iot-malware download)|spammer|mass scanner
# Only allow /fail2ban endpoint from these IPv4 sources
# If unset/empty, /fail2ban will be blocked
FAIL2BAN_ALLOWLIST 127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
# Who may pull /blacklist (same syntax as FAIL2BAN_ALLOWLIST). Unset -> FAIL2BAN_ALLOWLIST is
# used; if that is unset too, /blacklist is blocked. An authenticated session is always allowed,
# so this only governs unauthenticated pullers such as firewall automation.
#BLACKLIST_ALLOWLIST 127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
# To take a remediated host off /blacklist and /fail2ban WITHOUT whitelisting it forever, list it
# in LOG_DIR/cleared.txt - one per line, optionally with the moment it was cleared:
# 10.13.13.37 (cleared as of that file's last modification)
# 10.13.13.99 2026-08-11 09:30:00
# Earlier events for that host are then ignored; any NEW detection puts it straight back, which a
# whitelist entry would not.
# Blacklist generation rules
# BLACKLIST
# src_ip !~ ^192.168. and dst_port ~ ^22$
# src_ip ~ ^192.168. and filter ~ malware
# [Sensor]
# Number of processes (old sensor). The current sensor captures with ONE worker by default -
# fast enough for ~1.1M packets/s - and does not derive its worker count from this value: the
# kernel flow-hashes fanout by connection while the scan heuristics count by source, so extra
# workers each see only a fraction of a scan. Set CAPTURE_FANOUT (below) to scale out explicitly.
PROCESS_COUNT 16
# Expose Prometheus metrics on this address (sensor only; unset = disabled)
# Alert on 'maltrail_capture_dropped_total' (a non-zero rate means missed detections) and on
# 'maltrail_trail_generation' failing to advance (trails have stopped refreshing)
#STATS_ADDRESS 127.0.0.1:9114
# Disable setting of CPU affinity (with schedtool) on Linux machines (e.g. because of load issues with other processes)
DISABLE_CPU_AFFINITY false
# Use feeds (too) in trail updates
USE_FEED_UPDATES true
# Disable (retrieval from) specified feeds (Note: respective .py files inside /trails/feeds; turris and ciarmy/cinsscore seem to be too "noisy" lately; policeman is old and produces lots of false positives)
DISABLED_FEEDS turris, ciarmy, policeman, myip, alienvault
# Ignore IPs that appear on lower than IP_MINIMUM_FEEDS number of feeds (Note: static IP trails are always included)
IP_MINIMUM_FEEDS 3
# Disable trails based on the following regular expression run against the corresponding info
#DISABLED_TRAILS_INFO_REGEX known attacker|tor exit node
# Update trails after every given period (seconds)
UPDATE_PERIOD 86400
# Use remote custom feed (too) in trail updates
#CUSTOM_TRAILS_URL http://www.test.com/custom.txt
# Location of directory with custom trails (*.txt) files
CUSTOM_TRAILS_DIR ./trails/custom
# (Max.) size of multiprocessing network capture ring buffer (in bytes or percentage of total physical memory) used by sensor (e.g. 512MB)
# Note: on the Rust sensor this seeds CAPTURE_BUFFER_SIZE (the libpcap ring) when that is not set
# explicitly, capped at 256MB per worker. See the [Rust sensor tuning] section.
CAPTURE_BUFFER 10%
# Interface used for monitoring (e.g. eth0, eth1)
MONITOR_INTERFACE any
# Network capture filter (e.g. ip)
# Note(s): more info about filters can be found at: https://danielmiessler.com/study/tcpdump/
#CAPTURE_FILTER ip or ip6
CAPTURE_FILTER udp or icmp or (tcp and (tcp[tcpflags] == tcp-syn or port 80 or port 1080 or port 3128 or port 8000 or port 8080 or port 8118 or (port 443 and tcp[((tcp[12]&0xf0)>>2)] = 0x16)))
# Fast in-C prefilter (requires pcapy-ng with loop_filtered). When enabled, packet
# classification + admission happens in C and provably-inert noise is dropped before it ever
# crosses into Python, relieving the single capture thread on high-rate, noise-heavy pipes.
# Detection is unchanged (sound superset: DNS/HTTP/SYN/IP-trail/ICMP all preserved) and it
# additionally surfaces SNI domains on encrypted TLS/QUIC traffic. Off by default; when off (or
# if the installed pcapy lacks loop_filtered) the classic capture path is used unchanged.
#USE_FAST_PREFILTER true
# Capture the first N packets per flow on TLS/QUIC ports so the handshake (ClientHello / QUIC
# Initial) is delivered for SNI extraction. 0 disables SNI-head capture (pure throughput).
#FAST_FLOW_CUTOFF 4
# Severity admission level for the fast prefilter: 0 normal (admit all but inert noise),
# 1 busy (drop SYN heuristics), 2 strained (also drop TLS/QUIC heads), 3 overload (DNS + known-bad
# IP only). DNS and confirmed-bad-IP traffic are ALWAYS admitted at every level.
#FAST_ADMIT_LEVEL 0
# Auto-tune FAST_ADMIT_LEVEL from the live capture drop-rate (live interfaces only): shed
# low-priority classes only while the kernel/libpcap ring is actually dropping, and restore them
# when it recovers. DNS + known-bad-IP stay at 100% throughout.
#FAST_ADMIT_ADAPTIVE true
# Match TLS server certificates against the trail set by SHA-1 fingerprint (sensor only). The
# fingerprint outlives a C2's address and domain, so it keeps matching after those rotate; feeds
# such as abuse.ch SSLBL publish ~10,000 of them. Findings are rated 'suspicious' (medium), not
# malware, because a few listings are dual-use remote-administration tooling.
#
# The CAPTURE_FILTER below admits TLS HANDSHAKE records on 443 for this
# (tcp[((tcp[12]&0xf0)>>2)] = 0x16 tests the TLS content type at the start of the TCP payload).
# That is handshakes only, never bulk TLS, so the added volume is a few packets per connection.
# Remove that clause from the filter to opt out of the capture cost entirely.
#
# TLS 1.3 encrypts the certificate, so this matches 1.2 and below - which is what the implants
# these fingerprints identify actually negotiate.
#CHECK_TLS_CERTIFICATES true
# Capture fan-out (Linux only; requires pcapy-ng with set_fanout/PACKET_FANOUT). Opens N capture
# sockets per live interface joined to one kernel PACKET_FANOUT group, so the kernel flow-hashes
# the interface's traffic across N capture threads instead of a single one - the way to scale
# capture past one thread on a fat pipe. Each flow stays on one socket (no duplicate capture).
# Unset/1 = off (single socket); an integer = that many sockets; 'true'/'auto' = one per CPU core.
# Falls back to a single socket (with a logged note) if the kernel/pcapy lacks PACKET_FANOUT.
#
# COST, measured (sensor/tests/multi_worker_parity.rs): of the scan-heuristic alerts one worker
# raises, 91% survive at 2 sockets, 86% at 4, 65% at 8, because a scan's flows are spread across
# them. Exact trail detection is unaffected at any count. Only raise this if one worker cannot
# keep up - watch 'maltrail_capture_dropped_total' rather than guessing.
#CAPTURE_FANOUT auto
# Condensed observable store (LOG_DIR/meta.sqlite): one cumulative row per domain/IP ever seen,
# with first_seen/last_seen/count. Feeds the server's novelty and retro-hunt views. ON by default;
# turn it off on a very high-rate link if you do not use those views.
#USE_CONDENSED_STORAGE true
# --- Rust sensor tuning -------------------------------------------------------------------
# All optional, all with working defaults - listed here so they are discoverable rather than only
# documented in sensor/docs/. The Python sensor in old/ ignores them.
# Capture sockets/threads. Default 1, and NOT derived from PROCESS_COUNT: the kernel flow-hashes
# fanout while the scan heuristics count per source, so extra workers each see a fraction of a
# scan. Raise only if 'maltrail_capture_dropped_total' climbs. (See CAPTURE_FANOUT above.)
#CAPTURE_WORKERS 1
# libpcap ring per capture socket - how much traffic can burst in before the KERNEL drops it,
# and a dropped packet is never seen by any detection. Defaults to 64MB, or to CAPTURE_BUFFER
# when that is larger (capped at 256MB, because CAPTURE_BUFFER ships as a percentage of RAM
# and this is locked kernel memory PER WORKER). Set it explicitly to go higher, up to 1GB.
# Raise it when maltrail_capture_dropped_total climbs; 'maltrail-sensor -T' prints the total
# across all workers.
#CAPTURE_BUFFER_SIZE 64MB
# Bytes captured per packet (default 2000) and how long libpcap waits before returning a batch,
# in milliseconds (default 100). CAPTURE_IMMEDIATE delivers each packet as it arrives - lower
# latency, higher syscall cost.
#CAPTURE_SNAPLEN 2000
#CAPTURE_TIMEOUT 100
#CAPTURE_IMMEDIATE false
# PACKET_FANOUT details: distribution mode (hash|lb|cpu|rollover|random|qm, default hash),
# kernel-side defragmentation, and a fixed group id when several sensors share one interface.
#CAPTURE_FANOUT_MODE hash
#CAPTURE_FANOUT_DEFRAG false
#CAPTURE_FANOUT_GROUP 41234
# Timestamp source for '-r' replay: 'pcap' (default, the record's own time) or 'wallclock'
# (what sensor.py does on Python 3; needed only for strict differential parity runs).
#OFFLINE_TIMESTAMPS pcap
# Seconds between the sensor's own metrics lines (default 3600). See also STATS_ADDRESS.
#METRICS_INTERVAL 3600
# Event-log throttling. 'summarize' (default) writes EVENT_THROTTLE_BURST events per (ip, trail)
# per EVENT_THROTTLE_WINDOW seconds and then ONE aggregated line, so nothing is silently dropped;
# 'legacy' reproduces core/log.py's bucket byte for byte; 'off' disables throttling entirely.
# EVENT_THROTTLE_MAX_KEYS bounds the tracking table. At the cap the least-recently-seen keys are
# evicted to make room, which flushes their summaries EARLY - nothing is lost, but those events
# are aggregated sooner than EVENT_THROTTLE_WINDOW. Watch maltrail_throttle_evictions_total; a
# sustained rate means the table is too small for the number of distinct (source, trail) pairs.
#EVENT_THROTTLE_MODE summarize
#EVENT_THROTTLE_WINDOW 60
#EVENT_THROTTLE_BURST 3
#EVENT_THROTTLE_MAX_KEYS 50000
# Per-worker domain result cache (default 1000). Purely a cache - the size changes how often a
# verdict is recomputed, never the verdict. Raising it measured SLOWER under a DGA flood.
#DOMAIN_CACHE_ENTRIES 1000
# Opt out of the sensor's own trail refresh, for hosts where trails.csv is delivered externally.
# Off by default, i.e. the sensor refreshes trails exactly as sensor.py does.
#DISABLE_TRAIL_UPDATES false
# Salvage wildcard trail patterns that arrived truncated in a feed instead of dropping them.
# On by default; set false for byte-exact wildcard parity with sensor.py.
#REPAIR_TRUNCATED_TRAILS true
# Sensor name to appear in produced logs
SENSOR_NAME $HOSTNAME
# Remote Maltrail server instance to send log entries (Note: listening at <UDP_ADDRESS>:<UDP_PORT>)
#LOG_SERVER 192.168.2.107:8337
#LOG_SERVER [fe80::12c3:7bff:fe6d:cf9b%eno1]:8337
# Remote address to send Syslog events. Used by the sensor for detections, and by the server for
# login attempts ("Accepted"/"Failed password for <user> from <ip> port <port>"), so brute force
# against the reporting interface is visible to a SIEM and not only in the local auth log.
# Several may be given, separated by commas, to feed redundant collectors:
# SYSLOG_SERVER host1:port, host2:port
#SYSLOG_SERVER 192.168.2.107:514
# Remote address to send JSON events (e.g. Logstash)
# Several may be given, separated by commas, to feed redundant collectors:
# LOGSTASH_SERVER host1:port, host2:port
#LOGSTASH_SERVER 192.168.2.107:5000
# Regular expression used for calculating severity attribute when sending events to SYSLOG_SERVER or LOGSTASH_SERVER
REMOTE_SEVERITY_REGEX (?P<high>(remote )?custom\)|malwaredomainlist|iot-malware|malware(?! (distribution|site))|adversary|ransomware)|(?P<medium>potential malware site|malware distribution|c2 cert)|(?P<low>mass scanner|reputation|attacker|spammer|compromised|crawler|scanning)
# HTTP POST an alert for each matching event (empty = off). The server tails its event log, so this
# covers events from local and remote sensors alike. Any endpoint that accepts a POST will do: a
# Slack or Mattermost incoming webhook, a Discord webhook, a SIEM collector, your own script.
# NOTE: a literal Slack webhook URL here - even one made of placeholders - is rejected by GitHub's
# push protection as a leaked secret, so the example is deliberately generic.
#ALERT_WEBHOOK_URL https://alerts.example.com/maltrail
# Minimum severity worth a message, as calculated by REMOTE_SEVERITY_REGEX above:
# high named malware, ransomware, adversary, custom trails - "one of our hosts contacted known
# malware", which is the page-someone event
# medium also exploit kits, web skimmers, browser lockers, C2 frameworks
# low also inbound scan noise (attacker, reputation, spammer, crawler) - that is what
# FAIL2BAN_REGEX and /fail2ban are for, and it will fire all day on a healthy host
ALERT_SEVERITY high
# The request body, as a Python format string - there is no webhook standard, and you know your
# receiver. Available: %(timestamp)s %(sensor)s %(severity)s %(src_ip)s %(src_port)s %(dst_ip)s
# %(dst_port)s %(proto)s %(type)s %(trail)s %(info)s %(reference)s and %(json)s, which is the exact
# payload LOGSTASH_SERVER sends.
# Discord: {"content": "..."}
# SIEM / a script: %(json)s
ALERT_FORMAT {"text": "%(severity)s: %(src_ip)s -> %(trail)s (%(info)s) [%(type)s]"}
# Seconds to suppress a repeat of the same (source, trail) pair, so a beacon checking in every 30
# seconds is one message per interval rather than 2,880 a day. 0 disables the suppression.
ALERT_THROTTLE 300
# Set only (!) in cases when LOG_SERVER should be exclusively used for log storage
DISABLE_LOCAL_LOG_STORAGE false
# Remote address for pulling (latest) trail definitions (e.g. http://192.168.2.107:8338/trails). USE_SERVER_UPDATE_TRAILS directive should be active in [Server] parameters.
#UPDATE_SERVER http://192.168.2.107:8338/trails
# Use heuristic methods
USE_HEURISTICS true
# Sliding-window length (seconds) for the scan heuristics (port/web/infection scan). State is kept
# for this long instead of being cleared every ~1s, so SLOW scans (probes spread out to dodge a
# 1-second bucket, e.g. nmap -T2/-T3) are still caught. Longer = catches slower scans but retains
# more state (bounded internally). Default 30. (Stealth NULL/FIN/XMAS scans are always covered.)
#SCAN_WINDOW 30
# Source-affinity worker routing (multiprocessing only). Pins all packets from a source IP to one
# worker so the cross-packet scan heuristics (port/web/infection scan, DNS exhaustion) keep COMPLETE
# state on that worker. Without it, the round-robin worker pool splits a scan's packets across
# workers, so detection needs a scan ~PROCESS_COUNT x larger to trip and emits one duplicate event
# per worker. Trade-off: a few heavy source IPs can unbalance the pool (skip-pad write amplification).
# Off by default (round-robin). Recommended when PROCESS_COUNT is high and scan detection matters.
#USE_CAPTURE_AFFINITY true
# Mute individual (noisy / low-value) heuristics without turning USE_HEURISTICS off entirely.
# Comma-separated names: port_scanning, udp_scanning, infection, web_scanning, dns_exhaustion, long_domain.
# Unset = all enabled. E.g. silence the chattiest ones: DISABLED_HEURISTICS port_scanning, dns_exhaustion
#DISABLED_HEURISTICS port_scanning, dns_exhaustion
# Capture HTTP requests with missing Host header (introducing potential false positives)
CHECK_MISSING_HOST false
# Check values in Host header (along with standard non-HTTP checks) for malicious DNS trails (introducing greater number of events)
CHECK_HOST_DOMAINS false
# Location of file with whitelisted entries (i.e. IP addresses, domain names, etc.) (note: take a look into 'data/whitelist.txt')
#USER_WHITELIST
# Location of file with ignore event rules. Example under data/ignore_events.txt
#USER_IGNORELIST data/ignore_events.txt
# Regular expression to be used against the whole event entry to be ignored
#IGNORE_EVENTS_REGEX sql injection|long domain|117.21.225.3|sinkhole
# [All]
# Show debug messages (in console output)
SHOW_DEBUG false
# Directory used for log storage
LOG_DIR $SYSTEM_LOG_DIR/maltrail
# HTTP(s) proxy address
#PROXY_ADDRESS http://192.168.5.101:8118
# Disable checking of sudo/Administrator privileges (e.g. if using: setcap 'CAP_NET_RAW+eip CAP_NET_ADMIN+eip' /bin/python)
#DISABLE_CHECK_SUDO true
# Override default location for trail storage (~/.maltrail/trails.csv)
# Note: for a service install use a path the service user owns, e.g. the systemd unit's
# StateDirectory: TRAILS_FILE /var/lib/maltrail/trails.csv
#TRAILS_FILE /etc/maltrail.csv
# Refuse to start when the trail set is empty (a sensor with no trails detects nothing while
# reporting itself healthy). Set true only where an empty set is genuinely expected
#ALLOW_EMPTY_TRAILS false
# Smallest fraction of the current trail count a RELOAD may produce and still be accepted, so a
# truncated or half-written trails.csv cannot silently replace a good set. 0 disables the check
#TRAIL_RELOAD_MIN_RATIO 0.5
# Bounds on the per-worker capture ring, packet snap length and capture poll timeout are
# enforced at load: out-of-range values are clamped and reported by 'maltrail-sensor -T', which
# also prints the EFFECTIVE configuration and the total capture-ring memory across all workers
# (CAPTURE_BUFFER is per worker, so N workers want N times that much)