Creating scenarios
caution
All the examples assume that you have installed the iptables parser, and that your iptables configuration logs dropped packets.
#
Leaky bucketIn this example, we will write a simple scenario based on a leaky bucket that will trigger if an IP tries to port scan our machine.
We'll start with the most basic scenario:
type: leakydebug: truename: demo/leaky-exampledescription: "detect cool stuff"filter: evt.Meta.log_type == 'iptables_drop'capacity: 1leakspeed: 1mblackhole: 1mlabels: type: scan
We define:
- A filter: an expression that decides if the scenario will apply to current event or not
- A type: here, we have a
leaky
bucket - A capacity: how many events can be poured in the bucket before it overflows
- A leakspeed: the speed at which events are removed from the bucket
- A blackhole: duration for which the bucket should be overflow again
- Some labels to qualify the event
This scenario in its current state is not really useful:
- We do not define a
groupby
: the same bucket will be used for all source IP, which prevent us to ban the IP actually doing the port scan. - Our filter is too generic: we also match dropped UDP packets, and it can be very dangerous to apply a decision on them.
Let's fix that !
type: leakydebug: truename: demo/leaky-exampledescription: "detect cool stuff"filter: "evt.Meta.log_type == 'iptables_drop' and evt.Parsed.proto == 'TCP'"groupby: evt.Meta.source_ipcapacity: 15leakspeed: 1mblackhole: 1mlabels: type: scan
We added:
A new filter,
and evt.Parsed.proto == 'TCP'
: our scenario will now only apply if the protocol is TCP (based on what was extracted from the log line by our GROK pattern)A
groupby
parameter: we now create one bucket per source ipWe also updated the capacity and leakspeed to be more useful for a real life scenario.
But this is still not perfect ! We want to create a scenario to detect port scan, but our current configuration will also detect repeated connections to the same port.
We can fix this by adding a
distinct
parameter:
type: leakydebug: truename: demo/leaky-exampledescription: "detect cool stuff"filter: "evt.Meta.log_type == 'iptables_drop' and evt.Parsed.proto == 'TCP'"groupby: evt.Meta.source_ipdistinct: evt.Parsed.dst_portcapacity: 15leakspeed: 1mblackhole: 1mlabels: type: scan
The distinct parameter will prevent an event to be poured into the bucket if there is already an event in it with the same value for evt.Parsed.dst_port
.
In our case, this means that we will only have unique ports in our bucket.
And voila ! We now have a working scenario able to detect port scanning.
#
Trigger bucketIn this example, we will write a simple scenario that will trigger if anyone sends a packet to the port 3389.
type: triggername: demo/trigger-exampledescription: "Detect connection to RDP port"filter: "evt.Meta.log_type == 'iptables_drop' and evt.Parsed.proto == 'TCP' and evt.Parsed.dst_port == '3389'"groupby: evt.Meta.source_ipblackhole: 2mlabels: service: rdp type: scan remediation: true
What changed in comparaison to our leaky example ?
- We defined the type to be
trigger
: this means that the bucket will overflow if any event is poured into it - Our filter checks if the value of
dst_port
is equal to3389
: you can access all capture group defined in a GROK pattern with the objectevt.Parsed
- We set the
remediation
label totrue
: if the bucket overflow, we will apply a remediation based on your profile configuration (by default, 4h ban)
Let's try it !
Put the scenario in a file called iptables-rdp.yaml
, in the scenarios
folder of crowdsec.
Use cscli
to make sure the scenario is detected:
$ cscli scenarios list ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- NAME ๐ฆ STATUS VERSION LOCAL PATH ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- iptables-rdp.yaml ๐ enabled,local /etc/crowdsec/scenarios/iptables-rdp.yaml ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Next, we will trigger the scenario by attempting to connect to the port 3389 on the machine where crowdsec is running:
$ nc -v 127.0.0.1 3389nc: connect to 127.0.0.1 port 3389 (tcp) failed: Connection refused
In our kernel logs, we can see:
Aug 20 16:20:09 mantis kernel: [887475.435839] DROP: IN=lo OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00 SRC=192.168.1.23 DST=192.168.1.23 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=29037 DF PROTO=TCP SPT=39158 DPT=3389 WINDOW=65495 RES=0x00 SYN URGP=0
We can then check that a decision has been applied:
$ cscli decisions list+-------+----------+-----------------+----------------------+--------+---------+----+--------+------------------+----------+| ID | SOURCE | SCOPE:VALUE | REASON | ACTION | COUNTRY | AS | EVENTS | EXPIRATION | ALERT ID |+-------+----------+-----------------+----------------------+--------+---------+----+--------+------------------+----------+| 17563 | crowdsec | Ip:192.168.1.47 | demo/trigger-example | ban | | | 1 | 3h59m5.00140614s | 82 |+-------+----------+-----------------+----------------------+--------+---------+----+--------+------------------+----------+