Detection rules › Panther

Panther rules: teleport

A long-lived cert was created

#
Severity
medium
Log types
Gravitational.TeleportAudit
Tags
Teleport
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

An unusually long-lived Teleport certificate was created

MITRE ATT&CK coverage

TacticTechniques
Persistence

Detection logic

from datetime import datetime, timedelta
from typing import Dict, Tuple

from panther_base_helpers import (
    golang_nanotime_to_python_datetime,
    panther_nanotime_to_python_datetime,
)

PANTHER_TIME_FORMAT = r"%Y-%m-%d %H:%M:%S.%f"
# Tune this to be some Greatest Common Denominator of session TTLs for your
# environment
MAXIMUM_NORMAL_VALIDITY_INTERVAL = timedelta(hours=12)
# To allow some time in between when a request is submitted and authorized
# vs when the certificate actually gets generated. In practice, this is much
# less than 5 seconds.
ISSUANCE_GRACE_PERIOD = timedelta(seconds=5)

# You can audit your logs in Panther to try and understand your role/validity
# patterns from a known-good period of access.
# A query example:
# ```sql
#  SELECT
#     cluster_name,
#     identity:roles,
#     DATEDIFF('HOUR', time, identity:expires) AS validity
#  FROM
#     panther_logs.public.gravitational_teleportaudit
#  WHERE
#     p_occurs_between('2023-09-01 00:00:00','2023-10-06 21:00:00Z')
#     AND event = 'cert.create'
#  GROUP BY cluster_name, identity:roles, validity
#  ORDER BY validity DESC
# ```

# A dictionary of:
#  cluster names: to a dictionary of:
#     role names: mapping to a tuple of:
#        ( maximum usual validity, expiration datetime for this rule )
CLUSTER_ROLE_MAX_VALIDITIES: Dict[str, Dict[str, Tuple[timedelta, datetime]]] = {
    # "teleport.example.com": {
    #     "example_role": (timedelta(hours=720), datetime(2023, 12, 01, 01, 02, 03)),
    #     "other_example_role": (timedelta(hours=720), datetime.max),
    # },
}


def rule(event):
    if not event.get("event") == "cert.create":
        return False
    max_validity = MAXIMUM_NORMAL_VALIDITY_INTERVAL + ISSUANCE_GRACE_PERIOD
    for role in event.deep_get("identity", "roles", default=[]):
        validity, expiration = CLUSTER_ROLE_MAX_VALIDITIES.get(event.get("cluster_name"), {}).get(
            role, (None, None)
        )
        if validity and expiration:
            # Ignore exceptions that have passed their expiry date
            if datetime.utcnow() < expiration:
                max_validity = max(max_validity, validity)
    return validity_interval(event) > max_validity


def validity_interval(event):
    event_time = panther_nanotime_to_python_datetime(event.get("time"))
    expires = golang_nanotime_to_python_datetime(
        event.deep_get("identity", "expires", default=None)
    )
    if not event_time and expires:
        return False
    interval = expires - event_time
    return interval


def title(event):
    identity = event.deep_get("identity", "user", default="<Cert with no User!?>")
    return (
        f"A Certificate for [{identity}] "
        f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}] "
        f"has been issued for an unusually long time: {validity_interval(event)!r} "
    )

Rule specification

AnalysisType: rule
Filename: teleport_long_lived_certs.py
RuleID: Teleport.LongLivedCerts
DisplayName: A long-lived cert was created
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - Teleport
Severity: Medium
Description: An unusually long-lived Teleport certificate was created
DedupPeriodMinutes: 60
Reports:
  MITRE ATT&CK:
    - TA0003:T1098
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  Teleport certificates are usually issued for a short period of time. Alert if long-lived certificates were created.
SummaryAttributes:
  - event
  - code
  - time
  - identity

Stages and Predicates

Fires on Gravitational.TeleportAudit events when the condition below holds.

Condition

  • event is cert.create

This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
eventeq
  • cert.create
field:"event" kind:eq value:"cert.create"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

FieldSource
useridentity.user
cluster_name

Response runbook

Teleport certificates are usually issued for a short period of time. Alert if long-lived certificates were created.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "cert_type": "user",
  "cluster_name": "teleport.example.com",
  "code": "TC000I",
  "ei": 0,
  "event": "cert.create",
  "identity": {
    "disallow_reissue": true,
    "expires": "2043-09-17T22:00:00.444444428Z",
    "impersonator": "bot-application",
    "kubernetes_cluster": "staging",
    "kubernetes_groups": [
      "application"
    ],
    "logins": [
      "-teleport-nologin-88888888-4444-4444-4444-222222222222",
      "-teleport-internal-join"
    ],
    "prev_identity_expires": "0001-01-01T00:00:00Z",
    "roles": [
      "application"
    ],
    "route_to_cluster": "teleport.example.com",
    "teleport_cluster": "teleport.example.com",
    "traits": {},
    "user": "bot-application"
  },
  "time": "2023-09-17 21:00:00.000000",
  "uid": "88888888-4444-4444-4444-222222222222"
}

A SAML Connector was created or modified

#
Severity
high
Log types
Gravitational.TeleportAudit
Tags
Teleport
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A SAML connector was created or modified

MITRE ATT&CK coverage

TacticTechniques
Resource Development

Detection logic

def rule(event):
    return event.get("event") == "saml.created"


def title(event):
    return (
        f"A SAML connector was created or updated by User [{event.get('user', '<UNKNOWN_USER>')}] "
        f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}]"
    )

Rule specification

AnalysisType: rule
Filename: teleport_saml_created.py
RuleID: Teleport.SAMLCreated
DisplayName: A SAML Connector was created or modified
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - Teleport
Severity: High
Description: A SAML connector was created or modified
DedupPeriodMinutes: 60
Reports:
  MITRE ATT&CK:
    - TA0042:T1585
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  When a SAML connector is modified, it can potentially change the trust model of the Teleport Cluster. Validate that these changes were expected and correct.
SummaryAttributes:
  - event
  - code
  - user
  - name

Stages and Predicates

Fires on Gravitational.TeleportAudit events when the condition below holds.

Condition

  • event is saml.created

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
eventeq
  • saml.created
field:"event" kind:eq value:"saml.created"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
cluster_name

Response runbook

When a SAML connector is modified, it can potentially change the trust model of the Teleport Cluster. Validate that these changes were expected and correct.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "cluster_name": "teleport.example.com",
  "code": "T8200I",
  "ei": 0,
  "event": "saml.created",
  "name": "okta",
  "time": "2023-09-19 18:00:00",
  "uid": "88888888-4444-4444-4444-222222222222",
  "user": "max.mustermann@zumbeispiel.example"
}

A Teleport Lock was created

#
Severity
informational
Log types
Gravitational.TeleportAudit
Tags
Teleport
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A Teleport Lock was created

Detection logic

def rule(event):
    return event.get("event") == "lock.created"


def title(event):
    return (
        f"A Teleport Lock was created by {event.get('updated_by', '<UNKNOWN_UPDATED_BY>')} "
        f"to Lock out user {event.get('target', {}).get('user', '<UNKNOWN_USER>')} "
        f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}]"
    )

Rule specification

AnalysisType: rule
Filename: teleport_lock_created.py
RuleID: Teleport.LockCreated
DisplayName: A Teleport Lock was created
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - Teleport
Severity: Info
Description: A Teleport Lock was created
DedupPeriodMinutes: 60
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  A Teleport Lock was created; this is an unusual administrative action. Investigate to understand why a Lock was created.
SummaryAttributes:
  - event
  - code
  - time
  - identity

Stages and Predicates

Fires on Gravitational.TeleportAudit events when the condition below holds.

Condition

  • event is lock.created

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
eventeq
  • lock.created
field:"event" kind:eq value:"lock.created"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

FieldSource
updated_by
usertarget.user
cluster_name

Response runbook

A Teleport Lock was created; this is an unusual administrative action. Investigate to understand why a Lock was created.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "cluster_name": "teleport.example.com",
  "code": "TLK00I",
  "ei": 0,
  "event": "lock.created",
  "expires": "0001-01-01T00:00:00Z",
  "name": "88888888-4444-4444-4444-222222222222",
  "target": {
    "user": "user-to-disable"
  },
  "time": "2023-09-21T00:00:00.000000Z",
  "uid": "88888888-4444-4444-4444-222222222222",
  "updated_by": "max.mustermann@example.com",
  "user": "max.mustermann@example.com"
}

A Teleport Role was modified or created

#
Severity
medium
Log types
Gravitational.TeleportAudit
Tags
Teleport
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A Teleport Role was modified or created

MITRE ATT&CK coverage

Detection logic

def rule(event):
    return event.get("event") == "role.created"


def title(event):
    return (
        f"User [{event.get('user', '<UNKNOWN_USER>')}] created Role "
        f"[{event.get('name', '<UNKNOWN_NAME>')}] "
        f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}]"
    )

Rule specification

AnalysisType: rule
Filename: teleport_role_created.py
RuleID: Teleport.RoleCreated
DisplayName: A Teleport Role was modified or created
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - Teleport
Severity: Medium
Description: A Teleport Role was modified or created
DedupPeriodMinutes: 60
Reports:
  MITRE ATT&CK:
    - TA0003:T1098.001
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  A Teleport Role was modified or created. Validate its legitimacy.
SummaryAttributes:
  - event
  - code
  - user
  - name

Stages and Predicates

Fires on Gravitational.TeleportAudit events when the condition below holds.

Condition

  • event is role.created

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
eventeq
  • role.created
field:"event" kind:eq value:"role.created"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
name
cluster_name

Response runbook

A Teleport Role was modified or created. Validate its legitimacy.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "cluster_name": "teleport.example.com",
  "code": "T9000I",
  "ei": 0,
  "event": "role.created",
  "expires": "0001-01-01T00:00:00Z",
  "name": "teleport-event-handler",
  "time": "2023-09-20T23:00:000.000000Z",
  "uid": "88888888-4444-4444-4444-222222222222",
  "user": "max.mustermann@example.com"
}

A user authenticated with SAML, but from an unknown company domain

#
Severity
medium
Log types
Gravitational.TeleportAudit
Tags
Teleport
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A user authenticated with SAML, but from an unknown company domain

MITRE ATT&CK coverage

TacticTechniques
Persistence

Detection logic

def rule(event):
    cluster = event.get("cluster_name", "")
    user_domain = event.get("user", "@").split("@")[-1]
    return (
        event.get("event") == "user.login"
        and event.get("success") is True
        and event.get("method") == "saml"
        and not cluster.endswith(user_domain)
    )


def title(event):
    return (
        f"User [{event.get('user', '<UNKNOWN_USER>')}] logged into "
        f"[{event.get('cluster_name', '<UNNAMED_CLUSTER>')}] using SAML from a different domain"
    )

Rule specification

AnalysisType: rule
Filename: teleport_saml_login_not_company_domain.py
RuleID: Teleport.SAMLLoginWithoutCompanyDomain
DisplayName: "A user authenticated with SAML, but from an unknown company domain"
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - Teleport
Severity: Medium
Description: "A user authenticated with SAML, but from an unknown company domain"
DedupPeriodMinutes: 60
Reports:
  MITRE ATT&CK:
    - TA0003:T1098
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  A user authenticated with SAML, but from an unknown company domain
SummaryAttributes:
  - event
  - code
  - user
  - method
  - mfa_device

Stages and Predicates

Fires on Gravitational.TeleportAudit events when all of the conditions below hold.

Condition

  • event is user.login
  • success is true
  • method is saml

This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.

Indicators

These rows show field, operator, and value matches.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
cluster_name

Response runbook

A user authenticated with SAML, but from an unknown company domain

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "cluster_name": "teleport.example.com",
  "code": "T1001I",
  "ei": 0,
  "event": "user.login",
  "method": "saml",
  "success": true,
  "time": "2023-09-18 00:00:00",
  "uid": "88888888-4444-4444-4444-222222222222",
  "user": "wtf.how@omghax.gravitational.io"
}

A User from the company domain(s) Logged in without SAML

#
Severity
medium
Log types
Gravitational.TeleportAudit
Tags
Teleport
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A User from the company domain(s) Logged in without SAML

MITRE ATT&CK coverage

TacticTechniques
Defense Impairment

Detection logic

def rule(event):
    user_domain = event.get("user", "@").split("@")[-1]
    cluster = event.get("cluster_name", "")
    return bool(
        event.get("event") == "user.login"
        and event.get("success") is True
        and cluster.endswith(user_domain)
        and event.get("method") != "saml"
    )


def title(event):
    return (
        f"User [{event.get('user', '<UNKNOWN_USER>')}] logged into "
        f"[{event.get('cluster_name', '<UNNAMED_CLUSTER>')}] without using SAML"
    )

Rule specification

AnalysisType: rule
Filename: teleport_company_domain_login_without_saml.py
RuleID: Teleport.CompanyDomainLoginWithoutSAML
DisplayName: "A User from the company domain(s) Logged in without SAML"
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - Teleport
Severity: Medium
Description: "A User from the company domain(s) Logged in without SAML"
DedupPeriodMinutes: 60
Reports:
  MITRE ATT&CK:
    - TA0005:T1562
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  A User from the company domain(s) Logged in without SAML
SummaryAttributes:
  - event
  - code
  - user
  - method
  - mfa_device

Stages and Predicates

Fires on Gravitational.TeleportAudit events when all of the conditions below hold.

Condition

  • event is user.login
  • success is true
  • method is not saml

This rule also runs imperative logic the parser cannot express as a filter. The conditions above are the structured part it could extract.

Indicators

These rows show field, operator, and value matches.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
cluster_name

Response runbook

A User from the company domain(s) Logged in without SAML

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "cluster_name": "teleport.example.com",
  "code": "T1001I",
  "ei": 0,
  "event": "user.login",
  "method": "local",
  "success": true,
  "time": "2023-09-18 00:00:00",
  "uid": "88888888-4444-4444-4444-222222222222",
  "user": "jane.doe@example.com"
}

Teleport Create User Accounts

#
Severity
high
Log types
Gravitational.TeleportAudit
Tags
SSH, Persistence:Create Account
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A user has been manually created, modified, or deleted

MITRE ATT&CK coverage

TacticTechniques
Persistence

Detection logic

from panther_base_helpers import pattern_match_list

USER_CREATE_PATTERNS = [
    "chage",  # user password expiry
    "passwd",  # change passwords for users
    "user*",  # create, modify, and delete users
]


def rule(event):
    # Filter the events
    if event.get("event") != "session.command":
        return False
    # Check that the program matches our list above
    return pattern_match_list(event.get("program", ""), USER_CREATE_PATTERNS)


def title(event):
    return (
        f"User [{event.get('user', '<UNKNOWN_USER>')}] has manually modified system users "
        f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}]"
    )

Rule specification

AnalysisType: rule
Filename: teleport_create_user_accounts.py
RuleID: "Teleport.CreateUserAccounts"
DisplayName: "Teleport Create User Accounts"
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - SSH
  - Persistence:Create Account
Reports:
  MITRE ATT&CK:
    - TA0003:T1136
Severity: High
Description: A user has been manually created, modified, or deleted
DedupPeriodMinutes: 15
Reference: https://goteleport.com/docs/management/admin/
Runbook: Analyze why it was manually created and delete it if necessary.
SummaryAttributes:
  - event
  - code
  - user
  - program
  - path
  - return_code
  - login
  - server_id
  - sid

Stages and Predicates

Fires on Gravitational.TeleportAudit events when all of the conditions below hold.

Condition

  • event is session.command
  • any of:
    • program matches the pattern chage
    • program matches the pattern passwd
    • program matches the pattern user*
Alert deduplication
repeat matches within 15m group into one alert

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
eventeq
  • session.command
field:"event" kind:eq value:"session.command"
programwildcard
  • chage
  • passwd
  • user*
field:"program" kind:wildcard

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
cluster_name

Response runbook

Analyze why it was manually created and delete it if necessary.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "argv": [
    "jacknew"
  ],
  "cgroup_id": 4294967567,
  "code": "T4000I",
  "ei": 105,
  "event": "session.command",
  "login": "root",
  "namespace": "default",
  "path": "/sbin/userdel",
  "pid": 8931,
  "ppid": 8930,
  "program": "userdel",
  "return_code": 0,
  "server_id": "e75992b4-9e27-456f-b1c9-7a32da83c661",
  "sid": "4244c271-8069-4679-a27e-f7c18f88ce45",
  "time": "2020-08-17T18:39:26.192Z",
  "uid": "346d3f61-a010-4871-84de-897f50b18118",
  "user": "panther"
}

Teleport Network Scan Initiated

#
Severity
medium
Log types
Gravitational.TeleportAudit
Tags
SSH, Discovery:Network Service Discovery
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A user has invoked a network scan that could potentially indicate enumeration of the network.

MITRE ATT&CK coverage

TacticTechniques
Discovery

Detection logic

SCAN_COMMANDS = {"arp", "arp-scan", "fping", "nmap"}


def rule(event):
    # Filter out commands
    if event.get("event") == "session.command" and not event.get("argv"):
        return False
    # Check that the program is in our watch list
    return event.get("program") in SCAN_COMMANDS


def title(event):
    return (
        f"User [{event.get('user', '<UNKNOWN_USER>')}] has issued a network scan with "
        f"[{event.get('program', '<UNKNOWN_PROGRAM>')}] "
        f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}]"
    )

Rule specification

AnalysisType: rule
Filename: teleport_network_scanning.py
RuleID: "Teleport.NetworkScanning"
DisplayName: "Teleport Network Scan Initiated"
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - SSH
  - Discovery:Network Service Discovery
Severity: Medium
Description: A user has invoked a network scan that could potentially indicate enumeration of the network.
DedupPeriodMinutes: 60
Reports:
  MITRE ATT&CK:
    - TA0007:T1046
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  Find related commands within the time window and determine if the command was invoked legitimately. Examine the arguments to determine how the command was used.
SummaryAttributes:
  - event
  - code
  - user
  - program
  - path
  - return_code
  - login
  - server_id
  - sid

Stages and Predicates

Fires on Gravitational.TeleportAudit events when all of the conditions below hold.

Condition

  • any of:
    • event is not session.command
    • argv is present
  • program is one of arp, arp-scan, fping, nmap

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
argvis_null(no value, null check)excludes:argv
eventeqsession.commandexcludes:event field:"event" value:"session.command"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
programin
  • arp
  • arp-scan
  • fping
  • nmap
field:"program" kind:in

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
program
cluster_name

Response runbook

Find related commands within the time window and determine if the command was invoked legitimately. Examine the arguments to determine how the command was used.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "argv": [
    "-v",
    "-iR",
    "100000",
    "-Pn",
    "-p",
    "80"
  ],
  "cgroup_id": 4294967672,
  "code": "T4000I",
  "ei": 16,
  "event": "session.command",
  "login": "root",
  "namespace": "default",
  "path": "/bin/nmap",
  "pid": 13555,
  "ppid": 13525,
  "program": "nmap",
  "return_code": 0,
  "server_id": "e75992b4-9e27-456f-b1c9-7a32da83c661",
  "sid": "a3562a0e-e57f-4273-9f69-eedb6cd029cb",
  "time": "2020-08-17T21:13:47.117Z",
  "uid": "c7f6367b-04bb-4b1d-9a3a-0497e8f4a650",
  "user": "panther"
}

Teleport Scheduled Jobs

#
Severity
medium
Log types
Gravitational.TeleportAudit
Tags
SSH, Execution:Scheduled Task/Job
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A user has manually edited the Linux crontab

MITRE ATT&CK coverage

TacticTechniques
Execution

Detection logic

def rule(event):
    # Filter the events
    if event.get("event") != "session.command":
        return False
    # Ignore list/read events
    if "-l" in event.get("argv", []):
        return False
    return event.get("program") == "crontab"


def title(event):
    return (
        f"User [{event.get('user', '<UNKNOWN_USER>')}] has modified scheduled jobs"
        f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}]"
    )

Rule specification

AnalysisType: rule
Filename: teleport_scheduled_jobs.py
RuleID: "Teleport.ScheduledJobs"
DisplayName: "Teleport Scheduled Jobs"
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - SSH
  - Execution:Scheduled Task/Job
Severity: Medium
Reports:
  MITRE ATT&CK:
    - TA0002:T1053
Description: A user has manually edited the Linux crontab
Threshold: 10
DedupPeriodMinutes: 15
Reference: https://goteleport.com/docs/management/admin/
Runbook: Validate the user behavior and rotate the host if necessary.
SummaryAttributes:
  - event
  - code
  - user
  - program
  - path
  - return_code
  - login
  - server_id
  - sid

Stages and Predicates

Fires on Gravitational.TeleportAudit events when all of the conditions below hold.

Condition

  • event is session.command
  • argv does not contain -l
  • program is crontab
Alert cadence
alerts after 10 matches within 15m

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
argvcontains-lexcludes:argv field:"argv" value:"-l"

Indicators

These rows show field, operator, and value matches.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
cluster_name

Response runbook

Validate the user behavior and rotate the host if necessary.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "argv": [],
  "cgroup_id": 4294967717,
  "code": "T4000I",
  "ei": 39,
  "event": "session.command",
  "login": "root",
  "namespace": "default",
  "path": "/bin/crontab",
  "pid": 18415,
  "ppid": 18413,
  "program": "crontab",
  "return_code": 0,
  "server_id": "e073ecab-6091-45da-83e4-80196e7bc659",
  "sid": "29a3d18c-2c05-453d-979a-2ed888a14788",
  "time": "2020-08-18T00:05:12.465Z",
  "uid": "83e88438-efbc-41a2-8135-b0157e0d14c0",
  "user": "panther"
}

Teleport SSH Auth Errors

#
Severity
medium
Log types
Gravitational.TeleportAudit
Tags
SSH, Credential Access:Brute Force
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A high volume of SSH errors could indicate a brute-force attack

MITRE ATT&CK coverage

TacticTechniques
Credential Access

Detection logic

def rule(event):
    return bool(event.get("error")) and event.get("event") == "auth"


def title(event):
    return (
        f"A high volume of SSH errors was detected from user "
        f"[{event.get('user', '<UNKNOWN_USER>')}] "
        f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}]"
    )

Rule specification

AnalysisType: rule
Filename: teleport_auth_errors.py
RuleID: "Teleport.AuthErrors"
DisplayName: "Teleport SSH Auth Errors"
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - SSH
  - Credential Access:Brute Force
Severity: Medium
Reports:
  MITRE ATT&CK:
    - TA0006:T1110
Description: A high volume of SSH errors could indicate a brute-force attack
Threshold: 10
DedupPeriodMinutes: 15
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  Check that the user making the failed requests legitimately tried logging in that many times.
SummaryAttributes:
  - event
  - code
  - user
  - program
  - path
  - return_code
  - login
  - server_id
  - sid

Stages and Predicates

Fires on Gravitational.TeleportAudit events when all of the conditions below hold.

Condition

  • error is present
  • event is auth
Alert cadence
alerts after 10 matches within 15m

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
erroris_not_null
  • (no value, null check)
field:"error" kind:is_not_null
eventeq
  • auth
field:"event" kind:eq value:"auth"

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
cluster_name

Response runbook

Check that the user making the failed requests legitimately tried logging in that many times.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "code": "T3007W",
  "error": "ssh: principal \"jack\" not in the set of valid principals for given certificate: [\"ec2-user\"]",
  "event": "auth",
  "success": false,
  "time": "2020-08-13T18:39:42Z",
  "uid": "53e474cc-db1c-45f1-a60d-b31239e20098",
  "user": "panther"
}

Teleport Suspicious Commands Executed

#
Severity
medium
Log types
Gravitational.TeleportAudit
Tags
SSH, Execution:Command and Scripting Interpreter
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A user has invoked a suspicious command that could lead to a host compromise

MITRE ATT&CK coverage

Detection logic

SUSPICIOUS_COMMANDS = {"nc", "wget"}


def rule(event):
    if event.get("event") != "session.command":
        return False
    # Ignore commands without arguments
    if not event.get("argv"):
        return False
    return event.get("program") in SUSPICIOUS_COMMANDS


def title(event):
    return (
        f"User [{event.get('user', '<UNKNOWN_USER>')}] has executed the command "
        f"[{event.get('program', '<UNKNOWN_PROGRAM>')}] "
        f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}]"
    )

Rule specification

AnalysisType: rule
Filename: teleport_suspicious_commands.py
RuleID: "Teleport.SuspiciousCommands"
DisplayName: "Teleport Suspicious Commands Executed"
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - SSH
  - Execution:Command and Scripting Interpreter
Severity: Medium
Description: A user has invoked a suspicious command that could lead to a host compromise
DedupPeriodMinutes: 60
Reports:
  MITRE ATT&CK:
    - TA0002:T1059
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  Find related commands within the time window and determine if the command was invoked legitimately. Examine the arguments to determine how the command was used and reach out to the user to verify the intentions.
SummaryAttributes:
  - event
  - code
  - user
  - program
  - path
  - return_code
  - login
  - server_id
  - sid

Stages and Predicates

Fires on Gravitational.TeleportAudit events when all of the conditions below hold.

Condition

  • event is session.command
  • argv is present
  • program is one of nc, wget

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
argvis_not_null
  • (no value, null check)
field:"argv" kind:is_not_null
eventeq
  • session.command
field:"event" kind:eq value:"session.command"
programin
  • nc
  • wget
field:"program" kind:in

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
program
cluster_name

Response runbook

Find related commands within the time window and determine if the command was invoked legitimately. Examine the arguments to determine how the command was used and reach out to the user to verify the intentions.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "argv": [
    "-l",
    "-p",
    "11434"
  ],
  "cgroup_id": 4294967537,
  "code": "T4000I",
  "ei": 15,
  "event": "session.command",
  "login": "root",
  "namespace": "default",
  "path": "/bin/nc",
  "pid": 7143,
  "ppid": 7115,
  "program": "nc",
  "return_code": 0,
  "server_id": "e75992b4-9e27-456f-b1c9-7a32da83c661",
  "sid": "8a3fc038-785b-43f3-8737-827b3e25fe5b",
  "time": "2020-08-17T17:40:37.491Z",
  "uid": "8eaf8f39-09d4-4a42-a22a-65163d2af702",
  "user": "panther"
}

User Logged in as root

#
Severity
medium
Log types
Gravitational.TeleportAudit
Tags
SSH, Execution:Command and Scripting Interpreter, Teleport
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A User logged in as root

MITRE ATT&CK coverage

Detection logic

def rule(event):
    return event.get("event") == "session.start" and event.get("login") == "root"


def title(event):
    return (
        f"User [{event.get('user', '<UNKNOWN_USER>')}] logged into "
        f"[{event.get('server_hostname', '<UNKNOWN_HOSTNAME>')}] as root "
        f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}]"
    )

Rule specification

AnalysisType: rule
Filename: teleport_root_login.py
RuleID: Teleport.RootLogin
DisplayName: User Logged in as root
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - SSH
  - Execution:Command and Scripting Interpreter
  - Teleport
Severity: Medium
Description: A User logged in as root
DedupPeriodMinutes: 60
Reports:
  MITRE ATT&CK:
    - TA0002:T1059
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  Use user accounts and policies, rather than root when logging in. With access to root, it is possible to evade auditing and logging.
SummaryAttributes:
  - event
  - code
  - user
  - login
  - server_hostname
  - server_labels

Stages and Predicates

Fires on Gravitational.TeleportAudit events when all of the conditions below hold.

Condition

  • event is session.start
  • login is root

Indicators

These rows show field, operator, and value matches.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
server_hostname
cluster_name

Response runbook

Use user accounts and policies, rather than root when logging in. With access to root, it is possible to evade auditing and logging.

Worked example

A sample event from the rule's unit tests that triggers a match.

Sample Test Event
{
  "addr_remote": "192.0.2.1:35194",
  "cluster_name": "teleport.example.com",
  "code": "t2000i",
  "ei": 0,
  "event": "session.start",
  "initial_command": [
    ""
  ],
  "login": "root",
  "namespace": "default",
  "proto": "ssh",
  "server_hostname": "ip-10-0-0-1.us-west-2.compute.internal",
  "server_id": "12345678-1111-2222-3333-54e9467ff0e6",
  "server_labels": {
    "env": "prod",
    "role": "project123"
  },
  "session_recording": "node-sync",
  "sid": "12345678-1111-2222-3333-54e9467ff0e6",
  "size": "80:25",
  "time": "2023-09-18 11:22:33",
  "uid": "12345678-1111-2222-3333-54e9467ff0e6",
  "user": "max.mustermann@zumbeispiel.example"
}

User Logged in wihout MFA

#
Severity
high
Log types
Gravitational.TeleportAudit
Tags
Teleport
Reference
goteleport.com
Source
github.com/panther-labs/panther-analysis

A local User logged in without MFA

MITRE ATT&CK coverage

TacticTechniques
Initial Access

Detection logic

SENSITIVE_LOCAL_USERS = ["breakglass"]


def rule(event):
    return (
        event.get("event") == "user.login"
        and event.get("success") == "true"
        and event.get("method") == "local"
        and not event.get("mfa_device")
    )


def severity(event):
    if event.get("user") in SENSITIVE_LOCAL_USERS:
        return "HIGH"
    return "MEDIUM"


def title(event):
    return (
        f"User [{event.get('user', '<UNKNOWN_USER>')}] logged into "
        f"[{event.get('cluster_name', '<UNNAMED_CLUSTER>')}] locally "
        f"without using MFA"
    )

Rule specification

AnalysisType: rule
Filename: teleport_local_user_login_without_mfa.py
RuleID: Teleport.LocalUserLoginWithoutMFA
DisplayName: User Logged in wihout MFA
Enabled: true
LogTypes:
  - Gravitational.TeleportAudit
Tags:
  - Teleport
Severity: High
Description: A local User logged in without MFA
DedupPeriodMinutes: 60
Reports:
  MITRE ATT&CK:
    - TA0001:T1078
Reference: https://goteleport.com/docs/management/admin/
Runbook: >
  A local user logged in without Multi-Factor Authentication
SummaryAttributes:
  - event
  - code
  - user
  - success
  - mfa_device

Stages and Predicates

Fires on Gravitational.TeleportAudit events when all of the conditions below hold.

Condition

  • event is user.login
  • success is true
  • method is local
  • mfa_device is empty

Indicators

These rows show field, operator, and value matches.

Output fields

Fields the rule emits when it matches, drawn from the rule's alert_context.

Field
user
cluster_name

Response runbook

A local user logged in without Multi-Factor Authentication