From 2410909c8760812b33e2274b6d38635ea4c38996 Mon Sep 17 00:00:00 2001 From: Laurence Date: Wed, 21 Jan 2026 11:49:44 +0000 Subject: [PATCH] enhance: change -3 to -4 to handle outliers in timebased bf --- .tests/ssh-time-based-bf/scenario.assert | 20 +++++++++++++++++-- .../ssh-time-based-bf/ssh-time-based-bf.log | 13 ++++++++---- scenarios/crowdsecurity/ssh-time-based-bf.md | 8 ++++---- .../crowdsecurity/ssh-time-based-bf.yaml | 8 ++++---- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/.tests/ssh-time-based-bf/scenario.assert b/.tests/ssh-time-based-bf/scenario.assert index b07bce53ebb..a35daa5cdfc 100644 --- a/.tests/ssh-time-based-bf/scenario.assert +++ b/.tests/ssh-time-based-bf/scenario.assert @@ -28,9 +28,17 @@ results[0].Overflow.Alert.Events[2].GetMeta("service") == "ssh" results[0].Overflow.Alert.Events[2].GetMeta("source_ip") == "10.0.0.101" results[0].Overflow.Alert.Events[2].GetMeta("target_user") == "oracle" results[0].Overflow.Alert.Events[2].GetMeta("timestamp") == "2026-09-30T11:40:00Z" +basename(results[0].Overflow.Alert.Events[3].GetMeta("datasource_path")) == "ssh-time-based-bf.log" +results[0].Overflow.Alert.Events[3].GetMeta("datasource_type") == "file" +results[0].Overflow.Alert.Events[3].GetMeta("log_type") == "ssh_failed-auth" +results[0].Overflow.Alert.Events[3].GetMeta("machine") == "server" +results[0].Overflow.Alert.Events[3].GetMeta("service") == "ssh" +results[0].Overflow.Alert.Events[3].GetMeta("source_ip") == "10.0.0.101" +results[0].Overflow.Alert.Events[3].GetMeta("target_user") == "postgres" +results[0].Overflow.Alert.Events[3].GetMeta("timestamp") == "2026-09-30T12:00:00Z" results[0].Overflow.Alert.GetScenario() == "crowdsecurity/ssh-time-based-bf_user-enum" results[0].Overflow.Alert.Remediation == false -results[0].Overflow.Alert.GetEventsCount() == 3 +results[0].Overflow.Alert.GetEventsCount() == 4 "10.0.0.101" in results[1].Overflow.GetSources() results[1].Overflow.Sources["10.0.0.101"].IP == "10.0.0.101" results[1].Overflow.Sources["10.0.0.101"].Range == "" @@ -60,6 +68,14 @@ results[1].Overflow.Alert.Events[2].GetMeta("service") == "ssh" results[1].Overflow.Alert.Events[2].GetMeta("source_ip") == "10.0.0.101" results[1].Overflow.Alert.Events[2].GetMeta("target_user") == "oracle" results[1].Overflow.Alert.Events[2].GetMeta("timestamp") == "2026-09-30T11:40:00Z" +basename(results[1].Overflow.Alert.Events[3].GetMeta("datasource_path")) == "ssh-time-based-bf.log" +results[1].Overflow.Alert.Events[3].GetMeta("datasource_type") == "file" +results[1].Overflow.Alert.Events[3].GetMeta("log_type") == "ssh_failed-auth" +results[1].Overflow.Alert.Events[3].GetMeta("machine") == "server" +results[1].Overflow.Alert.Events[3].GetMeta("service") == "ssh" +results[1].Overflow.Alert.Events[3].GetMeta("source_ip") == "10.0.0.101" +results[1].Overflow.Alert.Events[3].GetMeta("target_user") == "postgres" +results[1].Overflow.Alert.Events[3].GetMeta("timestamp") == "2026-09-30T12:00:00Z" results[1].Overflow.Alert.GetScenario() == "crowdsecurity/ssh-time-based-bf" results[1].Overflow.Alert.Remediation == false -results[1].Overflow.Alert.GetEventsCount() == 3 +results[1].Overflow.Alert.GetEventsCount() == 4 diff --git a/.tests/ssh-time-based-bf/ssh-time-based-bf.log b/.tests/ssh-time-based-bf/ssh-time-based-bf.log index d705380659d..e6a2bd1ffed 100644 --- a/.tests/ssh-time-based-bf/ssh-time-based-bf.log +++ b/.tests/ssh-time-based-bf/ssh-time-based-bf.log @@ -4,7 +4,12 @@ Sep 30 10:12:00 server sshd[12347]: Invalid user guest from 10.0.0.100 port 5676 Sep 30 11:00:00 server sshd[12348]: Invalid user root from 10.0.0.101 port 56765 Sep 30 11:20:00 server sshd[12349]: Invalid user mysql from 10.0.0.101 port 56766 Sep 30 11:40:00 server sshd[12350]: Invalid user oracle from 10.0.0.101 port 56767 -Sep 30 12:00:00 server sshd[12351]: Invalid user postgres from 10.0.0.102 port 56768 -Sep 30 12:10:00 server sshd[12352]: Invalid user jenkins from 10.0.0.102 port 56769 -Sep 30 12:15:00 server sshd[12353]: Accepted password for jenkins from 10.0.0.102 port 56770 ssh2 -Sep 30 12:20:00 server sshd[12354]: Invalid user admin from 10.0.0.102 port 56771 +Sep 30 12:00:00 server sshd[12351]: Invalid user postgres from 10.0.0.101 port 56768 +Sep 30 12:30:00 server sshd[12352]: Invalid user admin from 10.0.0.102 port 56769 +Sep 30 12:40:00 server sshd[12353]: Invalid user test from 10.0.0.102 port 56770 +Sep 30 12:45:00 server sshd[12354]: Accepted password for test from 10.0.0.102 port 56771 ssh2 +Sep 30 12:50:00 server sshd[12355]: Invalid user guest from 10.0.0.102 port 56772 +Sep 30 13:00:00 server sshd[12356]: Invalid user root from 10.0.0.103 port 56773 +Sep 30 13:00:02 server sshd[12357]: Invalid user admin from 10.0.0.103 port 56774 +Sep 30 13:00:04 server sshd[12358]: Invalid user test from 10.0.0.103 port 56775 +Sep 30 14:00:04 server sshd[12359]: Invalid user guest from 10.0.0.103 port 56776 diff --git a/scenarios/crowdsecurity/ssh-time-based-bf.md b/scenarios/crowdsecurity/ssh-time-based-bf.md index f16a85860eb..c02e63682df 100644 --- a/scenarios/crowdsecurity/ssh-time-based-bf.md +++ b/scenarios/crowdsecurity/ssh-time-based-bf.md @@ -1,7 +1,7 @@ Detect time-based ssh bruteforce attempts that evade traditional rate limiting with false positive reduction: - Uses conditional type with capacity -1 (unlimited) - - Triggers when at least 3 failed authentication attempts occur + - Triggers when at least 4 failed authentication attempts occur - Median interval between failed attempts exceeds 10 minutes - **False positive reduction**: Uses `cancel_on` to cancel bucket if user successfully authenticates - Prevents "forgot password" scenarios from triggering alerts @@ -14,10 +14,10 @@ Detect time-based ssh bruteforce attempts that evade traditional rate limiting w - Requires `crowdsecurity/sshd-success-logs` parser for cancel_on functionality **Two variants:** -1. **ssh-time-based-bf**: Standard bruteforce detection (3 failed logins from same IP) -2. **ssh-time-based-bf_user-enum**: User enumeration detection (3 distinct usernames from same IP) +1. **ssh-time-based-bf**: Standard bruteforce detection (4 failed logins from same IP) +2. **ssh-time-based-bf_user-enum**: User enumeration detection (4 distinct usernames from same IP) This scenario complements the standard ssh-bf (capacity 5, leakspeed 10s) and ssh-slow-bf (capacity 10, leakspeed 60s) scenarios with no overlap: - ssh-bf catches 5 failures within ~50 seconds (rate-based) - ssh-slow-bf catches 10 failures within ~10 minutes (rate-based) -- ssh-time-based-bf catches 3 failures with median interval >10 minutes (time-pattern-based, naturally capped by 2h leakspeed) +- ssh-time-based-bf catches 4 failures with median interval >10 minutes (time-pattern-based, naturally capped by 2h leakspeed) diff --git a/scenarios/crowdsecurity/ssh-time-based-bf.yaml b/scenarios/crowdsecurity/ssh-time-based-bf.yaml index c1c798b6296..697b5a9c485 100644 --- a/scenarios/crowdsecurity/ssh-time-based-bf.yaml +++ b/scenarios/crowdsecurity/ssh-time-based-bf.yaml @@ -8,8 +8,8 @@ capacity: -1 cancel_on: "evt.Meta.log_type == 'auth_success'" condition: | let failedAuths = filter(queue.Queue, {#.Meta.log_type == 'ssh_failed-auth'}); - len(failedAuths) >= 3 && - MedianInterval(map(failedAuths[-3:], {#.Time})) > duration("10m") + len(failedAuths) >= 4 && + MedianInterval(map(failedAuths[-4:], {#.Time})) > duration("10m") leakspeed: 2h blackhole: 5m reprocess: true @@ -34,8 +34,8 @@ capacity: -1 cancel_on: "evt.Meta.log_type == 'auth_success'" condition: | let failedAuths = filter(queue.Queue, {#.Meta.log_type == 'ssh_failed-auth'}); - len(failedAuths) >= 3 && - MedianInterval(map(failedAuths[-3:], {#.Time})) > duration("10m") + len(failedAuths) >= 4 && + MedianInterval(map(failedAuths[-4:], {#.Time})) > duration("10m") leakspeed: 2h blackhole: 5m reprocess: true