Since the v1.5 release — our biggest release since v1.0 — the CrowdSec Security Engine Version provides even more control over your security management.
In this blog series, I want to walk you through some of the most interesting features and explore a few use cases. In the first installment, I will explore the new capability to detect multiple-step behaviors in a single scenario using the example of a successful SSH brute force attack.
What is brute force?
First things, first. To avoid any confusion, let’s clearly define what is a brute force attack. A brute force attack is a hacking method where an attacker attempts to gain unauthorized access to a system by systematically trying all possible combinations of passwords or encryption keys until the correct one is found.
Brute force relies on the sheer computational power to exhaustively guess credentials, without any other clues or shortcuts. While brute force attacks can be time-consuming and resource-intensive, they can be effective against weak or poorly secured systems, especially those with simple, short, or commonly used passwords.
Now, let’s see how you can use the CrowdSec Security Engine to protect your systems against brute force attacks.
Requirements
Setup the acquisition (optional)
The CrowdSec Security runs in two modes — the service mode and the replay mode. In service mode (aka the default mode), the Security Engine monitors live logs while in replay mode you can run the Security Engine against a specified log file. The replay mode is mostly used when you want to test a parser or a scenario, or when you want to detect false positives.
The acquisition configuration defines which stream of information the Security Engine must process (log file path, S3 bucket, CloudWatch log stream, etc.). This is what the acquis.yaml
would look like to monitor CrowdSec SSH logs in service mode.
#/etc/crowdsec/acquis.yaml
---
Filenames:
- /var/log/auth.log
Labels:
type: syslog
---
Install the SSHD collection
You can find the collection here.
sudo cscli collections install crowdsecurity/sshd
Parse successful authentication
To detect a successful brute force attack, CrowdSec needs to parse a successful authentication log. You can refer to this documentation article to understand how to write a parser. We can do this by adding this node in the crowdsecurity/sshd-logs
parser:
#/etc/crowdsec/parsers/s01-parse/sshd-logs.yaml
---
- grok:
pattern: 'Accepted password for %{USERNAME:sshd_valid_user} from %{IP_WORKAROUND:sshd_client_ip} port d+'
apply_on: message
statics:
- meta: log_type
value: ssh_success-auth
- meta: target_user
expression: "evt.Parsed.sshd_valid_user"
Note: This may not cover all the SSH successful authentication messages.
The scenario
For this use case I am using the new type of scenario conditional
.
With this type of scenario, it is possible to access previous events that were poured into the bucket and determine where they sit on the timeline — i.e. which event happened when. Find more info about the conditional
scenario here.
To detect a successful brute force attack, we must check that our bucket contains at least five failed authentication logs and one successful authentication log.
Here is the condition that we will use for our conditional bucket:
condition: |
count(queue.Queue, #.Meta.log_type == 'ssh_failed-auth') > 5 and count(queue.Queue, #.Meta.log_type == 'ssh_success-auth') > 0
This checks that the buckets contain at least five failed authentication logs and one successful authentication log.
And here is the full scenario that detects successful SSH brute force:
#/etc/crowdsec/scenarios/successful-ssh-bf.yaml
---
type: conditional
name: crowdsecurity/successful-ssh-bf
description: "Detect successful ssh bruteforce"
filter: "evt.Meta.service == 'ssh'"
leakspeed: "10s"
cache_size: 15
capacity: -1
condition: |
count(queue.Queue, #.Meta.log_type == 'ssh_failed-auth') > 5 and count(queue.Queue, #.Meta.log_type == 'ssh_success-auth') > 0
groupby: evt.Meta.source_ip
blackhole: 1m
labels:
remediation: true
Test with CrowdSec replay mode
To check that everything works properly, we can now test this scenario with the CrowdSec replay mode.
Here are some logs that represent a successful brute force:
#ssh_bf.log
---
Apr 11 15:53:50 myvps sshd[834901]: Failed password for root from 1.2.3.4 port 1042 ssh2
Apr 11 15:53:51 myvps sshd[834901]: Failed password for root from 1.2.3.4 port 1042 ssh2
Apr 11 15:53:51 myvps sshd[834901]: Failed password for root from 1.2.3.4 port 1042 ssh2
Apr 11 15:53:52 myvps sshd[835121]: Failed password for root from 1.2.3.4 port 1042 ssh2
Apr 11 15:53:53 myvps sshd[835121]: Failed password for root from 1.2.3.4 port 1042 ssh2
Apr 11 15:53:54 myvps sshd[835121]: Failed password for root from 1.2.3.4 port 1042 ssh2
Apr 11 15:53:54 myvps sshd[835241]: Accepted password for root from 1.2.3.4 port 1042 ssh2
We can run the CrowdSec Replay mode with the following command:
$ sudo crowdsec -dsn file://ssh_bf.log -type syslog -no-api
INFO[11-04-2023 19:23:41] single file mode : log_media=stdout daemonize=false
INFO[11-04-2023 19:23:41] Enabled feature flags: none
INFO[11-04-2023 19:23:41] Crowdsec v1.5.0-rc3-6-g169b8442-linux-169b84421227a4a2cab1c680863df958862bc615
WARN[11-04-2023 19:23:41] MaxOpenConns is 0, defaulting to 100
INFO[11-04-2023 19:23:41] Loading prometheus collectors
WARN[11-04-2023 19:23:41] Exprhelpers loaded without database client.
INFO[11-04-2023 19:23:41] Loading grok library /etc/crowdsec/patterns
INFO[11-04-2023 19:23:41] Loading enrich plugins
INFO[11-04-2023 19:23:41] Successfully registered enricher 'GeoIpCity'
INFO[11-04-2023 19:23:41] Successfully registered enricher 'GeoIpASN'
INFO[11-04-2023 19:23:41] Successfully registered enricher 'IpToRange'
INFO[11-04-2023 19:23:41] Successfully registered enricher 'reverse_dns'
INFO[11-04-2023 19:23:41] Successfully registered enricher 'ParseDate'
INFO[11-04-2023 19:23:41] Successfully registered enricher 'UnmarshalJSON'
INFO[11-04-2023 19:23:41] Loading parsers from 5 files
INFO[11-04-2023 19:23:41] Loaded 2 parser nodes file=/etc/crowdsec/parsers/s00-raw/syslog-logs.yaml stage=s00-raw
INFO[11-04-2023 19:23:41] Loaded 1 parser nodes file=/etc/crowdsec/parsers/s01-parse/sshd-logs.yaml stage=s01-parse
INFO[11-04-2023 19:23:41] Loaded 1 parser nodes file=/etc/crowdsec/parsers/s02-enrich/dateparse-enrich.yaml stage=s02-enrich
INFO[11-04-2023 19:23:41] Loaded 1 parser nodes file=/etc/crowdsec/parsers/s02-enrich/geoip-enrich.yaml stage=s02-enrich
INFO[11-04-2023 19:23:41] Loaded 1 parser nodes file=/etc/crowdsec/parsers/s02-enrich/whitelists.yaml stage=s02-enrich
INFO[11-04-2023 19:23:41] Loaded 6 nodes from 3 stages
INFO[11-04-2023 19:23:41] No postoverflow parsers to load
INFO[11-04-2023 19:23:41] Loading 1 scenario files
INFO[11-04-2023 19:23:41] Adding conditional bucket cfg=proud-resonance file=/etc/crowdsec/scenarios/successfull-ssh-bf.yaml name=crowdsecurity/successful-ssh-bf
WARN[11-04-2023 19:23:41] Loaded 1 scenarios
INFO[11-04-2023 19:23:41] Adding file ssh_bf.log to filelist type="file://ssh_bf.log"
INFO[11-04-2023 19:23:41] Starting processing data
INFO[11-04-2023 19:23:41] reading ssh_bf.log at once type="file://ssh_bf.log"
WARN[11-04-2023 19:23:41] prometheus: listen tcp 127.0.0.1:6060: bind: address already in use
WARN[11-04-2023 19:23:41] Acquisition is finished, shutting down
INFO[11-04-2023 19:23:42] Ip 1.2.3.4 performed 'crowdsecurity/successful-ssh-bf' (7 events over 1h29m51.947340934s) at 2023-04-11 15:53:54 +0000 UTC
INFO[11-04-2023 19:23:42] Killing parser routines
INFO[11-04-2023 19:23:43] Bucket routine exiting
INFO[11-04-2023 19:23:44] crowdsec shutdown
And there you have it — CrowdSec detected the successful brute force!
Demo
Now it is time to check if our new scenario works in real-time. For this demo, I will use Hydra to perform the brute force and I will target the user demo
using a server that I own.
Note: Before we get started with this step, please make sure to restart the Security Engine since we have edited some parsers and scenarios and added context to alerts.
Once you restart the Security Engine, run Hydra.
$ hydra -l demo -P pass.txt DEMO_IP ssh -t 4
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2023-04-25 15:04:44
[DATA] max 4 tasks per 1 server, overall 4 tasks, 9 login tries (l:1/p:9), ~3 tries per task
[DATA] attacking ssh://DEMO_IP:22/
[22][ssh] host: DEMO_IP login: demo password: Password123
1 of 1 target successfully completed, 1 valid password found
We managed to find the password of the demo
user, and we can see in the Security Engine logs that the scenario has been triggered:
time="25-04-2023 13:04:49" level=info msg="Ip demo_ip performed 'crowdsecurity/successful-ssh-bf' (10 events over 5.015762151s) at 2023-04-25 13:04:49.671649049 +0000 UTC"
time="25-04-2023 13:04:49" level=info msg="(ff7bfc7d3d8043188b90b8ca80edd29cEo2BtePh5aKyvESP/crowdsec) crowdsecurity/successful-ssh-bf by ip demo_ip (FR/5410) : 4h ban on Ip demo_ip"
Conclusion
As we saw in this article, the conditional bucket feature introduced in CrowdSec Security Engine 1.5 helps you detect more advanced behaviors. Check out part II of this series where I’ll show you how to use the same feature to detect the impossible travel behavior (same account connecting from two different countries within a short timeframe).