TT Lab
Get started
Learn Learning paths Courses

Cron Ran curl at Three in the Morning

A rule is a data structure, not a sentence

Continue in TT Lab

In one line

Sigma is a format for writing "look for this in these logs" as tool-independent YAML. There are only three required items, title, logsource, and detection, and the core of the detection is the named selections inside detection and the single condition line that assembles them.

Why you need this

Detection rules have different syntax in every tool. The same "a scheduled job made a request outside" is written as a query in a search engine, as a filter in a log pipeline, and as a form on its own screen in an EDR. When you change companies, you rewrite the rules from scratch. Worse, rules cannot be shared. Even if one team builds a rule that catches a new technique, other teams have to transcribe it into their own tool's language, and while transcribing, one condition quietly drops out.

Sigma puts a format in the middle. You write the rule in YAML, and a backend translates it into each tool's query language. So a rule becomes code that people read, review, and push to git, and the tool becomes a swappable back end.

How it works

The skeleton of one rule looks like this.

title: 예약 작업이 사외로 요청했다
id: 8b2f5f4a-1d3c-4c22-9f0e-6a7b1c2d3e4f
status: test
description: cron 밑에서 curl·wget 이 실행되고 목적지가 사내가 아닌 경우
logsource:
  product: linux
  category: process_creation
detection:
  selection:
    ParentImage|endswith: '/cron'
    CommandLine|contains:
      - 'curl'
      - 'wget'
  filter_internal:
    CommandLine|contains: 'repo.corp.internal'
  condition: selection and not filter_internal
level: high

The rules documentation nails down three rules. title, logsource, and detection are required, status is one of stable, test, experimental, deprecated, and unsupported, and level is one of critical, high, medium, low, and informational. logsource narrows which logs are targeted with category (a class such as web servers or firewalls), product (a product such as windows or linux), and service (a service within that product) (log source documentation).

The most confusing point is that a mapping and a list mean different things. Listing several fields inside a selection is AND, and giving several values to one field is OR. In the example above, ParentImage and CommandLine must both match, but for curl and wget only one of the two has to match.

condition is a small language that assembles those selections (conditions documentation). It uses and, or, not, and parentheses, and when there are several names, you group them with 1 of selection_* (any one) and all of selection_* (all of them). There are also 1 of them and all of them, but the documentation does not recommend these two for shared rules — because when you later add one more selection, the meaning of the rule changes silently.

How values are compared is decided by the modifier after the field name (modifiers documentation). |contains, |startswith, |endswith, and |re are common, and if you chain them as in |contains|all, it becomes "all of the listed values must be present." The place where every beginner gets burned at least once is the word boundary of |contains. If you write CommandLine|contains: 'nc' to find nc, it catches rsync. That is because in the string there is an n followed by a c.

What it looks like in the field

The reason rules die in the field is usually one of two. One is too many false positives, so nobody looks. At first people check when an alert goes off, but if nine out of ten are normal backup jobs, from the eleventh on nobody opens it. So the plan to "write the rule broadly, catch everything for now, and narrow it later" almost always fails.

The other is hardcoding addresses into the rule. If you put the string into the rule because this incident's destination was cdn.updates-cache.net, the moment the domain changes next week the rule catches nothing. A long-lived rule writes not the address but the behavior — piping what was downloaded straight into a shell, or attaching a shell and connecting outward.

And it is important not to treat metadata as decoration. If falsepositives has the single line "normal backup jobs," the person called at three in the morning makes a judgment in 3 minutes. Without that line, they spend 30 minutes. references and tags are worth the same.

What you will do in the next lab

In the lab you receive 24 synthetic process execution records and write six rules. You start from the smallest rule and narrow it gradually, remove normal backup jobs with a filter, write a rule that catches by behavior instead of by address, and tie two branches together with 1 of. Each time, you actually run the rule against the incident records and confirm with numbers how many it hit and how many it missed. This lab deals only with files, so it runs in the lab Pod.