Rules

Panther Rules are Python3 functions used to identify suspicious activity and generate helpful signal for your team.

Rule Components

Rules contain three main Python functions to control the detection, deduplication of events, and generation of alert titles.

  • A rule function with an event argument that returns True if the rule should send an alert or False if it should not

  • A title function to generate the alert title, sometimes interpolating values from the event

  • A dedup function to control how alerts are grouped together. If no dedup function is specified, we group alerts together based on the title.

Additionally, rules contain metadata for alert context, setting the severity, associating specific log type(s), and defining unit tests.

For example, the rule below validates if unauthenticated access has occurred on an S3 bucket:

# This rule applies to S3 Server Access Logs
def rule(event):
if event.get('bucket') not in {'my-set-of-authenticated-buckets'}:
return False
return 'requester' not in event
# This rule groups alert events by the bucket name
def dedup(event):
return event.get('bucket')
# Alerts will have a title like `Unauthenticated Access to S3 Bucket my-super-secret-data`
def title(event):
return 'Unauthenticated Access to S3 Bucket {}'.format(event.get('bucket'))

Workflow

Panther rules can be written, tested, and deployed either with the UI or the panther_analysis_tool.

Each rule takes an event input of a given log type from the supported logs page.

Rule Body

The rule body must:

  • Be valid Python3

  • Define a rule() function that accepts one argument, event

  • Return True (send an alert) or False (known good behavior) from the rule function

def rule(event):
if event['energy_level'] > 9000:
return True

The Python body may also:

  • Import any standard Python3 libraries

  • Import from the user-defined global helpers

  • Define additional helper functions as needed

  • Define variables and classes outside the scope of the rule function

  • Define a dedup() function that accepts an event argument and returns a string

  • Define a title() function that accepts an event argument and returns a string

Referencing the supported logs page provides details on all available fields in events.

Example Rule

Let's write a rule on a sample NGINX Access log:

{
"bodyBytesSent": 193,
"httpReferer": "https://domain1.com/?p=1",
"httpUserAgent": "Chrome/80.0.3987.132 Safari/537.36",
"remoteAddr": "180.76.15.143",
"request": "GET /admin-panel/ HTTP/1.1",
"status": 200,
"time": "2019-02-06 00:00:38 +0000 UTC"
}

This example rule would alert upon successful admin panel logins:

def rule(event):
# A common pattern is to exit the rule early to avoid unnecessary processing.
#
# It's also a common pattern to only proceed if requests are successful.
if event.get('status') != 200:
return False
# Send an alert if a user logs into the admin panel.
return 'admin-panel' in event.get('request')

In the rule(), returning a value of True indicates an alert should send. Returning a value of False indicates the log is not suspicious.

When accessing non-required event fields, it's recommended to use event.get('<field-name>')

Alert Deduplication

To avoid alert fatigue, events are grouped together (by a deduplication string) within a given time period (deduplication period), and associated with a single AlertID.

By default, events are merged on the key defaultDedupString:${RuleID} over a period of 1 hour, but can be set to:

  • 15m

  • 30m

  • 1h

  • 3h

  • 12h

  • 24h

To modify the deduplication key, use the dedup() function in your rule body:

def dedup(event):
return event.get('remoteAddr')

The length of the dedup string is limited to 1000 characters and the rest will be truncated

The same parsed log event is passed into this function, and you may use any logic desired to calculate the dedupString.

If a Falsy value is returned from dedup(), then the default string will be used

Alert Threshold

In some cases you may want a rule to trigger only if N events have matched during the rule's deduplication period. You can achieve this by configuring the Threshold field, which is set to 1 by default.

Alert Titles

Alert titles sent to our destinations are the default value of New Alert: ${Display Name or ID}. To override this message, use the title() function in your rule:

def title(event):
return 'Admin Panel Login'

The title can also be interpolated by using event attributes:

def title(event):
return 'Successful Admin Panel Login from IP {}'.format(
event.get('remoteAddr', '<IP_NOT_FOUND>'))

The second argument to event.get() is the default value returned if the key is not found. By supplying a value here, we avoid a title of 'Successful Admin Panel Login from IP None'

First Steps with Rules

When beginning your rule writing/editing journey, your team should decide between a UI or CLI driven workflow.

Then, configure the built in rules by searching for the Configuration Required tag. These rules are designed to be modified by you, the security professional, based on your organization's internal business logic.

Writing Rules with the Panther Analysis Tool

The panther_analysis_tool is a Python command line interface for testing, packaging, and deploying Panther Policies and Rules. This enables teams to work in a more developer oriented workflow and track detections with version control systems such as git.