diff --git a/.tests/postfix-sasl-bf/config.yaml b/.tests/postfix-sasl-bf/config.yaml new file mode 100644 index 00000000000..b488aaf3b39 --- /dev/null +++ b/.tests/postfix-sasl-bf/config.yaml @@ -0,0 +1,11 @@ +parsers: +- crowdsecurity/postfix-logs +- crowdsecurity/syslog-logs +- crowdsecurity/dateparse-enrich +scenarios: +- Guezli/postfix-sasl-bf +postoverflows: +- "" +log_file: postfix-sasl-bf.log +log_type: syslog +ignore_parsers: true diff --git a/.tests/postfix-sasl-bf/parser.assert b/.tests/postfix-sasl-bf/parser.assert new file mode 100644 index 00000000000..e69de29bb2d diff --git a/.tests/postfix-sasl-bf/postfix-sasl-bf.log b/.tests/postfix-sasl-bf/postfix-sasl-bf.log new file mode 100644 index 00000000000..8fc4dc48857 --- /dev/null +++ b/.tests/postfix-sasl-bf/postfix-sasl-bf.log @@ -0,0 +1,3 @@ +May 11 04:02:33 host1 postfix/smtpd[26897]: warning: unknown[1.2.3.4]: SASL LOGIN authentication failed: authentication failure +May 11 04:02:34 host1 postfix/smtpd[26897]: warning: unknown[1.2.3.4]: SASL LOGIN authentication failed: authentication failure +May 11 04:02:35 host1 postfix/smtpd[26897]: warning: unknown[1.2.3.4]: SASL LOGIN authentication failed: authentication failure diff --git a/.tests/postfix-sasl-bf/scenario.assert b/.tests/postfix-sasl-bf/scenario.assert new file mode 100644 index 00000000000..6bceeae94fc --- /dev/null +++ b/.tests/postfix-sasl-bf/scenario.assert @@ -0,0 +1,14 @@ +len(results) == 1 +"1.2.3.4" in results[0].Overflow.GetSources() +results[0].Overflow.Sources["1.2.3.4"].IP == "1.2.3.4" +results[0].Overflow.Sources["1.2.3.4"].GetScope() == "Ip" +results[0].Overflow.Sources["1.2.3.4"].GetValue() == "1.2.3.4" +results[0].Overflow.Alert.Events[0].GetMeta("datasource_path") == "postfix-sasl-bf.log" +results[0].Overflow.Alert.Events[0].GetMeta("datasource_type") == "file" +results[0].Overflow.Alert.Events[0].GetMeta("log_type") == "postfix" +results[0].Overflow.Alert.Events[0].GetMeta("log_type_enh") == "spam-attempt" +results[0].Overflow.Alert.Events[0].GetMeta("service") == "postfix" +results[0].Overflow.Alert.Events[0].GetMeta("source_hostname") == "unknown" +results[0].Overflow.Alert.Events[0].GetMeta("source_ip") == "1.2.3.4" +results[0].Overflow.Alert.GetScenario() == "Guezli/postfix-sasl-bf" +results[0].Overflow.Alert.Remediation == true diff --git a/scenarios/Guezli/postfix-sasl-bf.md b/scenarios/Guezli/postfix-sasl-bf.md new file mode 100644 index 00000000000..87b7d5fa17a --- /dev/null +++ b/scenarios/Guezli/postfix-sasl-bf.md @@ -0,0 +1,47 @@ +## Postfix SASL slow / distributed bruteforce + +Detects slow or distributed SASL LOGIN bruteforce attempts against postfix. + +The official `crowdsecurity/postfix-spam` scenario is tuned for fast spam waves +(`capacity: 5`, `leakspeed: 10s`). This scenario covers the opposite threat +model: distributed attackers each performing 1-2 SASL login attempts per hour +across many IPs in a /24, where the fast-pattern bucket leaks faster than it +fills. + +Threshold: **3 SASL LOGIN failures from the same IP within ~2h** -> ban. + +### Requirements + +- `crowdsecurity/postfix-logs` parser (part of the `crowdsecurity/postfix` + collection) + +### Acquisition example + +For Mailcow's postfix container: + +```yaml +source: docker +container_name: + - mailcowdockerized-postfix-mailcow-1 +labels: + type: syslog +``` + +For a standalone postfix with file-based syslog: + +```yaml +filenames: + - /var/log/mail.log +labels: + type: syslog +``` + +### Notes + +- The `behavior` label is `pop3/imap:bruteforce` because the hub taxonomy has + no dedicated `smtp:bruteforce` entry. This follows the precedent set by + `hitech95/email-generic-bf`. +- Companion to `crowdsecurity/postfix-spam`, which catches fast-pattern spam + waves; this one fills the slow / distributed gap. +- Tuning notes, installer and detailed background: + https://github.com/Guezli/postfix-sasl-bf diff --git a/scenarios/Guezli/postfix-sasl-bf.yaml b/scenarios/Guezli/postfix-sasl-bf.yaml new file mode 100644 index 00000000000..08c4b6de3bc --- /dev/null +++ b/scenarios/Guezli/postfix-sasl-bf.yaml @@ -0,0 +1,39 @@ +# Detect slow / distributed SASL LOGIN bruteforce against postfix. +# +# The official `crowdsecurity/postfix-spam` scenario is tuned for fast +# spam waves (capacity 5 / leak 10s). It misses distributed bruteforces +# where many IPs each try only a couple of times per hour, because the +# bucket leaks faster than it fills. +# +# This scenario uses a much slower leak so a single IP that fails +# 3+ SASL LOGINs within ~2h triggers a ban. +# +# Requires the official `crowdsecurity/postfix-logs` parser, which +# already extracts source_ip from lines like: +# "warning: unknown[]: SASL LOGIN authentication failed: ..." + +type: leaky +name: Guezli/postfix-sasl-bf +description: "Detect slow / distributed SASL bruteforce against postfix" +filter: | + evt.Meta.log_type == 'postfix' + && evt.Parsed.message contains 'SASL' + && evt.Parsed.message contains 'authentication failed' +groupby: evt.Meta.source_ip +# bucket holds 2 tokens, leaks 1 token every 2h +# => 3 fails within ~2h overflow the bucket +capacity: 2 +leakspeed: 7200s +blackhole: 2h +labels: + service: postfix + remediation: true + confidence: 3 + spoofable: 0 + classification: + - attack.T1110 + behavior: "pop3/imap:bruteforce" + label: "Postfix SASL Bruteforce" +references: + - https://www.postfix.org/SASL_README.html + - https://hub.crowdsec.net/author/crowdsecurity/collections/postfix