diff --git a/.tests/bitwarden-slow-bf/bitwarden-slow-bf.log b/.tests/bitwarden-slow-bf/bitwarden-slow-bf.log new file mode 100644 index 00000000000..5276edcdfa8 --- /dev/null +++ b/.tests/bitwarden-slow-bf/bitwarden-slow-bf.log @@ -0,0 +1,7 @@ +2023-04-24 10:00:00.000 -05:00 [WRN] Failed login attempt. 192.168.1.100 +2023-04-24 10:03:00.000 -05:00 [WRN] Failed login attempt. 192.168.1.100 +2023-04-24 10:06:00.000 -05:00 [WRN] Failed login attempt. 192.168.1.100 +2023-04-24 10:20:00.000 -05:00 [WRN] Failed login attempt. 192.168.1.101 +2023-04-24 10:40:00.000 -05:00 [WRN] Failed login attempt. 192.168.1.101 +2023-04-24 11:00:00.000 -05:00 [WRN] Failed login attempt. 192.168.1.101 + diff --git a/.tests/bitwarden-slow-bf/config.yaml b/.tests/bitwarden-slow-bf/config.yaml new file mode 100644 index 00000000000..caafe942965 --- /dev/null +++ b/.tests/bitwarden-slow-bf/config.yaml @@ -0,0 +1,16 @@ +parsers: +- crowdsecurity/syslog-logs +- crowdsecurity/dateparse-enrich +- ./parsers/s01-parse/MariuszKociubinski/bitwarden-logs.yaml +scenarios: +- ./scenarios/crowdsecurity/bitwarden-slow-bf.yaml +# we put this one on purpose : we want only to trigger the slow-bf +- ./scenarios/MariuszKociubinski/bitwarden-bf.yaml +postoverflows: +- "" +log_file: bitwarden-slow-bf.log +log_type: bitwarden +labels: {} +ignore_parsers: true +override_statics: [] + diff --git a/.tests/bitwarden-slow-bf/scenario.assert b/.tests/bitwarden-slow-bf/scenario.assert new file mode 100644 index 00000000000..0fa250e8914 --- /dev/null +++ b/.tests/bitwarden-slow-bf/scenario.assert @@ -0,0 +1,53 @@ +len(results) == 2 +"192.168.1.101" in results[0].Overflow.GetSources() +results[0].Overflow.Sources["192.168.1.101"].IP == "192.168.1.101" +results[0].Overflow.Sources["192.168.1.101"].Range == "" +results[0].Overflow.Sources["192.168.1.101"].GetScope() == "Ip" +results[0].Overflow.Sources["192.168.1.101"].GetValue() == "192.168.1.101" +basename(results[0].Overflow.Alert.Events[0].GetMeta("datasource_path")) == "bitwarden-slow-bf.log" +results[0].Overflow.Alert.Events[0].GetMeta("datasource_type") == "file" +results[0].Overflow.Alert.Events[0].GetMeta("log_type") == "bitwarden_failed_auth" +results[0].Overflow.Alert.Events[0].GetMeta("service") == "bitwarden" +results[0].Overflow.Alert.Events[0].GetMeta("source_ip") == "192.168.1.101" +results[0].Overflow.Alert.Events[0].GetMeta("timestamp") == "2023-04-24T10:20:00Z" +basename(results[0].Overflow.Alert.Events[1].GetMeta("datasource_path")) == "bitwarden-slow-bf.log" +results[0].Overflow.Alert.Events[1].GetMeta("datasource_type") == "file" +results[0].Overflow.Alert.Events[1].GetMeta("log_type") == "bitwarden_failed_auth" +results[0].Overflow.Alert.Events[1].GetMeta("service") == "bitwarden" +results[0].Overflow.Alert.Events[1].GetMeta("source_ip") == "192.168.1.101" +results[0].Overflow.Alert.Events[1].GetMeta("timestamp") == "2023-04-24T10:40:00Z" +basename(results[0].Overflow.Alert.Events[2].GetMeta("datasource_path")) == "bitwarden-slow-bf.log" +results[0].Overflow.Alert.Events[2].GetMeta("datasource_type") == "file" +results[0].Overflow.Alert.Events[2].GetMeta("log_type") == "bitwarden_failed_auth" +results[0].Overflow.Alert.Events[2].GetMeta("service") == "bitwarden" +results[0].Overflow.Alert.Events[2].GetMeta("source_ip") == "192.168.1.101" +results[0].Overflow.Alert.Events[2].GetMeta("timestamp") == "2023-04-24T11:00:00Z" +results[0].Overflow.Alert.GetScenario() == "crowdsecurity/bitwarden-slow-bf" +results[0].Overflow.Alert.Remediation == true +results[0].Overflow.Alert.GetEventsCount() == 3 +"192.168.1.100" in results[1].Overflow.GetSources() +results[1].Overflow.Sources["192.168.1.100"].IP == "192.168.1.100" +results[1].Overflow.Sources["192.168.1.100"].Range == "" +results[1].Overflow.Sources["192.168.1.100"].GetScope() == "Ip" +results[1].Overflow.Sources["192.168.1.100"].GetValue() == "192.168.1.100" +basename(results[1].Overflow.Alert.Events[0].GetMeta("datasource_path")) == "bitwarden-slow-bf.log" +results[1].Overflow.Alert.Events[0].GetMeta("datasource_type") == "file" +results[1].Overflow.Alert.Events[0].GetMeta("log_type") == "bitwarden_failed_auth" +results[1].Overflow.Alert.Events[0].GetMeta("service") == "bitwarden" +results[1].Overflow.Alert.Events[0].GetMeta("source_ip") == "192.168.1.100" +results[1].Overflow.Alert.Events[0].GetMeta("timestamp") == "2023-04-24T10:00:00Z" +basename(results[1].Overflow.Alert.Events[1].GetMeta("datasource_path")) == "bitwarden-slow-bf.log" +results[1].Overflow.Alert.Events[1].GetMeta("datasource_type") == "file" +results[1].Overflow.Alert.Events[1].GetMeta("log_type") == "bitwarden_failed_auth" +results[1].Overflow.Alert.Events[1].GetMeta("service") == "bitwarden" +results[1].Overflow.Alert.Events[1].GetMeta("source_ip") == "192.168.1.100" +results[1].Overflow.Alert.Events[1].GetMeta("timestamp") == "2023-04-24T10:03:00Z" +basename(results[1].Overflow.Alert.Events[2].GetMeta("datasource_path")) == "bitwarden-slow-bf.log" +results[1].Overflow.Alert.Events[2].GetMeta("datasource_type") == "file" +results[1].Overflow.Alert.Events[2].GetMeta("log_type") == "bitwarden_failed_auth" +results[1].Overflow.Alert.Events[2].GetMeta("service") == "bitwarden" +results[1].Overflow.Alert.Events[2].GetMeta("source_ip") == "192.168.1.100" +results[1].Overflow.Alert.Events[2].GetMeta("timestamp") == "2023-04-24T10:06:00Z" +results[1].Overflow.Alert.GetScenario() == "crowdsecurity/bitwarden-slow-bf" +results[1].Overflow.Alert.Remediation == true +results[1].Overflow.Alert.GetEventsCount() == 3 diff --git a/collections/MariuszKociubinski/bitwarden.yaml b/collections/MariuszKociubinski/bitwarden.yaml index 27cfb579ede..05e672c5ea5 100644 --- a/collections/MariuszKociubinski/bitwarden.yaml +++ b/collections/MariuszKociubinski/bitwarden.yaml @@ -2,6 +2,7 @@ parsers: - MariuszKociubinski/bitwarden-logs scenarios: - MariuszKociubinski/bitwarden-bf + - crowdsecurity/bitwarden-slow-bf description: "Bitwarden Self Hosted support : parser and brute-force detection" author: MariuszKociubinski tags: diff --git a/scenarios/crowdsecurity/bitwarden-slow-bf.md b/scenarios/crowdsecurity/bitwarden-slow-bf.md new file mode 100644 index 00000000000..77334ca1605 --- /dev/null +++ b/scenarios/crowdsecurity/bitwarden-slow-bf.md @@ -0,0 +1,10 @@ +Detect super slow bitwarden bruteforce attempts that evade traditional rate limiting: + + - Uses conditional type with capacity -1 (unlimited) + - Triggers when at least 3 failed authentication attempts occur + - Median interval between attempts exceeds 2 minutes + - Leakspeed of 2h naturally caps maximum interval (~40-60 minutes for 3 events) + - Uses `MedianInterval()` helper to detect consistent timing patterns (more robust against outliers) + +This scenario complements the standard bitwarden-bf scenario (capacity 5, leakspeed 20s) by catching attackers who deliberately slow their attempts to avoid detection. The standard scenario catches 5 failures within ~100 seconds, while this catches 3 failures with median interval >2 minutes (naturally capped by 2h leakspeed). + diff --git a/scenarios/crowdsecurity/bitwarden-slow-bf.yaml b/scenarios/crowdsecurity/bitwarden-slow-bf.yaml new file mode 100644 index 00000000000..e3883835cbd --- /dev/null +++ b/scenarios/crowdsecurity/bitwarden-slow-bf.yaml @@ -0,0 +1,22 @@ +# bitwarden super slow bruteforce detection +type: conditional +name: crowdsecurity/bitwarden-slow-bf +description: "Detect super slow bitwarden bruteforce attempts that evade rate limiting" +filter: "evt.Meta.log_type == 'bitwarden_failed_auth'" +groupby: evt.Meta.source_ip +capacity: -1 +condition: | + len(queue.Queue) >= 3 && + MedianInterval(map(queue.Queue[-3:], { #.Time })) > duration("2m") +leakspeed: 2h +blackhole: 5m +labels: + service: bitwarden + behavior: "http:bruteforce" + spoofable: 0 + confidence: 3 + classification: + - attack.T1110 + label: "Bitwarden Slow Bruteforce" + remediation: true +