A rule that watches while you sleep
A detection rule is a test that a security tool runs on every event to find signs of an attack. When an event passes the test, the tool raises an alert.
Nobody can read a million log lines a day. A good rule reads them all and speaks up only when it matters. You will write one.
By the end of this lesson
Note
I can write a rule that finds a guessing attack in log lines.
Key words
| Word | What it means | Example |
|---|---|---|
| Detection rule | a test that raises an alert when events look like an attack | |
| Pattern | the shape of text a rule looks for | |
| Match | a line that fits the pattern | |
| Threshold | how many matches it takes to raise an alert | 5 failures |
| Time window | the span of time in which matches are counted | 60 seconds |
| Brute force | trying many passwords until one works |
The cells students fill in are left blank in this preview.
A rule starts by matching a pattern
The first step is to say what one bad event looks like. A failed sign-in has the words Failed password in it. That text is the pattern.
grep -E reads a pattern with a few extra symbols. A question mark after a bracket means the part is optional. So one pattern can match both a known user and an unknown one.
Match failed sign-ins in three lines
printf 'Failed password for sam from 192.0.2.5 port 22\nAccepted password for sam from 192.0.2.5 port 22\nFailed password for invalid user admin from 192.0.2.9 port 22\n' | grep -E 'Failed password for (invalid user )?[a-z]+ from'Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
One failure is normal, so count them
People mistype passwords. One failed sign-in is not an attack, so a rule that alerts on every match is mostly noise.
A threshold fixes that: the rule counts matches and speaks up at a number you choose. A guessing attack, called brute force, makes many failures very fast. That speed is what the rule must see.
Worked example: failures at five moments
| Seconds into the day | Failures so far | Inside a 10 second window |
|---|---|---|
| 10 | 1 | 1 |
| 12 | 2 | 2 (10 and 12) |
| 14 | 3 | 3 (10, 12 and 14): alert at threshold 3 |
| 300 | 4 | 1 (only 300): the old ones are too far back |
| 305 | 5 | 2 (300 and 305): no alert |
The cells students fill in are left blank in this preview.
A rule over a time window
def too_many(times, limit, window):
for start in times:
inside = 0
for other in times:
if other >= start and other - start <= window:
inside = inside + 1
if inside >= limit:
return True
return False
# Seconds at which a sign-in failed.
print(too_many([10, 12, 14, 300, 305], 3, 10))
print(too_many([10, 120, 300, 305], 3, 10))
# Change the 3 to a 4 in the first line, then press Run.
Count seconds inside a window
printf '10\n12\n14\n300\n305\n' | awk '$1 >= 10 && $1 <= 20' | wc -lShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people think a rule should count all the failures in a day. Three failures spread over twenty-four hours are normal. Three failures in ten seconds are not. Always count inside a short window, so that time is part of the rule.
Tuning a rule trades false alarms for misses
A low threshold catches more attacks but raises more false alarms. A high one is quiet but can miss a careful attacker who goes slowly.
A team tests a new rule on old logs first. Then it tunes the numbers until the alerts are few enough to read and still catch the attack.
Count requests for secret files
grep -cE '"(GET|POST) /(\.env|\.git)' case/var/log/nginx/access.logShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Going further
Write a rule in words for a different attack: a web visitor who asks for twenty pages that do not exist in one minute.
State the pattern, the threshold and the window. Say what a normal visitor looks like, and what your rule would do to them.