Detection and Response
  • 25 Jan 2024
  • 8 Minutes to read
  • Contributors
  • Dark
    Light

Detection and Response

  • Dark
    Light

Article summary

Detection & Response rules automate actions based on the real-time events streaming into LimaCharlie. Each rule has two YAML descriptors: one that describes what to detect, and another that describes how to respond.

It's recommended to read about Events before diving into D&R rules.

What's the difference between Sigma and Soteria rules?

Sigma is an open source project that aims at creating a generic query language for security and D&R rules. It looks up known anomalies and Common Vulnerabilities and Exposures (CVEs).

As Sigma is an open source project,

  • applying the Sigma ruleset is free
  • there will be a higher rate of false positives

Soteria is a US-based MSSP that has been using LimaCharlie for a long time. They developed a corpus of hundreds of behavioral signatures for Windows / Mac / Linux (signature not in terms of a hash, but in terms of a rule that describes a behavior). With one click, you can apply their rules in a managed way. When Soteria updates the rules for their customers, you will get those updates in real time as well.

As Soteria is a managed ruleset,

  • applying the Soteria ruleset costs $0.5 per endpoint per month
  • the rate of false positives is much lower

A Basic Rule

Here's a rule that detects DNS requests to example.com and responds by reporting them within the organization with a category name DNS Hit example.com.

# Detection
event: DNS_REQUEST
op: is
path: event/DOMAIN_NAME
value: example.com

# Response
- action: report
  name: DNS Hit example.com

This rule will detect and respond to requests to example.com within 100ms of the DNS_REQUEST event occurring. It uses the is operator to assess if the given value can be found inside the event at the given path.

Want more detection resources?

For examples, check out the Detection and Response Examples.

You can also learn more in the LimaCharlie Academy, where we walkthrough how to build basic and advanced detections.

Detection

Targets and events

Detections must specify an event (or events), and may optionally specify a target. Each target offers different event types. Here are the 5 possible rule targets:

  • edr (default): telemetry events from LimaCharlie sensors
  • detection: detections generated by other rules
  • deployment: lifecycle events around deployment & enrollment of sensors
  • artifact: artifacts collected via REST API or via artifact_get sensor command
  • artifact_event: lifecycle events around artifacts such as ingestion

For a full list of events with examples, see Reference: Events.

Most of this page focuses on edr events. For information about other targets, see Detection on Alternate Targets.

Operators

Detections must specify an op (logical operator). The types of operators used are a good indicator for how complex the rule will be.

Here's a simple detection that uses a single is windows operator to detect a Windows sensor connecting to the Internet:

event: CONNECTED
op: is windows

And here's a more complex detection that uses the and operator to detect a non-Windows sensor that's making a DNS request to example.com.

event: DNS_REQUEST
op: and
rules:
- op: is windows
  not: true
- op: is
  path: event/DOMAIN_NAME
  value: example.com

There are 3 operators here:

  1. The and operator evaluates nested rules and will only itself be true if both of the rules inside it are true
  2. The is windows operator is accompanied by the not parameter, reversing the matching outcome and effectively saying "anything but windows"
  3. The is operator is comparing the value 'example.com' to the content of the event at the given path

Each operator may have parameters alongside it. Some parameters, such as not, are useable on all operators. Most operators have required parameters specific to them.

For a full list of operators and their usage, see Reference: Operators.

Paths

The path parameter is used commonly in several operators to specify which part of the event should be evaluated.

Here's an example of a standard JSON DNS_REQUEST event from a sensor:

{
  "event": {
    "DNS_TYPE": 1,
    "TIMESTAMP": 1456285240,
    "DNS_FLAGS": 0,
    "DOMAIN_NAME": "example.com"
  },
  "routing": {
    "event_type": "DNS_REQUEST",
    "oid": "8cbe27f4-agh1-4afb-ba19-138cd51389cd",
    "sid": "d3d17f12-eecf-5287-b3a1-bf267aabb3cf",
    "hostname": "test-host-123"
    // ...and other standardized routing data
  }
}

This detection will match the above event's hostname:

event: DNS_REQUEST
op: is
path: routing/hostname # where the value lives
value: test-host-123   # the expected value at that path

This works a lot like file paths in a directory system. Since LimaCharlie events are always formatted with separate event and routing data, almost all paths start with either event/ or routing/.

Tip: you can visit the Timeline view of any Sensor to browse historical events and bring them directly into the D&R rule editor.

Paths may also employ the use of wildcards * to represent 0 or more directory levels, or ? to represent exactly 1 directory level. This can be useful when working with events like NETWORK_CONNECTIONS:

{
  "event": {
    "NETWORK_ACTIVITY": [
      {
        "SOURCE": {
          "IP_ADDRESS": "172.16.223.138",
          "PORT": 50396
        },
        "IS_OUTGOING": 1,
        "DESTINATION": {
          "IP_ADDRESS": "23.214.49.56",
          "PORT": 80
        }
      },
      {
        "SOURCE": {
          "IP_ADDRESS": "172.16.223.138",
          "PORT": 50397
        },
        "IS_OUTGOING": 1,
        "DESTINATION": {
          "IP_ADDRESS": "189.247.166.18",
          "PORT": 80
        }
      },
      // ...there could be several connections
    ],
    "HASH": "2de228cad2e542b2af2554d61fab5463ecbba3ff8349ba88c3e48637ed8086e9",
    "COMMAND_LINE": "C:\\WINDOWS\\system32\\msfeedssync.exe sync",
    "PROCESS_ID": 6968,
    "FILE_IS_SIGNED": 1,
    "USER_NAME": "WIN-5KC7E0NG1OD\\dev",
    "FILE_PATH": "C:\\WINDOWS\\system32\\msfeedssync.exe",
    "PARENT_PROCESS_ID": 1892
  },
  "routing": { ... } // Omitted for brevity
}

Notice that the NETWORK_ACTIVITY inside this event is a list.

Here's a rule that would match a known destination IP in any of the entries within NETWORK_ACTIVITY:

event: NETWORK_CONNECTIONS
op: is
path: event/NETWORK_ACTIVITY/?/DESTINATION/IP_ADDRESS # <---
value: 189.247.166.18

The ? saves us from enumerating each index within the list and instead evaluates all values at the indicated level. This can be very powerful when used in combination with lookups: lists of threat indicators such as known bad IPs or domains.

To learn more about using lookups in detections, see the lookup operator.

Values

The value parameter is commonly used by several detection operations but can also be used by some response actions as well.

In most detections value will be used to specify a known value like all the previous examples on this page have done. They're also capable of referencing previously set sensor variables using value: [[var-name]] double square bracket syntax.

Values from events can also be forwarded in response actions using value: <<event/FILE_PATH>> double angle bracket syntax.

To see how sensor variables and lookback values are used, see the add var / del var action in Reference: Actions.

Response

Responses are much simpler than Detections. They're a list of actions to perform upon a matching detection.

Actions

The most common action is the report action, which creates a Detection that shows up in the LimaCharlie web app and passes it along to the detections output stream in real-time.

- action: report
  name: detected-something
  
# Example of accessing map values 
- action: report
  name: Event detected by {{ .event.USER_NAME }} from {{ index (index .event.NETWORK_ACTIVITY 0) "SOURCE" "IP_ADDRESS" }}

Each item in the response specifies an action and any accompanying parameters for that action.

A more complex response action could include running a sensor command such as yara_scan using a field from within the detected event. The following example looks for NEW_DOCUMENT events that meet certain criteria, then initiates a YARA scan against the offending file path.

Detect

event: NEW_DOCUMENT
op: and
rules:
  - case sensitive: false
    op: matches
    path: event/FILE_PATH
    re: .\:\\(users|windows\\temp)\\.*
  - case sensitive: false
    op: matches
    path: event/FILE_PATH
    re: .*\.(exe|dll)

Respond

# Report is optional, but informative
- action: report
  name: Executable written to Users or Temp (yara scan)
  
# Initiate a sensor command to yara scan the FILE_PATH
- action: task
  command: yara_scan hive://yara/malware-rule -f "{{ .event.FILE_PATH }}"
  investigation: Yara Scan Executable
  suppression:
    is_global: false
    keys:
      - '{{ .event.FILE_PATH }}'
      - Yara Scan Executable
    max_count: 1
    period: 1m

Notice the use of suppression to prevent the same FILE_PATH from being scanned more than once per minute to prevent a resource runaway situation.

Which D&R Rule Triggered a Command?

To determine which D&R rule triggered a command on an endpoint, navigate to the Platform Logs section. If a command was triggered by a D&R rule, the audit log will show the associate rule. If the command was sent via the API, the audit logs will show the API key name.

To learn about all possible actions, see Reference: Actions.

Putting It All Together

Let's take this knowledge and write a rule to detect something a little more interesting.

On Windows there's a command called icacls which can be used to modify access control lists. Let's write a rule which detects any tampering via that command.

The first thing we can do is detect any new icacls processes:

event: NEW_PROCESS
op: ends with
path: event/FILE_PATH
value: icacls.exe

And we'll set a basic response action to report the detection, too:

- action: report
  name: win-acl-tampering

If we save that, we'll start to see detections for any icacls processes spawning. However, not all of them will be particularly interesting from a security perspective. In this case, we only really care about invocations of icacls where the grant parameter is specified.

Let's make this rule more specific. We can do this by using the and operator to match multiple operators. We'll check for the string "grant" in the COMMAND_LINE, and while we're at it we'll make sure we don't bother evaluating other platforms by using the is windows operator.

event: NEW_PROCESS
op: and
rules:
- op: is windows
- op: ends with
	path: event/FILE_PATH
	value: icacls.exe
- op: contains
  path: event/COMMAND_LINE
  value: grant

This more specific rule means we'll see fewer false positives to look at or exclude later.

However, we still might miss some invocations of icacls with this detection if they use any capital letters — our operators are being evaluated with an implicit case sensitive: true by default. Let's turn case sensitivity off and observe the final rule:

# Detection
event: NEW_PROCESS
op: and
rules:
- op: is windows
- op: ends with
	case sensitive: false
	path: event/FILE_PATH
	value: icacls.exe
- op: contains
	case sensitive: false
  path: event/COMMAND_LINE
  value: grant

# Response
- action: report
  name: win-acl-tampering

This rule combines multiple operators to specify the exact conditions which might make an icacls process interesting. If it sees one, it'll report it as a win-acl-tampering detection which will be forwarded to Outputs and become viewable in the Detections page.

Tip: test your rules without waiting for events! We recommend enabling the replay add-on for a better D&R rule writing experience.

  • Visit Timeline of a sensor and Build D&R Rule directly from real events
  • While drafting a rule, Replay an event against the rule to see if it would match
  • Replay a rule over historical events to see if any detections would have occurred

Was this article helpful?