mirror of
https://github.com/marcel-dempers/docker-development-youtube-series.git
synced 2025-06-06 17:01:30 +00:00
4964 lines
210 KiB
YAML
4964 lines
210 KiB
YAML
---
|
|
# Source: falco/templates/serviceaccount.yaml
|
|
apiVersion: v1
|
|
kind: ServiceAccount
|
|
metadata:
|
|
name: falcosecurity
|
|
namespace: falco
|
|
labels:
|
|
app: falcosecurity
|
|
chart: "falco-1.18.5"
|
|
release: "falcosecurity"
|
|
heritage: "Helm"
|
|
---
|
|
# Source: falco/templates/configmap.yaml
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: falcosecurity
|
|
namespace: falco
|
|
labels:
|
|
app: falcosecurity
|
|
chart: "falco-1.18.5"
|
|
release: "falcosecurity"
|
|
heritage: "Helm"
|
|
data:
|
|
falco.yaml: |-
|
|
# File(s) or Directories containing Falco rules, loaded at startup.
|
|
# The name "rules_file" is only for backwards compatibility.
|
|
# If the entry is a file, it will be read directly. If the entry is a directory,
|
|
# every file in that directory will be read, in alphabetical order.
|
|
#
|
|
# falco_rules.yaml ships with the falco package and is overridden with
|
|
# every new software version. falco_rules.local.yaml is only created
|
|
# if it doesn't exist. If you want to customize the set of rules, add
|
|
# your customizations to falco_rules.local.yaml.
|
|
#
|
|
# The files will be read in the order presented here, so make sure if
|
|
# you have overrides they appear in later files.
|
|
rules_file:
|
|
- /etc/falco/falco_rules.yaml
|
|
- /etc/falco/falco_rules.local.yaml
|
|
- /etc/falco/k8s_audit_rules.yaml
|
|
- /etc/falco/rules.d
|
|
|
|
plugins:
|
|
- init_config: ""
|
|
library_path: libcloudtrail.so
|
|
name: cloudtrail
|
|
open_params: ""
|
|
- init_config: ""
|
|
library_path: libjson.so
|
|
name: json
|
|
|
|
# Setting this list to empty ensures that the above plugins are *not*
|
|
# loaded and enabled by default. If you want to use the above plugins,
|
|
# set a meaningful init_config/open_params for the cloudtrail plugin
|
|
# and then change this to:
|
|
# load_plugins: [cloudtrail, json]
|
|
load_plugins:
|
|
[]
|
|
|
|
# If true, the times displayed in log messages and output messages
|
|
# will be in ISO 8601. By default, times are displayed in the local
|
|
# time zone, as governed by /etc/localtime.
|
|
time_format_iso_8601: false
|
|
|
|
# Whether to output events in json or text
|
|
json_output: false
|
|
|
|
# When using json output, whether or not to include the "output" property
|
|
# itself (e.g. "File below a known binary directory opened for writing
|
|
# (user=root ....") in the json output.
|
|
json_include_output_property: true
|
|
|
|
# When using json output, whether or not to include the "tags" property
|
|
# itself in the json output. If set to true, outputs caused by rules
|
|
# with no tags will have a "tags" field set to an empty array. If set to
|
|
# false, the "tags" field will not be included in the json output at all.
|
|
json_include_tags_property: true
|
|
|
|
# Send information logs to stderr and/or syslog Note these are *not* security
|
|
# notification logs! These are just Falco lifecycle (and possibly error) logs.
|
|
log_stderr: true
|
|
log_syslog: true
|
|
|
|
# Minimum log level to include in logs. Note: these levels are
|
|
# separate from the priority field of rules. This refers only to the
|
|
# log level of falco's internal logging. Can be one of "emergency",
|
|
# "alert", "critical", "error", "warning", "notice", "info", "debug".
|
|
log_level: info
|
|
|
|
# Minimum rule priority level to load and run. All rules having a
|
|
# priority more severe than this level will be loaded/run. Can be one
|
|
# of "emergency", "alert", "critical", "error", "warning", "notice",
|
|
# "info", "debug".
|
|
priority: debug
|
|
|
|
# Whether or not output to any of the output channels below is
|
|
# buffered. Defaults to false
|
|
buffered_outputs: false
|
|
|
|
# Falco uses a shared buffer between the kernel and userspace to pass
|
|
# system call information. When Falco detects that this buffer is
|
|
# full and system calls have been dropped, it can take one or more of
|
|
# the following actions:
|
|
# - ignore: do nothing (default when list of actions is empty)
|
|
# - log: log a DEBUG message noting that the buffer was full
|
|
# - alert: emit a Falco alert noting that the buffer was full
|
|
# - exit: exit Falco with a non-zero rc
|
|
#
|
|
# Notice it is not possible to ignore and log/alert messages at the same time.
|
|
#
|
|
# The rate at which log/alert messages are emitted is governed by a
|
|
# token bucket. The rate corresponds to one message every 30 seconds
|
|
# with a burst of one message (by default).
|
|
#
|
|
# The messages are emitted when the percentage of dropped system calls
|
|
# with respect the number of events in the last second
|
|
# is greater than the given threshold (a double in the range [0, 1]).
|
|
#
|
|
# For debugging/testing it is possible to simulate the drops using
|
|
# the `simulate_drops: true`. In this case the threshold does not apply.
|
|
syscall_event_drops:
|
|
threshold: 0.1
|
|
actions:
|
|
- log
|
|
- alert
|
|
rate: 0.03333
|
|
max_burst: 1
|
|
|
|
# Falco uses a shared buffer between the kernel and userspace to receive
|
|
# the events (eg., system call information) in userspace.
|
|
#
|
|
# Anyways, the underlying libraries can also timeout for various reasons.
|
|
# For example, there could have been issues while reading an event.
|
|
# Or the particular event needs to be skipped.
|
|
# Normally, it's very unlikely that Falco does not receive events consecutively.
|
|
#
|
|
# Falco is able to detect such uncommon situation.
|
|
#
|
|
# Here you can configure the maximum number of consecutive timeouts without an event
|
|
# after which you want Falco to alert.
|
|
# By default this value is set to 1000 consecutive timeouts without an event at all.
|
|
# How this value maps to a time interval depends on the CPU frequency.
|
|
syscall_event_timeouts:
|
|
max_consecutives: 1000
|
|
|
|
# Falco continuously monitors outputs performance. When an output channel does not allow
|
|
# to deliver an alert within a given deadline, an error is reported indicating
|
|
# which output is blocking notifications.
|
|
# The timeout error will be reported to the log according to the above log_* settings.
|
|
# Note that the notification will not be discarded from the output queue; thus,
|
|
# output channels may indefinitely remain blocked.
|
|
# An output timeout error indeed indicate a misconfiguration issue or I/O problems
|
|
# that cannot be recovered by Falco and should be fixed by the user.
|
|
#
|
|
# The "output_timeout" value specifies the duration in milliseconds to wait before
|
|
# considering the deadline exceed.
|
|
#
|
|
# With a 2000ms default, the notification consumer can block the Falco output
|
|
# for up to 2 seconds without reaching the timeout.
|
|
output_timeout: 2000
|
|
|
|
# A throttling mechanism implemented as a token bucket limits the
|
|
# rate of falco notifications. This throttling is controlled by the following configuration
|
|
# options:
|
|
# - rate: the number of tokens (i.e. right to send a notification)
|
|
# gained per second. Defaults to 1.
|
|
# - max_burst: the maximum number of tokens outstanding. Defaults to 1000.
|
|
#
|
|
# With these defaults, falco could send up to 1000 notifications after
|
|
# an initial quiet period, and then up to 1 notification per second
|
|
# afterward. It would gain the full burst back after 1000 seconds of
|
|
# no activity.
|
|
outputs:
|
|
rate: 1
|
|
max_burst: 1000
|
|
|
|
# Where security notifications should go.
|
|
# Multiple outputs can be enabled.
|
|
syslog_output:
|
|
enabled: true
|
|
|
|
# If keep_alive is set to true, the file will be opened once and
|
|
# continuously written to, with each output message on its own
|
|
# line. If keep_alive is set to false, the file will be re-opened
|
|
# for each output message.
|
|
#
|
|
# Also, the file will be closed and reopened if falco is signaled with
|
|
# SIGUSR1.
|
|
file_output:
|
|
enabled: false
|
|
keep_alive: false
|
|
filename: ./events.txt
|
|
|
|
stdout_output:
|
|
enabled: true
|
|
|
|
# Falco contains an embedded webserver that can be used to accept K8s
|
|
# Audit Events. These config options control the behavior of that
|
|
# webserver. (By default, the webserver is disabled).
|
|
#
|
|
# The ssl_certificate is a combination SSL Certificate and corresponding
|
|
# key contained in a single file. You can generate a key/cert as follows:
|
|
#
|
|
# $ openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
|
|
# $ cat certificate.pem key.pem > falco.pem
|
|
# $ sudo cp falco.pem /etc/falco/falco.pem
|
|
webserver:
|
|
enabled: true
|
|
listen_port: 8765
|
|
k8s_audit_endpoint: /k8s-audit
|
|
k8s_healthz_endpoint: /healthz
|
|
ssl_enabled: false
|
|
ssl_certificate: /etc/falco/certs/server.pem
|
|
|
|
# If keep_alive is set to true, the program will be started once and
|
|
# continuously written to, with each output message on its own
|
|
# line. If keep_alive is set to false, the program will be re-spawned
|
|
# for each output message.
|
|
#
|
|
# Also, the program will be closed and reopened if falco is signaled with
|
|
# SIGUSR1.
|
|
program_output:
|
|
enabled: false
|
|
keep_alive: false
|
|
program: |
|
|
mail -s "Falco Notification" someone@example.com
|
|
|
|
http_output:
|
|
enabled: false
|
|
url: 'http://falcosecurity-falcosidekick:2801'
|
|
user_agent: falcosecurity/falco
|
|
|
|
|
|
# Falco supports running a gRPC server with two main binding types
|
|
# 1. Over the network with mandatory mutual TLS authentication (mTLS)
|
|
# 2. Over a local unix socket with no authentication
|
|
# By default, the gRPC server is disabled, with no enabled services (see grpc_output)
|
|
# please comment/uncomment and change accordingly the options below to configure it.
|
|
# Important note: if Falco has any troubles creating the gRPC server
|
|
# this information will be logged, however the main Falco daemon will not be stopped.
|
|
# gRPC server over network with (mandatory) mutual TLS configuration.
|
|
# This gRPC server is secure by default so you need to generate certificates and update their paths here.
|
|
# By default the gRPC server is off.
|
|
# You can configure the address to bind and expose it.
|
|
# By modifying the threadiness configuration you can fine-tune the number of threads (and context) it will use.
|
|
# grpc:
|
|
# enabled: true
|
|
# bind_address: "0.0.0.0:5060"
|
|
# # when threadiness is 0, Falco sets it by automatically figuring out the number of online cores
|
|
# threadiness: 0
|
|
# private_key: "/etc/falco/certs/server.key"
|
|
# cert_chain: "/etc/falco/certs/server.crt"
|
|
# root_certs: "/etc/falco/certs/ca.crt"
|
|
grpc:
|
|
enabled: false
|
|
threadiness: 0
|
|
bind_address: "unix:///var/run/falco/falco.sock"
|
|
|
|
|
|
# gRPC output service.
|
|
# By default it is off.
|
|
# By enabling this all the output events will be kept in memory until you read them with a gRPC client.
|
|
# Make sure to have a consumer for them or leave this disabled.
|
|
grpc_output:
|
|
enabled: false
|
|
|
|
# Container orchestrator metadata fetching params
|
|
metadata_download:
|
|
max_mb: 100
|
|
chunk_wait_us: 1000
|
|
watch_freq_sec: 1
|
|
|
|
application_rules.yaml: |-
|
|
#
|
|
# Copyright (C) 2019 The Falco Authors.
|
|
#
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
- required_engine_version: 2
|
|
|
|
################################################################
|
|
# By default all application-related rules are disabled for
|
|
# performance reasons. Depending on the application(s) you use,
|
|
# uncomment the corresponding rule definitions for
|
|
# application-specific activity monitoring.
|
|
################################################################
|
|
|
|
# Elasticsearch ports
|
|
- macro: elasticsearch_cluster_port
|
|
condition: fd.sport=9300
|
|
- macro: elasticsearch_api_port
|
|
condition: fd.sport=9200
|
|
- macro: elasticsearch_port
|
|
condition: elasticsearch_cluster_port or elasticsearch_api_port
|
|
|
|
# - rule: Elasticsearch unexpected network inbound traffic
|
|
# desc: inbound network traffic to elasticsearch on a port other than the standard ports
|
|
# condition: user.name = elasticsearch and inbound and not elasticsearch_port
|
|
# output: "Inbound network traffic to Elasticsearch on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# - rule: Elasticsearch unexpected network outbound traffic
|
|
# desc: outbound network traffic from elasticsearch on a port other than the standard ports
|
|
# condition: user.name = elasticsearch and outbound and not elasticsearch_cluster_port
|
|
# output: "Outbound network traffic from Elasticsearch on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
|
|
# ActiveMQ ports
|
|
- macro: activemq_cluster_port
|
|
condition: fd.sport=61616
|
|
- macro: activemq_web_port
|
|
condition: fd.sport=8161
|
|
- macro: activemq_port
|
|
condition: activemq_web_port or activemq_cluster_port
|
|
|
|
# - rule: Activemq unexpected network inbound traffic
|
|
# desc: inbound network traffic to activemq on a port other than the standard ports
|
|
# condition: user.name = activemq and inbound and not activemq_port
|
|
# output: "Inbound network traffic to ActiveMQ on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# - rule: Activemq unexpected network outbound traffic
|
|
# desc: outbound network traffic from activemq on a port other than the standard ports
|
|
# condition: user.name = activemq and outbound and not activemq_cluster_port
|
|
# output: "Outbound network traffic from ActiveMQ on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
|
|
# Cassandra ports
|
|
# https://docs.datastax.com/en/cassandra/2.0/cassandra/security/secureFireWall_r.html
|
|
- macro: cassandra_thrift_client_port
|
|
condition: fd.sport=9160
|
|
- macro: cassandra_cql_port
|
|
condition: fd.sport=9042
|
|
- macro: cassandra_cluster_port
|
|
condition: fd.sport=7000
|
|
- macro: cassandra_ssl_cluster_port
|
|
condition: fd.sport=7001
|
|
- macro: cassandra_jmx_port
|
|
condition: fd.sport=7199
|
|
- macro: cassandra_port
|
|
condition: >
|
|
cassandra_thrift_client_port or
|
|
cassandra_cql_port or cassandra_cluster_port or
|
|
cassandra_ssl_cluster_port or cassandra_jmx_port
|
|
|
|
# - rule: Cassandra unexpected network inbound traffic
|
|
# desc: inbound network traffic to cassandra on a port other than the standard ports
|
|
# condition: user.name = cassandra and inbound and not cassandra_port
|
|
# output: "Inbound network traffic to Cassandra on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# - rule: Cassandra unexpected network outbound traffic
|
|
# desc: outbound network traffic from cassandra on a port other than the standard ports
|
|
# condition: user.name = cassandra and outbound and not (cassandra_ssl_cluster_port or cassandra_cluster_port)
|
|
# output: "Outbound network traffic from Cassandra on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# Couchdb ports
|
|
# https://github.com/davisp/couchdb/blob/master/etc/couchdb/local.ini
|
|
- macro: couchdb_httpd_port
|
|
condition: fd.sport=5984
|
|
- macro: couchdb_httpd_ssl_port
|
|
condition: fd.sport=6984
|
|
# xxx can't tell what clustering ports are used. not writing rules for this
|
|
# yet.
|
|
|
|
# Fluentd ports
|
|
- macro: fluentd_http_port
|
|
condition: fd.sport=9880
|
|
- macro: fluentd_forward_port
|
|
condition: fd.sport=24224
|
|
|
|
# - rule: Fluentd unexpected network inbound traffic
|
|
# desc: inbound network traffic to fluentd on a port other than the standard ports
|
|
# condition: user.name = td-agent and inbound and not (fluentd_forward_port or fluentd_http_port)
|
|
# output: "Inbound network traffic to Fluentd on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# - rule: Tdagent unexpected network outbound traffic
|
|
# desc: outbound network traffic from fluentd on a port other than the standard ports
|
|
# condition: user.name = td-agent and outbound and not fluentd_forward_port
|
|
# output: "Outbound network traffic from Fluentd on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# Gearman ports
|
|
# http://gearman.org/protocol/
|
|
# - rule: Gearman unexpected network outbound traffic
|
|
# desc: outbound network traffic from gearman on a port other than the standard ports
|
|
# condition: user.name = gearman and outbound and outbound and not fd.sport = 4730
|
|
# output: "Outbound network traffic from Gearman on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# Zookeeper
|
|
- macro: zookeeper_port
|
|
condition: fd.sport = 2181
|
|
|
|
# Kafka ports
|
|
# - rule: Kafka unexpected network inbound traffic
|
|
# desc: inbound network traffic to kafka on a port other than the standard ports
|
|
# condition: user.name = kafka and inbound and fd.sport != 9092
|
|
# output: "Inbound network traffic to Kafka on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# Memcached ports
|
|
# - rule: Memcached unexpected network inbound traffic
|
|
# desc: inbound network traffic to memcached on a port other than the standard ports
|
|
# condition: user.name = memcached and inbound and fd.sport != 11211
|
|
# output: "Inbound network traffic to Memcached on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# - rule: Memcached unexpected network outbound traffic
|
|
# desc: any outbound network traffic from memcached. memcached never initiates outbound connections.
|
|
# condition: user.name = memcached and outbound
|
|
# output: "Unexpected Memcached outbound connection (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
|
|
# MongoDB ports
|
|
- macro: mongodb_server_port
|
|
condition: fd.sport = 27017
|
|
- macro: mongodb_shardserver_port
|
|
condition: fd.sport = 27018
|
|
- macro: mongodb_configserver_port
|
|
condition: fd.sport = 27019
|
|
- macro: mongodb_webserver_port
|
|
condition: fd.sport = 28017
|
|
|
|
# - rule: Mongodb unexpected network inbound traffic
|
|
# desc: inbound network traffic to mongodb on a port other than the standard ports
|
|
# condition: >
|
|
# user.name = mongodb and inbound and not (mongodb_server_port or
|
|
# mongodb_shardserver_port or mongodb_configserver_port or mongodb_webserver_port)
|
|
# output: "Inbound network traffic to MongoDB on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# MySQL ports
|
|
# - rule: Mysql unexpected network inbound traffic
|
|
# desc: inbound network traffic to mysql on a port other than the standard ports
|
|
# condition: user.name = mysql and inbound and fd.sport != 3306
|
|
# output: "Inbound network traffic to MySQL on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
|
|
# - rule: HTTP server unexpected network inbound traffic
|
|
# desc: inbound network traffic to a http server program on a port other than the standard ports
|
|
# condition: proc.name in (http_server_binaries) and inbound and fd.sport != 80 and fd.sport != 443
|
|
# output: "Inbound network traffic to HTTP Server on unexpected port (connection=%fd.name)"
|
|
# priority: WARNING
|
|
aws_cloudtrail_rules.yaml: |
|
|
#
|
|
# Copyright (C) 2022 The Falco Authors.
|
|
#
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
# All rules files related to plugins should require engine version 10
|
|
- required_engine_version: 10
|
|
|
|
# These rules can be read by cloudtrail plugin version 0.1.0, or
|
|
# anything semver-compatible.
|
|
- required_plugin_versions:
|
|
- name: cloudtrail
|
|
version: 0.2.3
|
|
- name: json
|
|
version: 0.2.2
|
|
|
|
# Note that this rule is disabled by default. It's useful only to
|
|
# verify that the cloudtrail plugin is sending events properly. The
|
|
# very broad condition evt.num > 0 only works because the rule source
|
|
# is limited to aws_cloudtrail. This ensures that the only events that
|
|
# are matched against the rule are from the cloudtrail plugin (or
|
|
# a different plugin with the same source).
|
|
- rule: All Cloudtrail Events
|
|
desc: Match all cloudtrail events.
|
|
condition:
|
|
evt.num > 0
|
|
output: Some Cloudtrail Event (evtnum=%evt.num info=%evt.plugininfo ts=%evt.time.iso8601 id=%ct.id error=%ct.error)
|
|
priority: DEBUG
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
source: aws_cloudtrail
|
|
enabled: false
|
|
|
|
- rule: Console Login Through Assume Role
|
|
desc: Detect a console login through Assume Role.
|
|
condition:
|
|
ct.name="ConsoleLogin" and not ct.error exists
|
|
and ct.user.identitytype="AssumedRole"
|
|
and json.value[/responseElements/ConsoleLogin]="Success"
|
|
output:
|
|
Detected a console login through Assume Role
|
|
(principal=%ct.user.principalid,
|
|
assumedRole=%ct.user.arn,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_console
|
|
- aws_iam
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Console Login Without MFA
|
|
desc: Detect a console login without MFA.
|
|
condition:
|
|
ct.name="ConsoleLogin" and not ct.error exists
|
|
and ct.user.identitytype!="AssumedRole"
|
|
and json.value[/responseElements/ConsoleLogin]="Success"
|
|
and json.value[/additionalEventData/MFAUsed]="No"
|
|
output:
|
|
Detected a console login without MFA
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region)
|
|
priority: CRITICAL
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_console
|
|
- aws_iam
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Console Root Login Without MFA
|
|
desc: Detect root console login without MFA.
|
|
condition:
|
|
ct.name="ConsoleLogin" and not ct.error exists
|
|
and json.value[/additionalEventData/MFAUsed]="No"
|
|
and ct.user.identitytype!="AssumedRole"
|
|
and json.value[/responseElements/ConsoleLogin]="Success"
|
|
and ct.user.identitytype="Root"
|
|
output:
|
|
Detected a root console login without MFA.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region)
|
|
priority: CRITICAL
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_console
|
|
- aws_iam
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Deactivate MFA for Root User
|
|
desc: Detect deactivating MFA configuration for root.
|
|
condition:
|
|
ct.name="DeactivateMFADevice" and not ct.error exists
|
|
and ct.user.identitytype="Root"
|
|
and ct.request.username="AWS ROOT USER"
|
|
output:
|
|
Multi Factor Authentication configuration has been disabled for root
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
MFA serial number=%ct.request.serialnumber)
|
|
priority: CRITICAL
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_iam
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Create AWS user
|
|
desc: Detect creation of a new AWS user.
|
|
condition:
|
|
ct.name="CreateUser" and not ct.error exists
|
|
output:
|
|
A new AWS user has been created
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
new user created=%ct.request.username)
|
|
priority: INFO
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_iam
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Create Group
|
|
desc: Detect creation of a new user group.
|
|
condition:
|
|
ct.name="CreateGroup" and not ct.error exists
|
|
output:
|
|
A new user group has been created.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
group name=%ct.request.groupname)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_iam
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Delete Group
|
|
desc: Detect deletion of a user group.
|
|
condition:
|
|
ct.name="DeleteGroup" and not ct.error exists
|
|
output:
|
|
A user group has been deleted.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
group name=%ct.request.groupname)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_iam
|
|
source: aws_cloudtrail
|
|
|
|
- rule: ECS Service Created
|
|
desc: Detect a new service is created in ECS.
|
|
condition:
|
|
ct.src="ecs.amazonaws.com" and
|
|
ct.name="CreateService" and
|
|
not ct.error exists
|
|
output:
|
|
A new service has been created in ECS
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
cluster=%ct.request.cluster,
|
|
service name=%ct.request.servicename,
|
|
task definition=%ct.request.taskdefinition)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_ecs
|
|
- aws_fargate
|
|
source: aws_cloudtrail
|
|
|
|
- rule: ECS Task Run or Started
|
|
desc: Detect a new task is started in ECS.
|
|
condition:
|
|
ct.src="ecs.amazonaws.com" and
|
|
(ct.name="RunTask" or ct.name="StartTask") and
|
|
not ct.error exists
|
|
output:
|
|
A new task has been started in ECS
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
cluster=%ct.request.cluster,
|
|
task definition=%ct.request.taskdefinition)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_ecs
|
|
- aws_fargate
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Create Lambda Function
|
|
desc: Detect creation of a Lambda function.
|
|
condition:
|
|
ct.name="CreateFunction20150331" and not ct.error exists
|
|
output:
|
|
Lambda function has been created.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
lambda function=%ct.request.functionname)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_lambda
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Update Lambda Function Code
|
|
desc: Detect updates to a Lambda function code.
|
|
condition:
|
|
ct.name="UpdateFunctionCode20150331v2" and not ct.error exists
|
|
output:
|
|
The code of a Lambda function has been updated.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
lambda function=%ct.request.functionname)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_lambda
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Update Lambda Function Configuration
|
|
desc: Detect updates to a Lambda function configuration.
|
|
condition:
|
|
ct.name="UpdateFunctionConfiguration20150331v2" and not ct.error exists
|
|
output:
|
|
The configuration of a Lambda function has been updated.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
lambda function=%ct.request.functionname)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_lambda
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Run Instances
|
|
desc: Detect launching of a specified number of instances.
|
|
condition:
|
|
ct.name="RunInstances" and not ct.error exists
|
|
output:
|
|
A number of instances have been launched.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
availability zone=%ct.request.availabilityzone,
|
|
subnet id=%ct.response.subnetid,
|
|
reservation id=%ct.response.reservationid)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_ec2
|
|
source: aws_cloudtrail
|
|
|
|
# Only instances launched on regions in this list are approved.
|
|
- list: approved_regions
|
|
items:
|
|
- us-east-0
|
|
|
|
- rule: Run Instances in Non-approved Region
|
|
desc: Detect launching of a specified number of instances in a non-approved region.
|
|
condition:
|
|
ct.name="RunInstances" and not ct.error exists and
|
|
not ct.region in (approved_regions)
|
|
output:
|
|
A number of instances have been launched in a non-approved region.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
availability zone=%ct.request.availabilityzone,
|
|
subnet id=%ct.response.subnetid,
|
|
reservation id=%ct.response.reservationid,
|
|
image id=%json.value[/responseElements/instancesSet/items/0/instanceId])
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_ec2
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Delete Bucket Encryption
|
|
desc: Detect deleting configuration to use encryption for bucket storage.
|
|
condition:
|
|
ct.name="DeleteBucketEncryption" and not ct.error exists
|
|
output:
|
|
A encryption configuration for a bucket has been deleted
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
bucket=%s3.bucket)
|
|
priority: CRITICAL
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_s3
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Delete Bucket Public Access Block
|
|
desc: Detect deleting blocking public access to bucket.
|
|
condition:
|
|
ct.name="PutBucketPublicAccessBlock" and not ct.error exists and
|
|
json.value[/requestParameters/publicAccessBlock]="" and
|
|
(json.value[/requestParameters/PublicAccessBlockConfiguration/RestrictPublicBuckets]=false or
|
|
json.value[/requestParameters/PublicAccessBlockConfiguration/BlockPublicPolicy]=false or
|
|
json.value[/requestParameters/PublicAccessBlockConfiguration/BlockPublicAcls]=false or
|
|
json.value[/requestParameters/PublicAccessBlockConfiguration/IgnorePublicAcls]=false)
|
|
output:
|
|
A public access block for a bucket has been deleted
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
bucket=%s3.bucket)
|
|
priority: CRITICAL
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_s3
|
|
source: aws_cloudtrail
|
|
|
|
- rule: List Buckets
|
|
desc: Detect listing of all S3 buckets.
|
|
condition:
|
|
ct.name="ListBuckets" and not ct.error exists
|
|
output:
|
|
A list of all S3 buckets has been requested.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
host=%ct.request.host)
|
|
priority: WARNING
|
|
enabled: false
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_s3
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Put Bucket ACL
|
|
desc: Detect setting the permissions on an existing bucket using access control lists.
|
|
condition:
|
|
ct.name="PutBucketAcl" and not ct.error exists
|
|
output:
|
|
The permissions on an existing bucket have been set using access control lists.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
bucket name=%s3.bucket)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_s3
|
|
source: aws_cloudtrail
|
|
|
|
- rule: Put Bucket Policy
|
|
desc: Detect applying an Amazon S3 bucket policy to an Amazon S3 bucket.
|
|
condition:
|
|
ct.name="PutBucketPolicy" and not ct.error exists
|
|
output:
|
|
An Amazon S3 bucket policy has been applied to an Amazon S3 bucket.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
bucket name=%s3.bucket,
|
|
policy=%ct.request.policy)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_s3
|
|
source: aws_cloudtrail
|
|
|
|
- rule: CloudTrail Trail Created
|
|
desc: Detect creation of a new trail.
|
|
condition:
|
|
ct.name="CreateTrail" and not ct.error exists
|
|
output:
|
|
A new trail has been created.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
trail name=%ct.request.name)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_cloudtrail
|
|
source: aws_cloudtrail
|
|
|
|
- rule: CloudTrail Logging Disabled
|
|
desc: The CloudTrail logging has been disabled, this could be potentially malicious.
|
|
condition:
|
|
ct.name="StopLogging" and not ct.error exists
|
|
output:
|
|
The CloudTrail logging has been disabled.
|
|
(requesting user=%ct.user,
|
|
requesting IP=%ct.srcip,
|
|
AWS region=%ct.region,
|
|
resource name=%ct.request.name)
|
|
priority: WARNING
|
|
tags:
|
|
- cloud
|
|
- aws
|
|
- aws_cloudtrail
|
|
source: aws_cloudtrail
|
|
falco_rules.local.yaml: |-
|
|
#
|
|
# Copyright (C) 2019 The Falco Authors.
|
|
#
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
####################
|
|
# Your custom rules!
|
|
####################
|
|
|
|
# Add new rules, like this one
|
|
# - rule: The program "sudo" is run in a container
|
|
# desc: An event will trigger every time you run sudo in a container
|
|
# condition: evt.type = execve and evt.dir=< and container.id != host and proc.name = sudo
|
|
# output: "Sudo run in container (user=%user.name %container.info parent=%proc.pname cmdline=%proc.cmdline)"
|
|
# priority: ERROR
|
|
# tags: [users, container]
|
|
|
|
# Or override/append to any rule, macro, or list from the Default Rules
|
|
falco_rules.yaml: |-
|
|
#
|
|
# Copyright (C) 2022 The Falco Authors.
|
|
#
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
# The latest Falco Engine version is 9.
|
|
# Starting with version 8, the Falco engine supports exceptions.
|
|
# However the Falco rules file does not use them by default.
|
|
- required_engine_version: 9
|
|
|
|
# Currently disabled as read/write are ignored syscalls. The nearly
|
|
# similar open_write/open_read check for files being opened for
|
|
# reading/writing.
|
|
# - macro: write
|
|
# condition: (syscall.type=write and fd.type in (file, directory))
|
|
# - macro: read
|
|
# condition: (syscall.type=read and evt.dir=> and fd.type in (file, directory))
|
|
|
|
- macro: open_write
|
|
condition: evt.type in (open,openat,openat2) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0
|
|
|
|
- macro: open_read
|
|
condition: evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='f' and fd.num>=0
|
|
|
|
- macro: open_directory
|
|
condition: evt.type in (open,openat,openat2) and evt.is_open_read=true and fd.typechar='d' and fd.num>=0
|
|
|
|
- macro: never_true
|
|
condition: (evt.num=0)
|
|
|
|
- macro: always_true
|
|
condition: (evt.num>=0)
|
|
|
|
# In some cases, such as dropped system call events, information about
|
|
# the process name may be missing. For some rules that really depend
|
|
# on the identity of the process performing an action such as opening
|
|
# a file, etc., we require that the process name be known.
|
|
- macro: proc_name_exists
|
|
condition: (proc.name!="<NA>")
|
|
|
|
- macro: rename
|
|
condition: evt.type in (rename, renameat, renameat2)
|
|
|
|
- macro: mkdir
|
|
condition: evt.type in (mkdir, mkdirat)
|
|
|
|
- macro: remove
|
|
condition: evt.type in (rmdir, unlink, unlinkat)
|
|
|
|
- macro: modify
|
|
condition: rename or remove
|
|
|
|
- macro: spawned_process
|
|
condition: evt.type in (execve, execveat) and evt.dir=<
|
|
|
|
- macro: create_symlink
|
|
condition: evt.type in (symlink, symlinkat) and evt.dir=<
|
|
|
|
- macro: create_hardlink
|
|
condition: evt.type in (link, linkat) and evt.dir=<
|
|
|
|
- macro: chmod
|
|
condition: (evt.type in (chmod, fchmod, fchmodat) and evt.dir=<)
|
|
|
|
# File categories
|
|
- macro: bin_dir
|
|
condition: fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin)
|
|
|
|
- macro: bin_dir_mkdir
|
|
condition: >
|
|
(evt.arg.path startswith /bin/ or
|
|
evt.arg.path startswith /sbin/ or
|
|
evt.arg.path startswith /usr/bin/ or
|
|
evt.arg.path startswith /usr/sbin/)
|
|
|
|
- macro: bin_dir_rename
|
|
condition: >
|
|
(evt.arg.path startswith /bin/ or
|
|
evt.arg.path startswith /sbin/ or
|
|
evt.arg.path startswith /usr/bin/ or
|
|
evt.arg.path startswith /usr/sbin/ or
|
|
evt.arg.name startswith /bin/ or
|
|
evt.arg.name startswith /sbin/ or
|
|
evt.arg.name startswith /usr/bin/ or
|
|
evt.arg.name startswith /usr/sbin/ or
|
|
evt.arg.oldpath startswith /bin/ or
|
|
evt.arg.oldpath startswith /sbin/ or
|
|
evt.arg.oldpath startswith /usr/bin/ or
|
|
evt.arg.oldpath startswith /usr/sbin/ or
|
|
evt.arg.newpath startswith /bin/ or
|
|
evt.arg.newpath startswith /sbin/ or
|
|
evt.arg.newpath startswith /usr/bin/ or
|
|
evt.arg.newpath startswith /usr/sbin/)
|
|
|
|
- macro: etc_dir
|
|
condition: fd.name startswith /etc/
|
|
|
|
# This detects writes immediately below / or any write anywhere below /root
|
|
- macro: root_dir
|
|
condition: (fd.directory=/ or fd.name startswith /root/)
|
|
|
|
- list: shell_binaries
|
|
items: [ash, bash, csh, ksh, sh, tcsh, zsh, dash]
|
|
|
|
- list: ssh_binaries
|
|
items: [
|
|
sshd, sftp-server, ssh-agent,
|
|
ssh, scp, sftp,
|
|
ssh-keygen, ssh-keysign, ssh-keyscan, ssh-add
|
|
]
|
|
|
|
- list: shell_mgmt_binaries
|
|
items: [add-shell, remove-shell]
|
|
|
|
- macro: shell_procs
|
|
condition: proc.name in (shell_binaries)
|
|
|
|
- list: coreutils_binaries
|
|
items: [
|
|
truncate, sha1sum, numfmt, fmt, fold, uniq, cut, who,
|
|
groups, csplit, sort, expand, printf, printenv, unlink, tee, chcon, stat,
|
|
basename, split, nice, "yes", whoami, sha224sum, hostid, users, stdbuf,
|
|
base64, unexpand, cksum, od, paste, nproc, pathchk, sha256sum, wc, test,
|
|
comm, arch, du, factor, sha512sum, md5sum, tr, runcon, env, dirname,
|
|
tsort, join, shuf, install, logname, pinky, nohup, expr, pr, tty, timeout,
|
|
tail, "[", seq, sha384sum, nl, head, id, mkfifo, sum, dircolors, ptx, shred,
|
|
tac, link, chroot, vdir, chown, touch, ls, dd, uname, "true", pwd, date,
|
|
chgrp, chmod, mktemp, cat, mknod, sync, ln, "false", rm, mv, cp, echo,
|
|
readlink, sleep, stty, mkdir, df, dir, rmdir, touch
|
|
]
|
|
|
|
# dpkg -L login | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
|
|
- list: login_binaries
|
|
items: [
|
|
login, systemd, '"(systemd)"', systemd-logind, su,
|
|
nologin, faillog, lastlog, newgrp, sg
|
|
]
|
|
|
|
# dpkg -L passwd | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
|
|
- list: passwd_binaries
|
|
items: [
|
|
shadowconfig, grpck, pwunconv, grpconv, pwck,
|
|
groupmod, vipw, pwconv, useradd, newusers, cppw, chpasswd, usermod,
|
|
groupadd, groupdel, grpunconv, chgpasswd, userdel, chage, chsh,
|
|
gpasswd, chfn, expiry, passwd, vigr, cpgr, adduser, addgroup, deluser, delgroup
|
|
]
|
|
|
|
# repoquery -l shadow-utils | grep bin | xargs ls -ld | grep -v '^d' |
|
|
# awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
|
|
- list: shadowutils_binaries
|
|
items: [
|
|
chage, gpasswd, lastlog, newgrp, sg, adduser, deluser, chpasswd,
|
|
groupadd, groupdel, addgroup, delgroup, groupmems, groupmod, grpck, grpconv, grpunconv,
|
|
newusers, pwck, pwconv, pwunconv, useradd, userdel, usermod, vigr, vipw, unix_chkpwd
|
|
]
|
|
|
|
- list: sysdigcloud_binaries
|
|
items: [setup-backend, dragent, sdchecks]
|
|
|
|
- list: docker_binaries
|
|
items: [docker, dockerd, exe, docker-compose, docker-entrypoi, docker-runc-cur, docker-current, dockerd-current]
|
|
|
|
- list: k8s_binaries
|
|
items: [hyperkube, skydns, kube2sky, exechealthz, weave-net, loopback, bridge, openshift-sdn, openshift]
|
|
|
|
- list: lxd_binaries
|
|
items: [lxd, lxcfs]
|
|
|
|
- list: http_server_binaries
|
|
items: [nginx, httpd, httpd-foregroun, lighttpd, apache, apache2]
|
|
|
|
- list: db_server_binaries
|
|
items: [mysqld, postgres, sqlplus]
|
|
|
|
- list: postgres_mgmt_binaries
|
|
items: [pg_dumpall, pg_ctl, pg_lsclusters, pg_ctlcluster]
|
|
|
|
- list: nosql_server_binaries
|
|
items: [couchdb, memcached, redis-server, rabbitmq-server, mongod]
|
|
|
|
- list: gitlab_binaries
|
|
items: [gitlab-shell, gitlab-mon, gitlab-runner-b, git]
|
|
|
|
- list: interpreted_binaries
|
|
items: [lua, node, perl, perl5, perl6, php, python, python2, python3, ruby, tcl]
|
|
|
|
- macro: interpreted_procs
|
|
condition: >
|
|
(proc.name in (interpreted_binaries))
|
|
|
|
- macro: server_procs
|
|
condition: proc.name in (http_server_binaries, db_server_binaries, docker_binaries, sshd)
|
|
|
|
# The explicit quotes are needed to avoid the - characters being
|
|
# interpreted by the filter expression.
|
|
- list: rpm_binaries
|
|
items: [dnf, rpm, rpmkey, yum, '"75-system-updat"', rhsmcertd-worke, rhsmcertd, subscription-ma,
|
|
repoquery, rpmkeys, rpmq, yum-cron, yum-config-mana, yum-debug-dump,
|
|
abrt-action-sav, rpmdb_stat, microdnf, rhn_check, yumdb]
|
|
|
|
- list: openscap_rpm_binaries
|
|
items: [probe_rpminfo, probe_rpmverify, probe_rpmverifyfile, probe_rpmverifypackage]
|
|
|
|
- macro: rpm_procs
|
|
condition: (proc.name in (rpm_binaries, openscap_rpm_binaries) or proc.name in (salt-minion))
|
|
|
|
- list: deb_binaries
|
|
items: [dpkg, dpkg-preconfigu, dpkg-reconfigur, dpkg-divert, apt, apt-get, aptitude,
|
|
frontend, preinst, add-apt-reposit, apt-auto-remova, apt-key,
|
|
apt-listchanges, unattended-upgr, apt-add-reposit, apt-cache, apt.systemd.dai
|
|
]
|
|
|
|
# The truncated dpkg-preconfigu is intentional, process names are
|
|
# truncated at the falcosecurity-libs level.
|
|
- list: package_mgmt_binaries
|
|
items: [rpm_binaries, deb_binaries, update-alternat, gem, npm, pip, pip3, sane-utils.post, alternatives, chef-client, apk, snapd]
|
|
|
|
- macro: package_mgmt_procs
|
|
condition: proc.name in (package_mgmt_binaries)
|
|
|
|
- macro: package_mgmt_ancestor_procs
|
|
condition: proc.pname in (package_mgmt_binaries) or
|
|
proc.aname[2] in (package_mgmt_binaries) or
|
|
proc.aname[3] in (package_mgmt_binaries) or
|
|
proc.aname[4] in (package_mgmt_binaries)
|
|
|
|
- macro: coreos_write_ssh_dir
|
|
condition: (proc.name=update-ssh-keys and fd.name startswith /home/core/.ssh)
|
|
|
|
- macro: run_by_package_mgmt_binaries
|
|
condition: proc.aname in (package_mgmt_binaries, needrestart)
|
|
|
|
- list: ssl_mgmt_binaries
|
|
items: [ca-certificates]
|
|
|
|
- list: dhcp_binaries
|
|
items: [dhclient, dhclient-script, 11-dhclient]
|
|
|
|
# A canonical set of processes that run other programs with different
|
|
# privileges or as a different user.
|
|
- list: userexec_binaries
|
|
items: [sudo, su, suexec, critical-stack, dzdo]
|
|
|
|
- list: known_setuid_binaries
|
|
items: [
|
|
sshd, dbus-daemon-lau, ping, ping6, critical-stack-, pmmcli,
|
|
filemng, PassengerAgent, bwrap, osdetect, nginxmng, sw-engine-fpm,
|
|
start-stop-daem
|
|
]
|
|
|
|
- list: user_mgmt_binaries
|
|
items: [login_binaries, passwd_binaries, shadowutils_binaries]
|
|
|
|
- list: dev_creation_binaries
|
|
items: [blkid, rename_device, update_engine, sgdisk]
|
|
|
|
- list: hids_binaries
|
|
items: [aide, aide.wrapper, update-aide.con, logcheck, syslog-summary, osqueryd, ossec-syscheckd]
|
|
|
|
- list: vpn_binaries
|
|
items: [openvpn]
|
|
|
|
- list: nomachine_binaries
|
|
items: [nxexec, nxnode.bin, nxserver.bin, nxclient.bin]
|
|
|
|
- macro: system_procs
|
|
condition: proc.name in (coreutils_binaries, user_mgmt_binaries)
|
|
|
|
- list: mail_binaries
|
|
items: [
|
|
sendmail, sendmail-msp, postfix, procmail, exim4,
|
|
pickup, showq, mailq, dovecot, imap-login, imap,
|
|
mailmng-core, pop3-login, dovecot-lda, pop3
|
|
]
|
|
|
|
- list: mail_config_binaries
|
|
items: [
|
|
update_conf, parse_mc, makemap_hash, newaliases, update_mk, update_tlsm4,
|
|
update_db, update_mc, ssmtp.postinst, mailq, postalias, postfix.config.,
|
|
postfix.config, postfix-script, postconf
|
|
]
|
|
|
|
- list: sensitive_file_names
|
|
items: [/etc/shadow, /etc/sudoers, /etc/pam.conf, /etc/security/pwquality.conf]
|
|
|
|
- list: sensitive_directory_names
|
|
items: [/, /etc, /etc/, /root, /root/]
|
|
|
|
- macro: sensitive_files
|
|
condition: >
|
|
fd.name startswith /etc and
|
|
(fd.name in (sensitive_file_names)
|
|
or fd.directory in (/etc/sudoers.d, /etc/pam.d))
|
|
|
|
# Indicates that the process is new. Currently detected using time
|
|
# since process was started, using a threshold of 5 seconds.
|
|
- macro: proc_is_new
|
|
condition: proc.duration <= 5000000000
|
|
|
|
# Network
|
|
- macro: inbound
|
|
condition: >
|
|
(((evt.type in (accept,listen) and evt.dir=<) or
|
|
(evt.type in (recvfrom,recvmsg) and evt.dir=< and
|
|
fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and
|
|
(fd.typechar = 4 or fd.typechar = 6) and
|
|
(fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and
|
|
(evt.rawres >= 0 or evt.res = EINPROGRESS))
|
|
|
|
# RFC1918 addresses were assigned for private network usage
|
|
- list: rfc_1918_addresses
|
|
items: ['"10.0.0.0/8"', '"172.16.0.0/12"', '"192.168.0.0/16"']
|
|
|
|
- macro: outbound
|
|
condition: >
|
|
(((evt.type = connect and evt.dir=<) or
|
|
(evt.type in (sendto,sendmsg) and evt.dir=< and
|
|
fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and
|
|
(fd.typechar = 4 or fd.typechar = 6) and
|
|
(fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8" and not fd.snet in (rfc_1918_addresses)) and
|
|
(evt.rawres >= 0 or evt.res = EINPROGRESS))
|
|
|
|
# Very similar to inbound/outbound, but combines the tests together
|
|
# for efficiency.
|
|
- macro: inbound_outbound
|
|
condition: >
|
|
((((evt.type in (accept,listen,connect) and evt.dir=<)) and
|
|
(fd.typechar = 4 or fd.typechar = 6)) and
|
|
(fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and
|
|
(evt.rawres >= 0 or evt.res = EINPROGRESS))
|
|
|
|
- macro: ssh_port
|
|
condition: fd.sport=22
|
|
|
|
# In a local/user rules file, you could override this macro to
|
|
# enumerate the servers for which ssh connections are allowed. For
|
|
# example, you might have a ssh gateway host for which ssh connections
|
|
# are allowed.
|
|
#
|
|
# In the main falco rules file, there isn't any way to know the
|
|
# specific hosts for which ssh access is allowed, so this macro just
|
|
# repeats ssh_port, which effectively allows ssh from all hosts. In
|
|
# the overridden macro, the condition would look something like
|
|
# "fd.sip="a.b.c.d" or fd.sip="e.f.g.h" or ..."
|
|
- macro: allowed_ssh_hosts
|
|
condition: ssh_port
|
|
|
|
- rule: Disallowed SSH Connection
|
|
desc: Detect any new ssh connection to a host other than those in an allowed group of hosts
|
|
condition: (inbound_outbound) and ssh_port and not allowed_ssh_hosts
|
|
output: Disallowed SSH Connection (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [network, mitre_remote_service]
|
|
|
|
# These rules and supporting macros are more of an example for how to
|
|
# use the fd.*ip and fd.*ip.name fields to match connection
|
|
# information against ips, netmasks, and complete domain names.
|
|
#
|
|
# To use this rule, you should modify consider_all_outbound_conns and
|
|
# populate allowed_{source,destination}_{ipaddrs,networks,domains} with the
|
|
# values that make sense for your environment.
|
|
- macro: consider_all_outbound_conns
|
|
condition: (never_true)
|
|
|
|
# Note that this can be either individual IPs or netmasks
|
|
- list: allowed_outbound_destination_ipaddrs
|
|
items: ['"127.0.0.1"', '"8.8.8.8"']
|
|
|
|
- list: allowed_outbound_destination_networks
|
|
items: ['"127.0.0.1/8"']
|
|
|
|
- list: allowed_outbound_destination_domains
|
|
items: [google.com, www.yahoo.com]
|
|
|
|
- rule: Unexpected outbound connection destination
|
|
desc: Detect any outbound connection to a destination outside of an allowed set of ips, networks, or domain names
|
|
condition: >
|
|
consider_all_outbound_conns and outbound and not
|
|
((fd.sip in (allowed_outbound_destination_ipaddrs)) or
|
|
(fd.snet in (allowed_outbound_destination_networks)) or
|
|
(fd.sip.name in (allowed_outbound_destination_domains)))
|
|
output: Disallowed outbound connection destination (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [network]
|
|
|
|
- macro: consider_all_inbound_conns
|
|
condition: (never_true)
|
|
|
|
- list: allowed_inbound_source_ipaddrs
|
|
items: ['"127.0.0.1"']
|
|
|
|
- list: allowed_inbound_source_networks
|
|
items: ['"127.0.0.1/8"', '"10.0.0.0/8"']
|
|
|
|
- list: allowed_inbound_source_domains
|
|
items: [google.com]
|
|
|
|
- rule: Unexpected inbound connection source
|
|
desc: Detect any inbound connection from a source outside of an allowed set of ips, networks, or domain names
|
|
condition: >
|
|
consider_all_inbound_conns and inbound and not
|
|
((fd.cip in (allowed_inbound_source_ipaddrs)) or
|
|
(fd.cnet in (allowed_inbound_source_networks)) or
|
|
(fd.cip.name in (allowed_inbound_source_domains)))
|
|
output: Disallowed inbound connection source (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [network]
|
|
|
|
- list: bash_config_filenames
|
|
items: [.bashrc, .bash_profile, .bash_history, .bash_login, .bash_logout, .inputrc, .profile]
|
|
|
|
- list: bash_config_files
|
|
items: [/etc/profile, /etc/bashrc]
|
|
|
|
# Covers both csh and tcsh
|
|
- list: csh_config_filenames
|
|
items: [.cshrc, .login, .logout, .history, .tcshrc, .cshdirs]
|
|
|
|
- list: csh_config_files
|
|
items: [/etc/csh.cshrc, /etc/csh.login]
|
|
|
|
- list: zsh_config_filenames
|
|
items: [.zshenv, .zprofile, .zshrc, .zlogin, .zlogout]
|
|
|
|
- list: shell_config_filenames
|
|
items: [bash_config_filenames, csh_config_filenames, zsh_config_filenames]
|
|
|
|
- list: shell_config_files
|
|
items: [bash_config_files, csh_config_files]
|
|
|
|
- list: shell_config_directories
|
|
items: [/etc/zsh]
|
|
|
|
- rule: Modify Shell Configuration File
|
|
desc: Detect attempt to modify shell configuration files
|
|
condition: >
|
|
open_write and
|
|
(fd.filename in (shell_config_filenames) or
|
|
fd.name in (shell_config_files) or
|
|
fd.directory in (shell_config_directories))
|
|
and not proc.name in (shell_binaries)
|
|
and not exe_running_docker_save
|
|
output: >
|
|
a shell configuration file has been modified (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pcmdline=%proc.pcmdline file=%fd.name container_id=%container.id image=%container.image.repository)
|
|
priority:
|
|
WARNING
|
|
tags: [file, mitre_persistence]
|
|
|
|
# This rule is not enabled by default, as there are many legitimate
|
|
# readers of shell config files. If you want to enable it, modify the
|
|
# following macro.
|
|
|
|
- macro: consider_shell_config_reads
|
|
condition: (never_true)
|
|
|
|
- rule: Read Shell Configuration File
|
|
desc: Detect attempts to read shell configuration files by non-shell programs
|
|
condition: >
|
|
open_read and
|
|
consider_shell_config_reads and
|
|
(fd.filename in (shell_config_filenames) or
|
|
fd.name in (shell_config_files) or
|
|
fd.directory in (shell_config_directories)) and
|
|
(not proc.name in (shell_binaries))
|
|
output: >
|
|
a shell configuration file was read by a non-shell program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
|
|
priority:
|
|
WARNING
|
|
tags: [file, mitre_discovery]
|
|
|
|
- macro: consider_all_cron_jobs
|
|
condition: (never_true)
|
|
|
|
- macro: user_known_cron_jobs
|
|
condition: (never_true)
|
|
|
|
- rule: Schedule Cron Jobs
|
|
desc: Detect cron jobs scheduled
|
|
condition: >
|
|
((open_write and fd.name startswith /etc/cron) or
|
|
(spawned_process and proc.name = "crontab")) and
|
|
consider_all_cron_jobs and
|
|
not user_known_cron_jobs
|
|
output: >
|
|
Cron jobs were scheduled to run (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline
|
|
file=%fd.name container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
|
priority:
|
|
NOTICE
|
|
tags: [file, mitre_persistence]
|
|
|
|
# Use this to test whether the event occurred within a container.
|
|
|
|
# When displaying container information in the output field, use
|
|
# %container.info, without any leading term (file=%fd.name
|
|
# %container.info user=%user.name user_loginuid=%user.loginuid, and not file=%fd.name
|
|
# container=%container.info user=%user.name user_loginuid=%user.loginuid). The output will change
|
|
# based on the context and whether or not -pk/-pm/-pc was specified on
|
|
# the command line.
|
|
- macro: container
|
|
condition: (container.id != host)
|
|
|
|
- macro: container_started
|
|
condition: >
|
|
((evt.type = container or
|
|
(spawned_process and proc.vpid=1)) and
|
|
container.image.repository != incomplete)
|
|
|
|
- macro: interactive
|
|
condition: >
|
|
((proc.aname=sshd and proc.name != sshd) or
|
|
proc.name=systemd-logind or proc.name=login)
|
|
|
|
- list: cron_binaries
|
|
items: [anacron, cron, crond, crontab]
|
|
|
|
# https://github.com/liske/needrestart
|
|
- list: needrestart_binaries
|
|
items: [needrestart, 10-dpkg, 20-rpm, 30-pacman]
|
|
|
|
# Possible scripts run by sshkit
|
|
- list: sshkit_script_binaries
|
|
items: [10_etc_sudoers., 10_passwd_group]
|
|
|
|
- list: plesk_binaries
|
|
items: [sw-engine, sw-engine-fpm, sw-engine-kv, filemng, f2bmng]
|
|
|
|
# System users that should never log into a system. Consider adding your own
|
|
# service users (e.g. 'apache' or 'mysqld') here.
|
|
- macro: system_users
|
|
condition: user.name in (bin, daemon, games, lp, mail, nobody, sshd, sync, uucp, www-data)
|
|
|
|
- macro: httpd_writing_ssl_conf
|
|
condition: >
|
|
(proc.pname=run-httpd and
|
|
(proc.cmdline startswith "sed -ri" or proc.cmdline startswith "sed -i") and
|
|
(fd.name startswith /etc/httpd/conf.d/ or fd.name startswith /etc/httpd/conf))
|
|
|
|
- macro: userhelper_writing_etc_security
|
|
condition: (proc.name=userhelper and fd.name startswith /etc/security)
|
|
|
|
- macro: ansible_running_python
|
|
condition: (proc.name in (python, pypy, python3) and proc.cmdline contains ansible)
|
|
|
|
- macro: python_running_chef
|
|
condition: (proc.name=python and (proc.cmdline contains yum-dump.py or proc.cmdline="python /usr/bin/chef-monitor.py"))
|
|
|
|
- macro: python_running_denyhosts
|
|
condition: >
|
|
(proc.name=python and
|
|
(proc.cmdline contains /usr/sbin/denyhosts or
|
|
proc.cmdline contains /usr/local/bin/denyhosts.py))
|
|
|
|
# Qualys seems to run a variety of shell subprocesses, at various
|
|
# levels. This checks at a few levels without the cost of a full
|
|
# proc.aname, which traverses the full parent hierarchy.
|
|
- macro: run_by_qualys
|
|
condition: >
|
|
(proc.pname=qualys-cloud-ag or
|
|
proc.aname[2]=qualys-cloud-ag or
|
|
proc.aname[3]=qualys-cloud-ag or
|
|
proc.aname[4]=qualys-cloud-ag)
|
|
|
|
- macro: run_by_sumologic_securefiles
|
|
condition: >
|
|
((proc.cmdline="usermod -a -G sumologic_collector" or
|
|
proc.cmdline="groupadd sumologic_collector") and
|
|
(proc.pname=secureFiles.sh and proc.aname[2]=java))
|
|
|
|
- macro: run_by_yum
|
|
condition: ((proc.pname=sh and proc.aname[2]=yum) or
|
|
(proc.aname[2]=sh and proc.aname[3]=yum))
|
|
|
|
- macro: run_by_ms_oms
|
|
condition: >
|
|
(proc.aname[3] startswith omsagent- or
|
|
proc.aname[3] startswith scx-)
|
|
|
|
- macro: run_by_google_accounts_daemon
|
|
condition: >
|
|
(proc.aname[1] startswith google_accounts or
|
|
proc.aname[2] startswith google_accounts or
|
|
proc.aname[3] startswith google_accounts)
|
|
|
|
# Chef is similar.
|
|
- macro: run_by_chef
|
|
condition: (proc.aname[2]=chef_command_wr or proc.aname[3]=chef_command_wr or
|
|
proc.aname[2]=chef-client or proc.aname[3]=chef-client or
|
|
proc.name=chef-client)
|
|
|
|
- macro: run_by_adclient
|
|
condition: (proc.aname[2]=adclient or proc.aname[3]=adclient or proc.aname[4]=adclient)
|
|
|
|
- macro: run_by_centrify
|
|
condition: (proc.aname[2]=centrify or proc.aname[3]=centrify or proc.aname[4]=centrify)
|
|
|
|
# Also handles running semi-indirectly via scl
|
|
- macro: run_by_foreman
|
|
condition: >
|
|
(user.name=foreman and
|
|
((proc.pname in (rake, ruby, scl) and proc.aname[5] in (tfm-rake,tfm-ruby)) or
|
|
(proc.pname=scl and proc.aname[2] in (tfm-rake,tfm-ruby))))
|
|
|
|
- macro: java_running_sdjagent
|
|
condition: proc.name=java and proc.cmdline contains sdjagent.jar
|
|
|
|
- macro: kubelet_running_loopback
|
|
condition: (proc.pname=kubelet and proc.name=loopback)
|
|
|
|
- macro: python_mesos_marathon_scripting
|
|
condition: (proc.pcmdline startswith "python3 /marathon-lb/marathon_lb.py")
|
|
|
|
- macro: splunk_running_forwarder
|
|
condition: (proc.pname=splunkd and proc.cmdline startswith "sh -c /opt/splunkforwarder")
|
|
|
|
- macro: parent_supervise_running_multilog
|
|
condition: (proc.name=multilog and proc.pname=supervise)
|
|
|
|
- macro: supervise_writing_status
|
|
condition: (proc.name in (supervise,svc) and fd.name startswith "/etc/sb/")
|
|
|
|
- macro: pki_realm_writing_realms
|
|
condition: (proc.cmdline startswith "bash /usr/local/lib/pki/pki-realm" and fd.name startswith /etc/pki/realms)
|
|
|
|
- macro: htpasswd_writing_passwd
|
|
condition: (proc.name=htpasswd and fd.name=/etc/nginx/.htpasswd)
|
|
|
|
- macro: lvprogs_writing_conf
|
|
condition: >
|
|
(proc.name in (dmeventd,lvcreate,pvscan,lvs) and
|
|
(fd.name startswith /etc/lvm/archive or
|
|
fd.name startswith /etc/lvm/backup or
|
|
fd.name startswith /etc/lvm/cache))
|
|
|
|
- macro: ovsdb_writing_openvswitch
|
|
condition: (proc.name=ovsdb-server and fd.directory=/etc/openvswitch)
|
|
|
|
- macro: perl_running_plesk
|
|
condition: (proc.cmdline startswith "perl /opt/psa/admin/bin/plesk_agent_manager" or
|
|
proc.pcmdline startswith "perl /opt/psa/admin/bin/plesk_agent_manager")
|
|
|
|
- macro: perl_running_updmap
|
|
condition: (proc.cmdline startswith "perl /usr/bin/updmap")
|
|
|
|
- macro: perl_running_centrifydc
|
|
condition: (proc.cmdline startswith "perl /usr/share/centrifydc")
|
|
|
|
- macro: runuser_reading_pam
|
|
condition: (proc.name=runuser and fd.directory=/etc/pam.d)
|
|
|
|
# CIS Linux Benchmark program
|
|
- macro: linux_bench_reading_etc_shadow
|
|
condition: ((proc.aname[2]=linux-bench and
|
|
proc.name in (awk,cut,grep)) and
|
|
(fd.name=/etc/shadow or
|
|
fd.directory=/etc/pam.d))
|
|
|
|
- macro: parent_ucf_writing_conf
|
|
condition: (proc.pname=ucf and proc.aname[2]=frontend)
|
|
|
|
- macro: consul_template_writing_conf
|
|
condition: >
|
|
((proc.name=consul-template and fd.name startswith /etc/haproxy) or
|
|
(proc.name=reload.sh and proc.aname[2]=consul-template and fd.name startswith /etc/ssl))
|
|
|
|
- macro: countly_writing_nginx_conf
|
|
condition: (proc.cmdline startswith "nodejs /opt/countly/bin" and fd.name startswith /etc/nginx)
|
|
|
|
- list: ms_oms_binaries
|
|
items: [omi.postinst, omsconfig.posti, scx.postinst, omsadmin.sh, omiagent]
|
|
|
|
- macro: ms_oms_writing_conf
|
|
condition: >
|
|
((proc.name in (omiagent,omsagent,in_heartbeat_r*,omsadmin.sh,PerformInventor,dsc_host)
|
|
or proc.pname in (ms_oms_binaries)
|
|
or proc.aname[2] in (ms_oms_binaries))
|
|
and (fd.name startswith /etc/opt/omi or fd.name startswith /etc/opt/microsoft/omsagent))
|
|
|
|
- macro: ms_scx_writing_conf
|
|
condition: (proc.name in (GetLinuxOS.sh) and fd.name startswith /etc/opt/microsoft/scx)
|
|
|
|
- macro: azure_scripts_writing_conf
|
|
condition: (proc.pname startswith "bash /var/lib/waagent/" and fd.name startswith /etc/azure)
|
|
|
|
- macro: azure_networkwatcher_writing_conf
|
|
condition: (proc.name in (NetworkWatcherA) and fd.name=/etc/init.d/AzureNetworkWatcherAgent)
|
|
|
|
- macro: couchdb_writing_conf
|
|
condition: (proc.name=beam.smp and proc.cmdline contains couchdb and fd.name startswith /etc/couchdb)
|
|
|
|
- macro: update_texmf_writing_conf
|
|
condition: (proc.name=update-texmf and fd.name startswith /etc/texmf)
|
|
|
|
- macro: slapadd_writing_conf
|
|
condition: (proc.name=slapadd and fd.name startswith /etc/ldap)
|
|
|
|
- macro: openldap_writing_conf
|
|
condition: (proc.pname=run-openldap.sh and fd.name startswith /etc/openldap)
|
|
|
|
- macro: ucpagent_writing_conf
|
|
condition: (proc.name=apiserver and container.image.repository=docker/ucp-agent and fd.name=/etc/authorization_config.cfg)
|
|
|
|
- macro: iscsi_writing_conf
|
|
condition: (proc.name=iscsiadm and fd.name startswith /etc/iscsi)
|
|
|
|
- macro: istio_writing_conf
|
|
condition: (proc.name=pilot-agent and fd.name startswith /etc/istio)
|
|
|
|
- macro: symantec_writing_conf
|
|
condition: >
|
|
((proc.name=symcfgd and fd.name startswith /etc/symantec) or
|
|
(proc.name=navdefutil and fd.name=/etc/symc-defutils.conf))
|
|
|
|
- macro: liveupdate_writing_conf
|
|
condition: (proc.cmdline startswith "java LiveUpdate" and fd.name in (/etc/liveupdate.conf, /etc/Product.Catalog.JavaLiveUpdate))
|
|
|
|
- macro: rancher_agent
|
|
condition: (proc.name=agent and container.image.repository contains "rancher/agent")
|
|
|
|
- macro: rancher_network_manager
|
|
condition: (proc.name=rancher-bridge and container.image.repository contains "rancher/network-manager")
|
|
|
|
- macro: sosreport_writing_files
|
|
condition: >
|
|
(proc.name=urlgrabber-ext- and proc.aname[3]=sosreport and
|
|
(fd.name startswith /etc/pkt/nssdb or fd.name startswith /etc/pki/nssdb))
|
|
|
|
- macro: pkgmgmt_progs_writing_pki
|
|
condition: >
|
|
(proc.name=urlgrabber-ext- and proc.pname in (yum, yum-cron, repoquery) and
|
|
(fd.name startswith /etc/pkt/nssdb or fd.name startswith /etc/pki/nssdb))
|
|
|
|
- macro: update_ca_trust_writing_pki
|
|
condition: (proc.pname=update-ca-trust and proc.name=trust and fd.name startswith /etc/pki)
|
|
|
|
- macro: brandbot_writing_os_release
|
|
condition: proc.name=brandbot and fd.name=/etc/os-release
|
|
|
|
- macro: selinux_writing_conf
|
|
condition: (proc.name in (semodule,genhomedircon,sefcontext_comp) and fd.name startswith /etc/selinux)
|
|
|
|
- list: veritas_binaries
|
|
items: [vxconfigd, sfcache, vxclustadm, vxdctl, vxprint, vxdmpadm, vxdisk, vxdg, vxassist, vxtune]
|
|
|
|
- macro: veritas_driver_script
|
|
condition: (proc.cmdline startswith "perl /opt/VRTSsfmh/bin/mh_driver.pl")
|
|
|
|
- macro: veritas_progs
|
|
condition: (proc.name in (veritas_binaries) or veritas_driver_script)
|
|
|
|
- macro: veritas_writing_config
|
|
condition: (veritas_progs and (fd.name startswith /etc/vx or fd.name startswith /etc/opt/VRTS or fd.name startswith /etc/vom))
|
|
|
|
- macro: nginx_writing_conf
|
|
condition: (proc.name in (nginx,nginx-ingress-c,nginx-ingress) and (fd.name startswith /etc/nginx or fd.name startswith /etc/ingress-controller))
|
|
|
|
- macro: nginx_writing_certs
|
|
condition: >
|
|
(((proc.name=openssl and proc.pname=nginx-launch.sh) or proc.name=nginx-launch.sh) and fd.name startswith /etc/nginx/certs)
|
|
|
|
- macro: chef_client_writing_conf
|
|
condition: (proc.pcmdline startswith "chef-client /opt/gitlab" and fd.name startswith /etc/gitlab)
|
|
|
|
- macro: centrify_writing_krb
|
|
condition: (proc.name in (adjoin,addns) and fd.name startswith /etc/krb5)
|
|
|
|
- macro: sssd_writing_krb
|
|
condition: (proc.name=adcli and proc.aname[2]=sssd and fd.name startswith /etc/krb5)
|
|
|
|
- macro: cockpit_writing_conf
|
|
condition: >
|
|
((proc.pname=cockpit-kube-la or proc.aname[2]=cockpit-kube-la)
|
|
and fd.name startswith /etc/cockpit)
|
|
|
|
- macro: ipsec_writing_conf
|
|
condition: (proc.name=start-ipsec.sh and fd.directory=/etc/ipsec)
|
|
|
|
- macro: exe_running_docker_save
|
|
condition: >
|
|
proc.name = "exe"
|
|
and (proc.cmdline contains "/var/lib/docker"
|
|
or proc.cmdline contains "/var/run/docker")
|
|
and proc.pname in (dockerd, docker, dockerd-current, docker-current)
|
|
|
|
# Ideally we'd have a length check here as well but
|
|
# filterchecks don't have operators like len()
|
|
- macro: sed_temporary_file
|
|
condition: (proc.name=sed and fd.name startswith "/etc/sed")
|
|
|
|
- macro: python_running_get_pip
|
|
condition: (proc.cmdline startswith "python get-pip.py")
|
|
|
|
- macro: python_running_ms_oms
|
|
condition: (proc.cmdline startswith "python /var/lib/waagent/")
|
|
|
|
- macro: gugent_writing_guestagent_log
|
|
condition: (proc.name=gugent and fd.name=GuestAgent.log)
|
|
|
|
- macro: dse_writing_tmp
|
|
condition: (proc.name=dse-entrypoint and fd.name=/root/tmp__)
|
|
|
|
- macro: zap_writing_state
|
|
condition: (proc.name=java and proc.cmdline contains "jar /zap" and fd.name startswith /root/.ZAP)
|
|
|
|
- macro: airflow_writing_state
|
|
condition: (proc.name=airflow and fd.name startswith /root/airflow)
|
|
|
|
- macro: rpm_writing_root_rpmdb
|
|
condition: (proc.name=rpm and fd.directory=/root/.rpmdb)
|
|
|
|
- macro: maven_writing_groovy
|
|
condition: (proc.name=java and proc.cmdline contains "classpath /usr/local/apache-maven" and fd.name startswith /root/.groovy)
|
|
|
|
- macro: chef_writing_conf
|
|
condition: (proc.name=chef-client and fd.name startswith /root/.chef)
|
|
|
|
- macro: kubectl_writing_state
|
|
condition: (proc.name in (kubectl,oc) and fd.name startswith /root/.kube)
|
|
|
|
- macro: java_running_cassandra
|
|
condition: (proc.name=java and proc.cmdline contains "cassandra.jar")
|
|
|
|
- macro: cassandra_writing_state
|
|
condition: (java_running_cassandra and fd.directory=/root/.cassandra)
|
|
|
|
# Istio
|
|
- macro: galley_writing_state
|
|
condition: (proc.name=galley and fd.name in (known_istio_files))
|
|
|
|
- list: known_istio_files
|
|
items: [/healthready, /healthliveness]
|
|
|
|
- macro: calico_writing_state
|
|
condition: (proc.name=kube-controller and fd.name startswith /status.json and k8s.pod.name startswith calico)
|
|
|
|
- macro: calico_writing_envvars
|
|
condition: (proc.name=start_runit and fd.name startswith "/etc/envvars" and container.image.repository endswith "calico/node")
|
|
|
|
- list: repository_files
|
|
items: [sources.list]
|
|
|
|
- list: repository_directories
|
|
items: [/etc/apt/sources.list.d, /etc/yum.repos.d, /etc/apt]
|
|
|
|
- macro: access_repositories
|
|
condition: (fd.directory in (repository_directories) or
|
|
(fd.name pmatch (repository_directories) and
|
|
fd.filename in (repository_files)))
|
|
|
|
- macro: modify_repositories
|
|
condition: (evt.arg.newpath pmatch (repository_directories))
|
|
|
|
- macro: user_known_update_package_registry
|
|
condition: (never_true)
|
|
|
|
- rule: Update Package Repository
|
|
desc: Detect package repositories get updated
|
|
condition: >
|
|
((open_write and access_repositories) or (modify and modify_repositories))
|
|
and not package_mgmt_procs
|
|
and not package_mgmt_ancestor_procs
|
|
and not exe_running_docker_save
|
|
and not user_known_update_package_registry
|
|
output: >
|
|
Repository files get updated (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline pcmdline=%proc.pcmdline file=%fd.name newpath=%evt.arg.newpath container_id=%container.id image=%container.image.repository)
|
|
priority:
|
|
NOTICE
|
|
tags: [filesystem, mitre_persistence]
|
|
|
|
# Users should overwrite this macro to specify conditions under which a
|
|
# write under the binary dir is ignored. For example, it may be okay to
|
|
# install a binary in the context of a ci/cd build.
|
|
- macro: user_known_write_below_binary_dir_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Write below binary dir
|
|
desc: an attempt to write to any file below a set of binary directories
|
|
condition: >
|
|
bin_dir and evt.dir = < and open_write
|
|
and not package_mgmt_procs
|
|
and not exe_running_docker_save
|
|
and not python_running_get_pip
|
|
and not python_running_ms_oms
|
|
and not user_known_write_below_binary_dir_activities
|
|
output: >
|
|
File below a known binary directory opened for writing (user=%user.name user_loginuid=%user.loginuid
|
|
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
|
|
priority: ERROR
|
|
tags: [filesystem, mitre_persistence]
|
|
|
|
# If you'd like to generally monitor a wider set of directories on top
|
|
# of the ones covered by the rule Write below binary dir, you can use
|
|
# the following rule and lists.
|
|
|
|
- list: monitored_directories
|
|
items: [/boot, /lib, /lib64, /usr/lib, /usr/local/lib, /usr/local/sbin, /usr/local/bin, /root/.ssh]
|
|
|
|
- macro: user_ssh_directory
|
|
condition: (fd.name glob '/home/*/.ssh/*')
|
|
|
|
# google_accounts_(daemon)
|
|
- macro: google_accounts_daemon_writing_ssh
|
|
condition: (proc.name=google_accounts and user_ssh_directory)
|
|
|
|
- macro: cloud_init_writing_ssh
|
|
condition: (proc.name=cloud-init and user_ssh_directory)
|
|
|
|
- macro: mkinitramfs_writing_boot
|
|
condition: (proc.pname in (mkinitramfs, update-initramf) and fd.directory=/boot)
|
|
|
|
- macro: monitored_dir
|
|
condition: >
|
|
(fd.directory in (monitored_directories)
|
|
or user_ssh_directory)
|
|
and not mkinitramfs_writing_boot
|
|
|
|
# Add conditions to this macro (probably in a separate file,
|
|
# overwriting this macro) to allow for specific combinations of
|
|
# programs writing below monitored directories.
|
|
#
|
|
# Its default value is an expression that always is false, which
|
|
# becomes true when the "not ..." in the rule is applied.
|
|
- macro: user_known_write_monitored_dir_conditions
|
|
condition: (never_true)
|
|
|
|
- rule: Write below monitored dir
|
|
desc: an attempt to write to any file below a set of monitored directories
|
|
condition: >
|
|
evt.dir = < and open_write and monitored_dir
|
|
and not package_mgmt_procs
|
|
and not coreos_write_ssh_dir
|
|
and not exe_running_docker_save
|
|
and not python_running_get_pip
|
|
and not python_running_ms_oms
|
|
and not google_accounts_daemon_writing_ssh
|
|
and not cloud_init_writing_ssh
|
|
and not user_known_write_monitored_dir_conditions
|
|
output: >
|
|
File below a monitored directory opened for writing (user=%user.name user_loginuid=%user.loginuid
|
|
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
|
|
priority: ERROR
|
|
tags: [filesystem, mitre_persistence]
|
|
|
|
# This rule is disabled by default as many system management tools
|
|
# like ansible, etc can read these files/paths. Enable it using this macro.
|
|
|
|
- macro: consider_ssh_reads
|
|
condition: (never_true)
|
|
|
|
- macro: user_known_read_ssh_information_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Read ssh information
|
|
desc: Any attempt to read files below ssh directories by non-ssh programs
|
|
condition: >
|
|
((open_read or open_directory) and
|
|
consider_ssh_reads and
|
|
(user_ssh_directory or fd.name startswith /root/.ssh) and
|
|
not user_known_read_ssh_information_activities and
|
|
not proc.name in (ssh_binaries))
|
|
output: >
|
|
ssh-related file/directory read by non-ssh program (user=%user.name user_loginuid=%user.loginuid
|
|
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline container_id=%container.id image=%container.image.repository)
|
|
priority: ERROR
|
|
tags: [filesystem, mitre_discovery]
|
|
|
|
- list: safe_etc_dirs
|
|
items: [/etc/cassandra, /etc/ssl/certs/java, /etc/logstash, /etc/nginx/conf.d, /etc/container_environment, /etc/hrmconfig, /etc/fluent/configs.d]
|
|
|
|
- macro: fluentd_writing_conf_files
|
|
condition: (proc.name=start-fluentd and fd.name in (/etc/fluent/fluent.conf, /etc/td-agent/td-agent.conf))
|
|
|
|
- macro: qualys_writing_conf_files
|
|
condition: (proc.name=qualys-cloud-ag and fd.name=/etc/qualys/cloud-agent/qagent-log.conf)
|
|
|
|
- macro: git_writing_nssdb
|
|
condition: (proc.name=git-remote-http and fd.directory=/etc/pki/nssdb)
|
|
|
|
- macro: plesk_writing_keys
|
|
condition: (proc.name in (plesk_binaries) and fd.name startswith /etc/sw/keys)
|
|
|
|
- macro: plesk_install_writing_apache_conf
|
|
condition: (proc.cmdline startswith "bash -hB /usr/lib/plesk-9.0/services/webserver.apache configure"
|
|
and fd.name="/etc/apache2/apache2.conf.tmp")
|
|
|
|
- macro: plesk_running_mktemp
|
|
condition: (proc.name=mktemp and proc.aname[3] in (plesk_binaries))
|
|
|
|
- macro: networkmanager_writing_resolv_conf
|
|
condition: proc.aname[2]=nm-dispatcher and fd.name=/etc/resolv.conf
|
|
|
|
- macro: add_shell_writing_shells_tmp
|
|
condition: (proc.name=add-shell and fd.name=/etc/shells.tmp)
|
|
|
|
- macro: duply_writing_exclude_files
|
|
condition: (proc.name=touch and proc.pcmdline startswith "bash /usr/bin/duply" and fd.name startswith "/etc/duply")
|
|
|
|
- macro: xmlcatalog_writing_files
|
|
condition: (proc.name=update-xmlcatal and fd.directory=/etc/xml)
|
|
|
|
- macro: datadog_writing_conf
|
|
condition: ((proc.cmdline startswith "python /opt/datadog-agent" or
|
|
proc.cmdline startswith "entrypoint.sh /entrypoint.sh datadog start" or
|
|
proc.cmdline startswith "agent.py /opt/datadog-agent")
|
|
and fd.name startswith "/etc/dd-agent")
|
|
|
|
- macro: rancher_writing_conf
|
|
condition: ((proc.name in (healthcheck, lb-controller, rancher-dns)) and
|
|
(container.image.repository contains "rancher/healthcheck" or
|
|
container.image.repository contains "rancher/lb-service-haproxy" or
|
|
container.image.repository contains "rancher/dns") and
|
|
(fd.name startswith "/etc/haproxy" or fd.name startswith "/etc/rancher-dns"))
|
|
|
|
- macro: rancher_writing_root
|
|
condition: (proc.name=rancher-metadat and
|
|
(container.image.repository contains "rancher/metadata" or container.image.repository contains "rancher/lb-service-haproxy") and
|
|
fd.name startswith "/answers.json")
|
|
|
|
- macro: checkpoint_writing_state
|
|
condition: (proc.name=checkpoint and
|
|
container.image.repository contains "coreos/pod-checkpointer" and
|
|
fd.name startswith "/etc/kubernetes")
|
|
|
|
- macro: jboss_in_container_writing_passwd
|
|
condition: >
|
|
((proc.cmdline="run-java.sh /opt/jboss/container/java/run/run-java.sh"
|
|
or proc.cmdline="run-java.sh /opt/run-java/run-java.sh")
|
|
and container
|
|
and fd.name=/etc/passwd)
|
|
|
|
- macro: curl_writing_pki_db
|
|
condition: (proc.name=curl and fd.directory=/etc/pki/nssdb)
|
|
|
|
- macro: haproxy_writing_conf
|
|
condition: ((proc.name in (update-haproxy-,haproxy_reload.) or proc.pname in (update-haproxy-,haproxy_reload,haproxy_reload.))
|
|
and (fd.name=/etc/openvpn/client.map or fd.name startswith /etc/haproxy))
|
|
|
|
- macro: java_writing_conf
|
|
condition: (proc.name=java and fd.name=/etc/.java/.systemPrefs/.system.lock)
|
|
|
|
- macro: rabbitmq_writing_conf
|
|
condition: (proc.name=rabbitmq-server and fd.directory=/etc/rabbitmq)
|
|
|
|
- macro: rook_writing_conf
|
|
condition: (proc.name=toolbox.sh and container.image.repository=rook/toolbox
|
|
and fd.directory=/etc/ceph)
|
|
|
|
- macro: httpd_writing_conf_logs
|
|
condition: (proc.name=httpd and fd.name startswith /etc/httpd/)
|
|
|
|
- macro: mysql_writing_conf
|
|
condition: >
|
|
((proc.name in (start-mysql.sh, run-mysqld) or proc.pname=start-mysql.sh) and
|
|
(fd.name startswith /etc/mysql or fd.directory=/etc/my.cnf.d))
|
|
|
|
- macro: redis_writing_conf
|
|
condition: >
|
|
(proc.name in (run-redis, redis-launcher.) and (fd.name=/etc/redis.conf or fd.name startswith /etc/redis))
|
|
|
|
- macro: openvpn_writing_conf
|
|
condition: (proc.name in (openvpn,openvpn-entrypo) and fd.name startswith /etc/openvpn)
|
|
|
|
- macro: php_handlers_writing_conf
|
|
condition: (proc.name=php_handlers_co and fd.name=/etc/psa/php_versions.json)
|
|
|
|
- macro: sed_writing_temp_file
|
|
condition: >
|
|
((proc.aname[3]=cron_start.sh and fd.name startswith /etc/security/sed) or
|
|
(proc.name=sed and (fd.name startswith /etc/apt/sources.list.d/sed or
|
|
fd.name startswith /etc/apt/sed or
|
|
fd.name startswith /etc/apt/apt.conf.d/sed)))
|
|
|
|
- macro: cron_start_writing_pam_env
|
|
condition: (proc.cmdline="bash /usr/sbin/start-cron" and fd.name=/etc/security/pam_env.conf)
|
|
|
|
# In some cases dpkg-reconfigur runs commands that modify /etc. Not
|
|
# putting the full set of package management programs yet.
|
|
- macro: dpkg_scripting
|
|
condition: (proc.aname[2] in (dpkg-reconfigur, dpkg-preconfigu))
|
|
|
|
- macro: ufw_writing_conf
|
|
condition: (proc.name=ufw and fd.directory=/etc/ufw)
|
|
|
|
- macro: calico_writing_conf
|
|
condition: >
|
|
(((proc.name = calico-node) or
|
|
(container.image.repository=gcr.io/projectcalico-org/node and proc.name in (start_runit, cp)) or
|
|
(container.image.repository=gcr.io/projectcalico-org/cni and proc.name=sed))
|
|
and fd.name startswith /etc/calico)
|
|
|
|
- macro: prometheus_conf_writing_conf
|
|
condition: (proc.name=prometheus-conf and fd.name startswith /etc/prometheus/config_out)
|
|
|
|
- macro: openshift_writing_conf
|
|
condition: (proc.name=oc and fd.name startswith /etc/origin/node)
|
|
|
|
- macro: keepalived_writing_conf
|
|
condition: (proc.name in (keepalived, kube-keepalived) and fd.name=/etc/keepalived/keepalived.conf)
|
|
|
|
- macro: etcd_manager_updating_dns
|
|
condition: (container and proc.name=etcd-manager and fd.name=/etc/hosts)
|
|
|
|
- macro: automount_using_mtab
|
|
condition: (proc.pname = automount and fd.name startswith /etc/mtab)
|
|
|
|
- macro: mcafee_writing_cma_d
|
|
condition: (proc.name=macompatsvc and fd.directory=/etc/cma.d)
|
|
|
|
- macro: avinetworks_supervisor_writing_ssh
|
|
condition: >
|
|
(proc.cmdline="se_supervisor.p /opt/avi/scripts/se_supervisor.py -d" and
|
|
(fd.name startswith /etc/ssh/known_host_ or
|
|
fd.name startswith /etc/ssh/ssh_monitor_config_ or
|
|
fd.name startswith /etc/ssh/ssh_config_))
|
|
|
|
- macro: multipath_writing_conf
|
|
condition: (proc.name = multipath and fd.name startswith /etc/multipath/)
|
|
|
|
# Add conditions to this macro (probably in a separate file,
|
|
# overwriting this macro) to allow for specific combinations of
|
|
# programs writing below specific directories below
|
|
# /etc. fluentd_writing_conf_files is a good example to follow, as it
|
|
# specifies both the program doing the writing as well as the specific
|
|
# files it is allowed to modify.
|
|
#
|
|
# In this file, it just takes one of the programs in the base macro
|
|
# and repeats it.
|
|
|
|
- macro: user_known_write_etc_conditions
|
|
condition: proc.name=confd
|
|
|
|
# This is a placeholder for user to extend the whitelist for write below etc rule
|
|
- macro: user_known_write_below_etc_activities
|
|
condition: (never_true)
|
|
|
|
- macro: write_etc_common
|
|
condition: >
|
|
etc_dir and evt.dir = < and open_write
|
|
and proc_name_exists
|
|
and not proc.name in (passwd_binaries, shadowutils_binaries, sysdigcloud_binaries,
|
|
package_mgmt_binaries, ssl_mgmt_binaries, dhcp_binaries,
|
|
dev_creation_binaries, shell_mgmt_binaries,
|
|
mail_config_binaries,
|
|
sshkit_script_binaries,
|
|
ldconfig.real, ldconfig, confd, gpg, insserv,
|
|
apparmor_parser, update-mime, tzdata.config, tzdata.postinst,
|
|
systemd, systemd-machine, systemd-sysuser,
|
|
debconf-show, rollerd, bind9.postinst, sv,
|
|
gen_resolvconf., update-ca-certi, certbot, runsv,
|
|
qualys-cloud-ag, locales.postins, nomachine_binaries,
|
|
adclient, certutil, crlutil, pam-auth-update, parallels_insta,
|
|
openshift-launc, update-rc.d, puppet)
|
|
and not (container and proc.cmdline in ("cp /run/secrets/kubernetes.io/serviceaccount/ca.crt /etc/pki/ca-trust/source/anchors/openshift-ca.crt"))
|
|
and not proc.pname in (sysdigcloud_binaries, mail_config_binaries, hddtemp.postins, sshkit_script_binaries, locales.postins, deb_binaries, dhcp_binaries)
|
|
and not fd.name pmatch (safe_etc_dirs)
|
|
and not fd.name in (/etc/container_environment.sh, /etc/container_environment.json, /etc/motd, /etc/motd.svc)
|
|
and not sed_temporary_file
|
|
and not exe_running_docker_save
|
|
and not ansible_running_python
|
|
and not python_running_denyhosts
|
|
and not fluentd_writing_conf_files
|
|
and not user_known_write_etc_conditions
|
|
and not run_by_centrify
|
|
and not run_by_adclient
|
|
and not qualys_writing_conf_files
|
|
and not git_writing_nssdb
|
|
and not plesk_writing_keys
|
|
and not plesk_install_writing_apache_conf
|
|
and not plesk_running_mktemp
|
|
and not networkmanager_writing_resolv_conf
|
|
and not run_by_chef
|
|
and not add_shell_writing_shells_tmp
|
|
and not duply_writing_exclude_files
|
|
and not xmlcatalog_writing_files
|
|
and not parent_supervise_running_multilog
|
|
and not supervise_writing_status
|
|
and not pki_realm_writing_realms
|
|
and not htpasswd_writing_passwd
|
|
and not lvprogs_writing_conf
|
|
and not ovsdb_writing_openvswitch
|
|
and not datadog_writing_conf
|
|
and not curl_writing_pki_db
|
|
and not haproxy_writing_conf
|
|
and not java_writing_conf
|
|
and not dpkg_scripting
|
|
and not parent_ucf_writing_conf
|
|
and not rabbitmq_writing_conf
|
|
and not rook_writing_conf
|
|
and not php_handlers_writing_conf
|
|
and not sed_writing_temp_file
|
|
and not cron_start_writing_pam_env
|
|
and not httpd_writing_conf_logs
|
|
and not mysql_writing_conf
|
|
and not openvpn_writing_conf
|
|
and not consul_template_writing_conf
|
|
and not countly_writing_nginx_conf
|
|
and not ms_oms_writing_conf
|
|
and not ms_scx_writing_conf
|
|
and not azure_scripts_writing_conf
|
|
and not azure_networkwatcher_writing_conf
|
|
and not couchdb_writing_conf
|
|
and not update_texmf_writing_conf
|
|
and not slapadd_writing_conf
|
|
and not symantec_writing_conf
|
|
and not liveupdate_writing_conf
|
|
and not sosreport_writing_files
|
|
and not selinux_writing_conf
|
|
and not veritas_writing_config
|
|
and not nginx_writing_conf
|
|
and not nginx_writing_certs
|
|
and not chef_client_writing_conf
|
|
and not centrify_writing_krb
|
|
and not sssd_writing_krb
|
|
and not cockpit_writing_conf
|
|
and not ipsec_writing_conf
|
|
and not httpd_writing_ssl_conf
|
|
and not userhelper_writing_etc_security
|
|
and not pkgmgmt_progs_writing_pki
|
|
and not update_ca_trust_writing_pki
|
|
and not brandbot_writing_os_release
|
|
and not redis_writing_conf
|
|
and not openldap_writing_conf
|
|
and not ucpagent_writing_conf
|
|
and not iscsi_writing_conf
|
|
and not istio_writing_conf
|
|
and not ufw_writing_conf
|
|
and not calico_writing_conf
|
|
and not calico_writing_envvars
|
|
and not prometheus_conf_writing_conf
|
|
and not openshift_writing_conf
|
|
and not keepalived_writing_conf
|
|
and not rancher_writing_conf
|
|
and not checkpoint_writing_state
|
|
and not jboss_in_container_writing_passwd
|
|
and not etcd_manager_updating_dns
|
|
and not user_known_write_below_etc_activities
|
|
and not automount_using_mtab
|
|
and not mcafee_writing_cma_d
|
|
and not avinetworks_supervisor_writing_ssh
|
|
and not multipath_writing_conf
|
|
|
|
- rule: Write below etc
|
|
desc: an attempt to write to any file below /etc
|
|
condition: write_etc_common
|
|
output: "File below /etc opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent=%proc.pname pcmdline=%proc.pcmdline file=%fd.name program=%proc.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)"
|
|
priority: ERROR
|
|
tags: [filesystem, mitre_persistence]
|
|
|
|
- list: known_root_files
|
|
items: [/root/.monit.state, /root/.auth_tokens, /root/.bash_history, /root/.ash_history, /root/.aws/credentials,
|
|
/root/.viminfo.tmp, /root/.lesshst, /root/.bzr.log, /root/.gitconfig.lock, /root/.babel.json, /root/.localstack,
|
|
/root/.node_repl_history, /root/.mongorc.js, /root/.dbshell, /root/.augeas/history, /root/.rnd, /root/.wget-hsts, /health, /exec.fifo]
|
|
|
|
- list: known_root_directories
|
|
items: [/root/.oracle_jre_usage, /root/.ssh, /root/.subversion, /root/.nami]
|
|
|
|
- macro: known_root_conditions
|
|
condition: (fd.name startswith /root/orcexec.
|
|
or fd.name startswith /root/.m2
|
|
or fd.name startswith /root/.npm
|
|
or fd.name startswith /root/.pki
|
|
or fd.name startswith /root/.ivy2
|
|
or fd.name startswith /root/.config/Cypress
|
|
or fd.name startswith /root/.config/pulse
|
|
or fd.name startswith /root/.config/configstore
|
|
or fd.name startswith /root/jenkins/workspace
|
|
or fd.name startswith /root/.jenkins
|
|
or fd.name startswith /root/.cache
|
|
or fd.name startswith /root/.sbt
|
|
or fd.name startswith /root/.java
|
|
or fd.name startswith /root/.glide
|
|
or fd.name startswith /root/.sonar
|
|
or fd.name startswith /root/.v8flag
|
|
or fd.name startswith /root/infaagent
|
|
or fd.name startswith /root/.local/lib/python
|
|
or fd.name startswith /root/.pm2
|
|
or fd.name startswith /root/.gnupg
|
|
or fd.name startswith /root/.pgpass
|
|
or fd.name startswith /root/.theano
|
|
or fd.name startswith /root/.gradle
|
|
or fd.name startswith /root/.android
|
|
or fd.name startswith /root/.ansible
|
|
or fd.name startswith /root/.crashlytics
|
|
or fd.name startswith /root/.dbus
|
|
or fd.name startswith /root/.composer
|
|
or fd.name startswith /root/.gconf
|
|
or fd.name startswith /root/.nv
|
|
or fd.name startswith /root/.local/share/jupyter
|
|
or fd.name startswith /root/oradiag_root
|
|
or fd.name startswith /root/workspace
|
|
or fd.name startswith /root/jvm
|
|
or fd.name startswith /root/.node-gyp)
|
|
|
|
# Add conditions to this macro (probably in a separate file,
|
|
# overwriting this macro) to allow for specific combinations of
|
|
# programs writing below specific directories below
|
|
# / or /root.
|
|
#
|
|
# In this file, it just takes one of the condition in the base macro
|
|
# and repeats it.
|
|
- macro: user_known_write_root_conditions
|
|
condition: fd.name=/root/.bash_history
|
|
|
|
# This is a placeholder for user to extend the whitelist for write below root rule
|
|
- macro: user_known_write_below_root_activities
|
|
condition: (never_true)
|
|
|
|
- macro: runc_writing_exec_fifo
|
|
condition: (proc.cmdline="runc:[1:CHILD] init" and fd.name=/exec.fifo)
|
|
|
|
- macro: runc_writing_var_lib_docker
|
|
condition: (proc.cmdline="runc:[1:CHILD] init" and evt.arg.filename startswith /var/lib/docker)
|
|
|
|
- macro: mysqlsh_writing_state
|
|
condition: (proc.name=mysqlsh and fd.directory=/root/.mysqlsh)
|
|
|
|
- rule: Write below root
|
|
desc: an attempt to write to any file directly below / or /root
|
|
condition: >
|
|
root_dir and evt.dir = < and open_write
|
|
and proc_name_exists
|
|
and not fd.name in (known_root_files)
|
|
and not fd.directory pmatch (known_root_directories)
|
|
and not exe_running_docker_save
|
|
and not gugent_writing_guestagent_log
|
|
and not dse_writing_tmp
|
|
and not zap_writing_state
|
|
and not airflow_writing_state
|
|
and not rpm_writing_root_rpmdb
|
|
and not maven_writing_groovy
|
|
and not chef_writing_conf
|
|
and not kubectl_writing_state
|
|
and not cassandra_writing_state
|
|
and not galley_writing_state
|
|
and not calico_writing_state
|
|
and not rancher_writing_root
|
|
and not runc_writing_exec_fifo
|
|
and not mysqlsh_writing_state
|
|
and not known_root_conditions
|
|
and not user_known_write_root_conditions
|
|
and not user_known_write_below_root_activities
|
|
output: "File below / or /root opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent=%proc.pname file=%fd.name program=%proc.name container_id=%container.id image=%container.image.repository)"
|
|
priority: ERROR
|
|
tags: [filesystem, mitre_persistence]
|
|
|
|
- macro: cmp_cp_by_passwd
|
|
condition: proc.name in (cmp, cp) and proc.pname in (passwd, run-parts)
|
|
|
|
- macro: user_known_read_sensitive_files_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Read sensitive file trusted after startup
|
|
desc: >
|
|
an attempt to read any sensitive file (e.g. files containing user/password/authentication
|
|
information) by a trusted program after startup. Trusted programs might read these files
|
|
at startup to load initial state, but not afterwards.
|
|
condition: sensitive_files and open_read and server_procs and not proc_is_new and proc.name!="sshd" and not user_known_read_sensitive_files_activities
|
|
output: >
|
|
Sensitive file opened for reading by trusted program after startup (user=%user.name user_loginuid=%user.loginuid
|
|
command=%proc.cmdline parent=%proc.pname file=%fd.name parent=%proc.pname gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
|
|
priority: WARNING
|
|
tags: [filesystem, mitre_credential_access]
|
|
|
|
- list: read_sensitive_file_binaries
|
|
items: [
|
|
iptables, ps, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, sshd,
|
|
vsftpd, systemd, mysql_install_d, psql, screen, debconf-show, sa-update,
|
|
pam-auth-update, pam-config, /usr/sbin/spamd, polkit-agent-he, lsattr, file, sosreport,
|
|
scxcimservera, adclient, rtvscand, cockpit-session, userhelper, ossec-syscheckd
|
|
]
|
|
|
|
# Add conditions to this macro (probably in a separate file,
|
|
# overwriting this macro) to allow for specific combinations of
|
|
# programs accessing sensitive files.
|
|
# fluentd_writing_conf_files is a good example to follow, as it
|
|
# specifies both the program doing the writing as well as the specific
|
|
# files it is allowed to modify.
|
|
#
|
|
# In this file, it just takes one of the macros in the base rule
|
|
# and repeats it.
|
|
|
|
- macro: user_read_sensitive_file_conditions
|
|
condition: cmp_cp_by_passwd
|
|
|
|
- list: read_sensitive_file_images
|
|
items: []
|
|
|
|
- macro: user_read_sensitive_file_containers
|
|
condition: (container and container.image.repository in (read_sensitive_file_images))
|
|
|
|
# This macro detects man-db postinst, see https://salsa.debian.org/debian/man-db/-/blob/master/debian/postinst
|
|
# The rule "Read sensitive file untrusted" use this macro to avoid FPs.
|
|
- macro: mandb_postinst
|
|
condition: >
|
|
(proc.name=perl and proc.args startswith "-e" and
|
|
proc.args contains "@pwd = getpwnam(" and
|
|
proc.args contains "exec " and
|
|
proc.args contains "/usr/bin/mandb")
|
|
|
|
- rule: Read sensitive file untrusted
|
|
desc: >
|
|
an attempt to read any sensitive file (e.g. files containing user/password/authentication
|
|
information). Exceptions are made for known trusted programs.
|
|
condition: >
|
|
sensitive_files and open_read
|
|
and proc_name_exists
|
|
and not proc.name in (user_mgmt_binaries, userexec_binaries, package_mgmt_binaries,
|
|
cron_binaries, read_sensitive_file_binaries, shell_binaries, hids_binaries,
|
|
vpn_binaries, mail_config_binaries, nomachine_binaries, sshkit_script_binaries,
|
|
in.proftpd, mandb, salt-minion, postgres_mgmt_binaries,
|
|
google_oslogin_
|
|
)
|
|
and not cmp_cp_by_passwd
|
|
and not ansible_running_python
|
|
and not run_by_qualys
|
|
and not run_by_chef
|
|
and not run_by_google_accounts_daemon
|
|
and not user_read_sensitive_file_conditions
|
|
and not mandb_postinst
|
|
and not perl_running_plesk
|
|
and not perl_running_updmap
|
|
and not veritas_driver_script
|
|
and not perl_running_centrifydc
|
|
and not runuser_reading_pam
|
|
and not linux_bench_reading_etc_shadow
|
|
and not user_known_read_sensitive_files_activities
|
|
and not user_read_sensitive_file_containers
|
|
output: >
|
|
Sensitive file opened for reading by non-trusted program (user=%user.name user_loginuid=%user.loginuid program=%proc.name
|
|
command=%proc.cmdline file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)
|
|
priority: WARNING
|
|
tags: [filesystem, mitre_credential_access, mitre_discovery]
|
|
|
|
- macro: amazon_linux_running_python_yum
|
|
condition: >
|
|
(proc.name = python and
|
|
proc.pcmdline = "python -m amazon_linux_extras system_motd" and
|
|
proc.cmdline startswith "python -c import yum;")
|
|
|
|
- macro: user_known_write_rpm_database_activities
|
|
condition: (never_true)
|
|
|
|
# Only let rpm-related programs write to the rpm database
|
|
- rule: Write below rpm database
|
|
desc: an attempt to write to the rpm database by any non-rpm related program
|
|
condition: >
|
|
fd.name startswith /var/lib/rpm and open_write
|
|
and not rpm_procs
|
|
and not ansible_running_python
|
|
and not python_running_chef
|
|
and not exe_running_docker_save
|
|
and not amazon_linux_running_python_yum
|
|
and not user_known_write_rpm_database_activities
|
|
output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline container_id=%container.id image=%container.image.repository)"
|
|
priority: ERROR
|
|
tags: [filesystem, software_mgmt, mitre_persistence]
|
|
|
|
- macro: postgres_running_wal_e
|
|
condition: (proc.pname=postgres and proc.cmdline startswith "sh -c envdir /etc/wal-e.d/env /usr/local/bin/wal-e")
|
|
|
|
- macro: redis_running_prepost_scripts
|
|
condition: (proc.aname[2]=redis-server and (proc.cmdline contains "redis-server.post-up.d" or proc.cmdline contains "redis-server.pre-up.d"))
|
|
|
|
- macro: rabbitmq_running_scripts
|
|
condition: >
|
|
(proc.pname=beam.smp and
|
|
(proc.cmdline startswith "sh -c exec ps" or
|
|
proc.cmdline startswith "sh -c exec inet_gethost" or
|
|
proc.cmdline= "sh -s unix:cmd" or
|
|
proc.cmdline= "sh -c exec /bin/sh -s unix:cmd 2>&1"))
|
|
|
|
- macro: rabbitmqctl_running_scripts
|
|
condition: (proc.aname[2]=rabbitmqctl and proc.cmdline startswith "sh -c ")
|
|
|
|
- macro: run_by_appdynamics
|
|
condition: (proc.pname=java and proc.pcmdline startswith "java -jar -Dappdynamics")
|
|
|
|
- macro: user_known_db_spawned_processes
|
|
condition: (never_true)
|
|
|
|
- rule: DB program spawned process
|
|
desc: >
|
|
a database-server related program spawned a new process other than itself.
|
|
This shouldn\'t occur and is a follow on from some SQL injection attacks.
|
|
condition: >
|
|
proc.pname in (db_server_binaries)
|
|
and spawned_process
|
|
and not proc.name in (db_server_binaries)
|
|
and not postgres_running_wal_e
|
|
and not user_known_db_spawned_processes
|
|
output: >
|
|
Database-related program spawned process other than itself (user=%user.name user_loginuid=%user.loginuid
|
|
program=%proc.cmdline parent=%proc.pname container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [process, database, mitre_execution]
|
|
|
|
- macro: user_known_modify_bin_dir_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Modify binary dirs
|
|
desc: an attempt to modify any file below a set of binary directories.
|
|
condition: bin_dir_rename and modify and not package_mgmt_procs and not exe_running_docker_save and not user_known_modify_bin_dir_activities
|
|
output: >
|
|
File below known binary directory renamed/removed (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline
|
|
pcmdline=%proc.pcmdline operation=%evt.type file=%fd.name %evt.args container_id=%container.id image=%container.image.repository)
|
|
priority: ERROR
|
|
tags: [filesystem, mitre_persistence]
|
|
|
|
- macro: user_known_mkdir_bin_dir_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Mkdir binary dirs
|
|
desc: an attempt to create a directory below a set of binary directories.
|
|
condition: >
|
|
mkdir
|
|
and bin_dir_mkdir
|
|
and not package_mgmt_procs
|
|
and not user_known_mkdir_bin_dir_activities
|
|
and not exe_running_docker_save
|
|
output: >
|
|
Directory below known binary directory created (user=%user.name user_loginuid=%user.loginuid
|
|
command=%proc.cmdline directory=%evt.arg.path container_id=%container.id image=%container.image.repository)
|
|
priority: ERROR
|
|
tags: [filesystem, mitre_persistence]
|
|
|
|
# This list allows for easy additions to the set of commands allowed
|
|
# to change thread namespace without having to copy and override the
|
|
# entire change thread namespace rule.
|
|
- list: user_known_change_thread_namespace_binaries
|
|
items: [crio, multus]
|
|
|
|
- macro: user_known_change_thread_namespace_activities
|
|
condition: (never_true)
|
|
|
|
- list: network_plugin_binaries
|
|
items: [aws-cni, azure-vnet]
|
|
|
|
- macro: calico_node
|
|
condition: (container.image.repository endswith calico/node and proc.name=calico-node)
|
|
|
|
- macro: weaveworks_scope
|
|
condition: (container.image.repository endswith weaveworks/scope and proc.name=scope)
|
|
|
|
- rule: Change thread namespace
|
|
desc: >
|
|
an attempt to change a program/thread\'s namespace (commonly done
|
|
as a part of creating a container) by calling setns.
|
|
condition: >
|
|
evt.type=setns and evt.dir=<
|
|
and proc_name_exists
|
|
and not (container.id=host and proc.name in (docker_binaries, k8s_binaries, lxd_binaries, nsenter))
|
|
and not proc.name in (sysdigcloud_binaries, sysdig, calico, oci-umount, cilium-cni, network_plugin_binaries)
|
|
and not proc.name in (user_known_change_thread_namespace_binaries)
|
|
and not proc.name startswith "runc"
|
|
and not proc.cmdline startswith "containerd"
|
|
and not proc.pname in (sysdigcloud_binaries, hyperkube, kubelet, protokube, dockerd, tini, aws)
|
|
and not java_running_sdjagent
|
|
and not kubelet_running_loopback
|
|
and not rancher_agent
|
|
and not rancher_network_manager
|
|
and not calico_node
|
|
and not weaveworks_scope
|
|
and not user_known_change_thread_namespace_activities
|
|
enabled: false
|
|
output: >
|
|
Namespace change (setns) by unexpected program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline
|
|
parent=%proc.pname %container.info container_id=%container.id image=%container.image.repository:%container.image.tag)
|
|
priority: NOTICE
|
|
tags: [process, mitre_privilege_escalation, mitre_lateral_movement]
|
|
|
|
# The binaries in this list and their descendents are *not* allowed
|
|
# spawn shells. This includes the binaries spawning shells directly as
|
|
# well as indirectly. For example, apache -> php/perl for
|
|
# mod_{php,perl} -> some shell is also not allowed, because the shell
|
|
# has apache as an ancestor.
|
|
|
|
- list: protected_shell_spawning_binaries
|
|
items: [
|
|
http_server_binaries, db_server_binaries, nosql_server_binaries, mail_binaries,
|
|
fluentd, flanneld, splunkd, consul, smbd, runsv, PM2
|
|
]
|
|
|
|
- macro: parent_java_running_zookeeper
|
|
condition: (proc.pname=java and proc.pcmdline contains org.apache.zookeeper.server)
|
|
|
|
- macro: parent_java_running_kafka
|
|
condition: (proc.pname=java and proc.pcmdline contains kafka.Kafka)
|
|
|
|
- macro: parent_java_running_elasticsearch
|
|
condition: (proc.pname=java and proc.pcmdline contains org.elasticsearch.bootstrap.Elasticsearch)
|
|
|
|
- macro: parent_java_running_activemq
|
|
condition: (proc.pname=java and proc.pcmdline contains activemq.jar)
|
|
|
|
- macro: parent_java_running_cassandra
|
|
condition: (proc.pname=java and (proc.pcmdline contains "-Dcassandra.config.loader" or proc.pcmdline contains org.apache.cassandra.service.CassandraDaemon))
|
|
|
|
- macro: parent_java_running_jboss_wildfly
|
|
condition: (proc.pname=java and proc.pcmdline contains org.jboss)
|
|
|
|
- macro: parent_java_running_glassfish
|
|
condition: (proc.pname=java and proc.pcmdline contains com.sun.enterprise.glassfish)
|
|
|
|
- macro: parent_java_running_hadoop
|
|
condition: (proc.pname=java and proc.pcmdline contains org.apache.hadoop)
|
|
|
|
- macro: parent_java_running_datastax
|
|
condition: (proc.pname=java and proc.pcmdline contains com.datastax)
|
|
|
|
- macro: nginx_starting_nginx
|
|
condition: (proc.pname=nginx and proc.cmdline contains "/usr/sbin/nginx -c /etc/nginx/nginx.conf")
|
|
|
|
- macro: nginx_running_aws_s3_cp
|
|
condition: (proc.pname=nginx and proc.cmdline startswith "sh -c /usr/local/bin/aws s3 cp")
|
|
|
|
- macro: consul_running_net_scripts
|
|
condition: (proc.pname=consul and (proc.cmdline startswith "sh -c curl" or proc.cmdline startswith "sh -c nc"))
|
|
|
|
- macro: consul_running_alert_checks
|
|
condition: (proc.pname=consul and proc.cmdline startswith "sh -c /bin/consul-alerts")
|
|
|
|
- macro: serf_script
|
|
condition: (proc.cmdline startswith "sh -c serf")
|
|
|
|
- macro: check_process_status
|
|
condition: (proc.cmdline startswith "sh -c kill -0 ")
|
|
|
|
# In some cases, you may want to consider node processes run directly
|
|
# in containers as protected shell spawners. Examples include using
|
|
# pm2-docker or pm2 start some-app.js --no-daemon-mode as the direct
|
|
# entrypoint of the container, and when the node app is a long-lived
|
|
# server using something like express.
|
|
#
|
|
# However, there are other uses of node related to build pipelines for
|
|
# which node is not really a server but instead a general scripting
|
|
# tool. In these cases, shells are very likely and in these cases you
|
|
# don't want to consider node processes protected shell spawners.
|
|
#
|
|
# We have to choose one of these cases, so we consider node processes
|
|
# as unprotected by default. If you want to consider any node process
|
|
# run in a container as a protected shell spawner, override the below
|
|
# macro to remove the "never_true" clause, which allows it to take effect.
|
|
- macro: possibly_node_in_container
|
|
condition: (never_true and (proc.pname=node and proc.aname[3]=docker-containe))
|
|
|
|
# Similarly, you may want to consider any shell spawned by apache
|
|
# tomcat as suspect. The famous apache struts attack (CVE-2017-5638)
|
|
# could be exploited to do things like spawn shells.
|
|
#
|
|
# However, many applications *do* use tomcat to run arbitrary shells,
|
|
# as a part of build pipelines, etc.
|
|
#
|
|
# Like for node, we make this case opt-in.
|
|
- macro: possibly_parent_java_running_tomcat
|
|
condition: (never_true and proc.pname=java and proc.pcmdline contains org.apache.catalina.startup.Bootstrap)
|
|
|
|
- macro: protected_shell_spawner
|
|
condition: >
|
|
(proc.aname in (protected_shell_spawning_binaries)
|
|
or parent_java_running_zookeeper
|
|
or parent_java_running_kafka
|
|
or parent_java_running_elasticsearch
|
|
or parent_java_running_activemq
|
|
or parent_java_running_cassandra
|
|
or parent_java_running_jboss_wildfly
|
|
or parent_java_running_glassfish
|
|
or parent_java_running_hadoop
|
|
or parent_java_running_datastax
|
|
or possibly_parent_java_running_tomcat
|
|
or possibly_node_in_container)
|
|
|
|
- list: mesos_shell_binaries
|
|
items: [mesos-docker-ex, mesos-slave, mesos-health-ch]
|
|
|
|
# Note that runsv is both in protected_shell_spawner and the
|
|
# exclusions by pname. This means that runsv can itself spawn shells
|
|
# (the ./run and ./finish scripts), but the processes runsv can not
|
|
# spawn shells.
|
|
- rule: Run shell untrusted
|
|
desc: an attempt to spawn a shell below a non-shell application. Specific applications are monitored.
|
|
condition: >
|
|
spawned_process
|
|
and shell_procs
|
|
and proc.pname exists
|
|
and protected_shell_spawner
|
|
and not proc.pname in (shell_binaries, gitlab_binaries, cron_binaries, user_known_shell_spawn_binaries,
|
|
needrestart_binaries,
|
|
mesos_shell_binaries,
|
|
erl_child_setup, exechealthz,
|
|
PM2, PassengerWatchd, c_rehash, svlogd, logrotate, hhvm, serf,
|
|
lb-controller, nvidia-installe, runsv, statsite, erlexec, calico-node,
|
|
"puma reactor")
|
|
and not proc.cmdline in (known_shell_spawn_cmdlines)
|
|
and not proc.aname in (unicorn_launche)
|
|
and not consul_running_net_scripts
|
|
and not consul_running_alert_checks
|
|
and not nginx_starting_nginx
|
|
and not nginx_running_aws_s3_cp
|
|
and not run_by_package_mgmt_binaries
|
|
and not serf_script
|
|
and not check_process_status
|
|
and not run_by_foreman
|
|
and not python_mesos_marathon_scripting
|
|
and not splunk_running_forwarder
|
|
and not postgres_running_wal_e
|
|
and not redis_running_prepost_scripts
|
|
and not rabbitmq_running_scripts
|
|
and not rabbitmqctl_running_scripts
|
|
and not run_by_appdynamics
|
|
and not user_shell_container_exclusions
|
|
output: >
|
|
Shell spawned by untrusted binary (user=%user.name user_loginuid=%user.loginuid shell=%proc.name parent=%proc.pname
|
|
cmdline=%proc.cmdline pcmdline=%proc.pcmdline gparent=%proc.aname[2] ggparent=%proc.aname[3]
|
|
aname[4]=%proc.aname[4] aname[5]=%proc.aname[5] aname[6]=%proc.aname[6] aname[7]=%proc.aname[7] container_id=%container.id image=%container.image.repository)
|
|
priority: DEBUG
|
|
tags: [shell, mitre_execution]
|
|
|
|
- macro: allowed_openshift_registry_root
|
|
condition: >
|
|
(container.image.repository startswith openshift3/ or
|
|
container.image.repository startswith registry.redhat.io/openshift3/ or
|
|
container.image.repository startswith registry.access.redhat.com/openshift3/)
|
|
|
|
# Source: https://docs.openshift.com/enterprise/3.2/install_config/install/disconnected_install.html
|
|
- macro: openshift_image
|
|
condition: >
|
|
(allowed_openshift_registry_root and
|
|
(container.image.repository endswith /logging-deployment or
|
|
container.image.repository endswith /logging-elasticsearch or
|
|
container.image.repository endswith /logging-kibana or
|
|
container.image.repository endswith /logging-fluentd or
|
|
container.image.repository endswith /logging-auth-proxy or
|
|
container.image.repository endswith /metrics-deployer or
|
|
container.image.repository endswith /metrics-hawkular-metrics or
|
|
container.image.repository endswith /metrics-cassandra or
|
|
container.image.repository endswith /metrics-heapster or
|
|
container.image.repository endswith /ose-haproxy-router or
|
|
container.image.repository endswith /ose-deployer or
|
|
container.image.repository endswith /ose-sti-builder or
|
|
container.image.repository endswith /ose-docker-builder or
|
|
container.image.repository endswith /ose-pod or
|
|
container.image.repository endswith /ose-node or
|
|
container.image.repository endswith /ose-docker-registry or
|
|
container.image.repository endswith /prometheus-node-exporter or
|
|
container.image.repository endswith /image-inspector))
|
|
|
|
# https://docs.aws.amazon.com/eks/latest/userguide/add-ons-images.html
|
|
# official AWS EKS registry list. AWS has different ECR repo per region
|
|
- macro: allowed_aws_ecr_registry_root_for_eks
|
|
condition: >
|
|
(container.image.repository startswith "602401143452.dkr.ecr" or
|
|
container.image.repository startswith "877085696533.dkr.ecr" or
|
|
container.image.repository startswith "800184023465.dkr.ecr" or
|
|
container.image.repository startswith "918309763551.dkr.ecr" or
|
|
container.image.repository startswith "961992271922.dkr.ecr" or
|
|
container.image.repository startswith "590381155156.dkr.ecr" or
|
|
container.image.repository startswith "558608220178.dkr.ecr" or
|
|
container.image.repository startswith "151742754352.dkr.ecr" or
|
|
container.image.repository startswith "013241004608.dkr.ecr")
|
|
|
|
|
|
- macro: aws_eks_core_images
|
|
condition: >
|
|
(allowed_aws_ecr_registry_root_for_eks and
|
|
(container.image.repository endswith ".amazonaws.com/amazon-k8s-cni" or
|
|
container.image.repository endswith ".amazonaws.com/eks/kube-proxy"))
|
|
|
|
|
|
- macro: aws_eks_image_sensitive_mount
|
|
condition: >
|
|
(allowed_aws_ecr_registry_root_for_eks and container.image.repository endswith ".amazonaws.com/amazon-k8s-cni")
|
|
|
|
# These images are allowed both to run with --privileged and to mount
|
|
# sensitive paths from the host filesystem.
|
|
#
|
|
# NOTE: This list is only provided for backwards compatibility with
|
|
# older local falco rules files that may have been appending to
|
|
# trusted_images. To make customizations, it's better to add images to
|
|
# either privileged_images or falco_sensitive_mount_images.
|
|
- list: trusted_images
|
|
items: []
|
|
|
|
# Add conditions to this macro (probably in a separate file,
|
|
# overwriting this macro) to specify additional containers that are
|
|
# trusted and therefore allowed to run privileged *and* with sensitive
|
|
# mounts.
|
|
#
|
|
# Like trusted_images, this is deprecated in favor of
|
|
# user_privileged_containers and user_sensitive_mount_containers and
|
|
# is only provided for backwards compatibility.
|
|
#
|
|
# In this file, it just takes one of the images in trusted_containers
|
|
# and repeats it.
|
|
- macro: user_trusted_containers
|
|
condition: (never_true)
|
|
|
|
- list: sematext_images
|
|
items: [docker.io/sematext/sematext-agent-docker, docker.io/sematext/agent, docker.io/sematext/logagent,
|
|
registry.access.redhat.com/sematext/sematext-agent-docker,
|
|
registry.access.redhat.com/sematext/agent,
|
|
registry.access.redhat.com/sematext/logagent]
|
|
|
|
# These container images are allowed to run with --privileged
|
|
- list: falco_privileged_images
|
|
items: [
|
|
docker.io/calico/node,
|
|
calico/node,
|
|
docker.io/cloudnativelabs/kube-router,
|
|
docker.io/docker/ucp-agent,
|
|
docker.io/falcosecurity/falco,
|
|
docker.io/mesosphere/mesos-slave,
|
|
docker.io/rook/toolbox,
|
|
docker.io/sysdig/sysdig,
|
|
falcosecurity/falco,
|
|
gcr.io/google_containers/kube-proxy,
|
|
gcr.io/google-containers/startup-script,
|
|
gcr.io/projectcalico-org/node,
|
|
gke.gcr.io/kube-proxy,
|
|
gke.gcr.io/gke-metadata-server,
|
|
gke.gcr.io/netd-amd64,
|
|
gcr.io/google-containers/prometheus-to-sd,
|
|
k8s.gcr.io/ip-masq-agent-amd64,
|
|
k8s.gcr.io/kube-proxy,
|
|
k8s.gcr.io/prometheus-to-sd,
|
|
public.ecr.aws/falcosecurity/falco,
|
|
quay.io/calico/node,
|
|
sysdig/sysdig,
|
|
sematext_images
|
|
]
|
|
|
|
- macro: falco_privileged_containers
|
|
condition: (openshift_image or
|
|
user_trusted_containers or
|
|
aws_eks_core_images or
|
|
container.image.repository in (trusted_images) or
|
|
container.image.repository in (falco_privileged_images) or
|
|
container.image.repository startswith istio/proxy_ or
|
|
container.image.repository startswith quay.io/sysdig/)
|
|
|
|
# Add conditions to this macro (probably in a separate file,
|
|
# overwriting this macro) to specify additional containers that are
|
|
# allowed to run privileged
|
|
#
|
|
# In this file, it just takes one of the images in falco_privileged_images
|
|
# and repeats it.
|
|
- macro: user_privileged_containers
|
|
condition: (never_true)
|
|
|
|
# These container images are allowed to mount sensitive paths from the
|
|
# host filesystem.
|
|
- list: falco_sensitive_mount_images
|
|
items: [
|
|
docker.io/sysdig/sysdig, sysdig/sysdig,
|
|
docker.io/falcosecurity/falco, falcosecurity/falco, public.ecr.aws/falcosecurity/falco,
|
|
gcr.io/google_containers/hyperkube,
|
|
gcr.io/google_containers/kube-proxy, docker.io/calico/node,
|
|
docker.io/rook/toolbox, docker.io/cloudnativelabs/kube-router, docker.io/consul,
|
|
docker.io/datadog/docker-dd-agent, docker.io/datadog/agent, docker.io/docker/ucp-agent, docker.io/gliderlabs/logspout,
|
|
docker.io/netdata/netdata, docker.io/google/cadvisor, docker.io/prom/node-exporter,
|
|
amazon/amazon-ecs-agent, prom/node-exporter, amazon/cloudwatch-agent
|
|
]
|
|
|
|
- macro: falco_sensitive_mount_containers
|
|
condition: (user_trusted_containers or
|
|
aws_eks_image_sensitive_mount or
|
|
container.image.repository in (trusted_images) or
|
|
container.image.repository in (falco_sensitive_mount_images) or
|
|
container.image.repository startswith quay.io/sysdig/)
|
|
|
|
# Add conditions to this macro (probably in a separate file,
|
|
# overwriting this macro) to specify additional containers that are
|
|
# allowed to perform sensitive mounts.
|
|
#
|
|
# In this file, it just takes one of the images in falco_sensitive_mount_images
|
|
# and repeats it.
|
|
- macro: user_sensitive_mount_containers
|
|
condition: (never_true)
|
|
|
|
- rule: Launch Privileged Container
|
|
desc: Detect the initial process started in a privileged container. Exceptions are made for known trusted images.
|
|
condition: >
|
|
container_started and container
|
|
and container.privileged=true
|
|
and not falco_privileged_containers
|
|
and not user_privileged_containers
|
|
output: Privileged container started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
|
|
priority: INFO
|
|
tags: [container, cis, mitre_privilege_escalation, mitre_lateral_movement]
|
|
|
|
# For now, only considering a full mount of /etc as
|
|
# sensitive. Ideally, this would also consider all subdirectories
|
|
# below /etc as well, but the globbing mechanism
|
|
# doesn't allow exclusions of a full pattern, only single characters.
|
|
- macro: sensitive_mount
|
|
condition: (container.mount.dest[/proc*] != "N/A" or
|
|
container.mount.dest[/var/run/docker.sock] != "N/A" or
|
|
container.mount.dest[/var/run/crio/crio.sock] != "N/A" or
|
|
container.mount.dest[/run/containerd/containerd.sock] != "N/A" or
|
|
container.mount.dest[/var/lib/kubelet] != "N/A" or
|
|
container.mount.dest[/var/lib/kubelet/pki] != "N/A" or
|
|
container.mount.dest[/] != "N/A" or
|
|
container.mount.dest[/home/admin] != "N/A" or
|
|
container.mount.dest[/etc] != "N/A" or
|
|
container.mount.dest[/etc/kubernetes] != "N/A" or
|
|
container.mount.dest[/etc/kubernetes/manifests] != "N/A" or
|
|
container.mount.dest[/root*] != "N/A")
|
|
|
|
# The steps libcontainer performs to set up the root program for a container are:
|
|
# - clone + exec self to a program runc:[0:PARENT]
|
|
# - clone a program runc:[1:CHILD] which sets up all the namespaces
|
|
# - clone a second program runc:[2:INIT] + exec to the root program.
|
|
# The parent of runc:[2:INIT] is runc:0:PARENT]
|
|
# As soon as 1:CHILD is created, 0:PARENT exits, so there's a race
|
|
# where at the time 2:INIT execs the root program, 0:PARENT might have
|
|
# already exited, or might still be around. So we handle both.
|
|
# We also let runc:[1:CHILD] count as the parent process, which can occur
|
|
# when we lose events and lose track of state.
|
|
|
|
- macro: container_entrypoint
|
|
condition: (not proc.pname exists or proc.pname in (runc:[0:PARENT], runc:[1:CHILD], runc, docker-runc, exe, docker-runc-cur))
|
|
|
|
- rule: Launch Sensitive Mount Container
|
|
desc: >
|
|
Detect the initial process started by a container that has a mount from a sensitive host directory
|
|
(i.e. /proc). Exceptions are made for known trusted images.
|
|
condition: >
|
|
container_started and container
|
|
and sensitive_mount
|
|
and not falco_sensitive_mount_containers
|
|
and not user_sensitive_mount_containers
|
|
output: Container with sensitive mount started (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag mounts=%container.mounts)
|
|
priority: INFO
|
|
tags: [container, cis, mitre_lateral_movement]
|
|
|
|
# In a local/user rules file, you could override this macro to
|
|
# explicitly enumerate the container images that you want to run in
|
|
# your environment. In this main falco rules file, there isn't any way
|
|
# to know all the containers that can run, so any container is
|
|
# allowed, by using a filter that is guaranteed to evaluate to true.
|
|
# In the overridden macro, the condition would look something like
|
|
# (container.image.repository = vendor/container-1 or
|
|
# container.image.repository = vendor/container-2 or ...)
|
|
|
|
- macro: allowed_containers
|
|
condition: (container.id exists)
|
|
|
|
- rule: Launch Disallowed Container
|
|
desc: >
|
|
Detect the initial process started by a container that is not in a list of allowed containers.
|
|
condition: container_started and container and not allowed_containers
|
|
output: Container started and not in allowed list (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
|
|
priority: WARNING
|
|
tags: [container, mitre_lateral_movement]
|
|
|
|
- macro: user_known_system_user_login
|
|
condition: (never_true)
|
|
|
|
# Anything run interactively by root
|
|
# - condition: evt.type != switch and user.name = root and proc.name != sshd and interactive
|
|
# output: "Interactive root (%user.name %proc.name %evt.dir %evt.type %evt.args %fd.name)"
|
|
# priority: WARNING
|
|
|
|
- rule: System user interactive
|
|
desc: an attempt to run interactive commands by a system (i.e. non-login) user
|
|
condition: spawned_process and system_users and interactive and not user_known_system_user_login
|
|
output: "System user ran an interactive command (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline container_id=%container.id image=%container.image.repository)"
|
|
priority: INFO
|
|
tags: [users, mitre_remote_access_tools]
|
|
|
|
# In some cases, a shell is expected to be run in a container. For example, configuration
|
|
# management software may do this, which is expected.
|
|
- macro: user_expected_terminal_shell_in_container_conditions
|
|
condition: (never_true)
|
|
|
|
- rule: Terminal shell in container
|
|
desc: A shell was used as the entrypoint/exec point into a container with an attached terminal.
|
|
condition: >
|
|
spawned_process and container
|
|
and shell_procs and proc.tty != 0
|
|
and container_entrypoint
|
|
and not user_expected_terminal_shell_in_container_conditions
|
|
output: >
|
|
A shell was spawned in a container with an attached terminal (user=%user.name user_loginuid=%user.loginuid %container.info
|
|
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [container, shell, mitre_execution]
|
|
|
|
# For some container types (mesos), there isn't a container image to
|
|
# work with, and the container name is autogenerated, so there isn't
|
|
# any stable aspect of the software to work with. In this case, we
|
|
# fall back to allowing certain command lines.
|
|
|
|
- list: known_shell_spawn_cmdlines
|
|
items: [
|
|
'"sh -c uname -p 2> /dev/null"',
|
|
'"sh -c uname -s 2>&1"',
|
|
'"sh -c uname -r 2>&1"',
|
|
'"sh -c uname -v 2>&1"',
|
|
'"sh -c uname -a 2>&1"',
|
|
'"sh -c ruby -v 2>&1"',
|
|
'"sh -c getconf CLK_TCK"',
|
|
'"sh -c getconf PAGESIZE"',
|
|
'"sh -c LC_ALL=C LANG=C /sbin/ldconfig -p 2>/dev/null"',
|
|
'"sh -c LANG=C /sbin/ldconfig -p 2>/dev/null"',
|
|
'"sh -c /sbin/ldconfig -p 2>/dev/null"',
|
|
'"sh -c stty -a 2>/dev/null"',
|
|
'"sh -c stty -a < /dev/tty"',
|
|
'"sh -c stty -g < /dev/tty"',
|
|
'"sh -c node index.js"',
|
|
'"sh -c node index"',
|
|
'"sh -c node ./src/start.js"',
|
|
'"sh -c node app.js"',
|
|
'"sh -c node -e \"require(''nan'')\""',
|
|
'"sh -c node -e \"require(''nan'')\")"',
|
|
'"sh -c node $NODE_DEBUG_OPTION index.js "',
|
|
'"sh -c crontab -l 2"',
|
|
'"sh -c lsb_release -a"',
|
|
'"sh -c lsb_release -is 2>/dev/null"',
|
|
'"sh -c whoami"',
|
|
'"sh -c node_modules/.bin/bower-installer"',
|
|
'"sh -c /bin/hostname -f 2> /dev/null"',
|
|
'"sh -c locale -a"',
|
|
'"sh -c -t -i"',
|
|
'"sh -c openssl version"',
|
|
'"bash -c id -Gn kafadmin"',
|
|
'"sh -c /bin/sh -c ''date +%%s''"'
|
|
]
|
|
|
|
# This list allows for easy additions to the set of commands allowed
|
|
# to run shells in containers without having to without having to copy
|
|
# and override the entire run shell in container macro. Once
|
|
# https://github.com/draios/falco/issues/255 is fixed this will be a
|
|
# bit easier, as someone could append of any of the existing lists.
|
|
- list: user_known_shell_spawn_binaries
|
|
items: []
|
|
|
|
# This macro allows for easy additions to the set of commands allowed
|
|
# to run shells in containers without having to override the entire
|
|
# rule. Its default value is an expression that always is false, which
|
|
# becomes true when the "not ..." in the rule is applied.
|
|
- macro: user_shell_container_exclusions
|
|
condition: (never_true)
|
|
|
|
- macro: login_doing_dns_lookup
|
|
condition: (proc.name=login and fd.l4proto=udp and fd.sport=53)
|
|
|
|
# sockfamily ip is to exclude certain processes (like 'groups') that communicate on unix-domain sockets
|
|
# systemd can listen on ports to launch things like sshd on demand
|
|
- rule: System procs network activity
|
|
desc: any network activity performed by system binaries that are not expected to send or receive any network traffic
|
|
condition: >
|
|
(fd.sockfamily = ip and (system_procs or proc.name in (shell_binaries)))
|
|
and (inbound_outbound)
|
|
and not proc.name in (known_system_procs_network_activity_binaries)
|
|
and not login_doing_dns_lookup
|
|
and not user_expected_system_procs_network_activity_conditions
|
|
output: >
|
|
Known system binary sent/received network traffic
|
|
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [network, mitre_exfiltration]
|
|
|
|
# This list allows easily whitelisting system proc names that are
|
|
# expected to communicate on the network.
|
|
- list: known_system_procs_network_activity_binaries
|
|
items: [systemd, hostid, id]
|
|
|
|
# This macro allows specifying conditions under which a system binary
|
|
# is allowed to communicate on the network. For instance, only specific
|
|
# proc.cmdline values could be allowed to be more granular in what is
|
|
# allowed.
|
|
- macro: user_expected_system_procs_network_activity_conditions
|
|
condition: (never_true)
|
|
|
|
# When filled in, this should look something like:
|
|
# (proc.env contains "HTTP_PROXY=http://my.http.proxy.com ")
|
|
# The trailing space is intentional so avoid matching on prefixes of
|
|
# the actual proxy.
|
|
- macro: allowed_ssh_proxy_env
|
|
condition: (always_true)
|
|
|
|
- list: http_proxy_binaries
|
|
items: [curl, wget]
|
|
|
|
- macro: http_proxy_procs
|
|
condition: (proc.name in (http_proxy_binaries))
|
|
|
|
- rule: Program run with disallowed http proxy env
|
|
desc: An attempt to run a program with a disallowed HTTP_PROXY environment variable
|
|
condition: >
|
|
spawned_process and
|
|
http_proxy_procs and
|
|
not allowed_ssh_proxy_env and
|
|
proc.env icontains HTTP_PROXY
|
|
output: >
|
|
Program run with disallowed HTTP_PROXY environment variable
|
|
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline env=%proc.env parent=%proc.pname container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [host, users]
|
|
|
|
# In some environments, any attempt by a interpreted program (perl,
|
|
# python, ruby, etc) to listen for incoming connections or perform
|
|
# outgoing connections might be suspicious. These rules are not
|
|
# enabled by default, but you can modify the following macros to
|
|
# enable them.
|
|
|
|
- macro: consider_interpreted_inbound
|
|
condition: (never_true)
|
|
|
|
- macro: consider_interpreted_outbound
|
|
condition: (never_true)
|
|
|
|
- rule: Interpreted procs inbound network activity
|
|
desc: Any inbound network activity performed by any interpreted program (perl, python, ruby, etc.)
|
|
condition: >
|
|
(inbound and consider_interpreted_inbound
|
|
and interpreted_procs)
|
|
output: >
|
|
Interpreted program received/listened for network traffic
|
|
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [network, mitre_exfiltration]
|
|
|
|
- rule: Interpreted procs outbound network activity
|
|
desc: Any outbound network activity performed by any interpreted program (perl, python, ruby, etc.)
|
|
condition: >
|
|
(outbound and consider_interpreted_outbound
|
|
and interpreted_procs)
|
|
output: >
|
|
Interpreted program performed outgoing network connection
|
|
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [network, mitre_exfiltration]
|
|
|
|
- list: openvpn_udp_ports
|
|
items: [1194, 1197, 1198, 8080, 9201]
|
|
|
|
- list: l2tp_udp_ports
|
|
items: [500, 1701, 4500, 10000]
|
|
|
|
- list: statsd_ports
|
|
items: [8125]
|
|
|
|
- list: ntp_ports
|
|
items: [123]
|
|
|
|
# Some applications will connect a udp socket to an address only to
|
|
# test connectivity. Assuming the udp connect works, they will follow
|
|
# up with a tcp connect that actually sends/receives data.
|
|
#
|
|
# With that in mind, we listed a few commonly seen ports here to avoid
|
|
# some false positives. In addition, we make the main rule opt-in, so
|
|
# it's disabled by default.
|
|
|
|
- list: test_connect_ports
|
|
items: [0, 9, 80, 3306]
|
|
|
|
- macro: do_unexpected_udp_check
|
|
condition: (never_true)
|
|
|
|
- list: expected_udp_ports
|
|
items: [53, openvpn_udp_ports, l2tp_udp_ports, statsd_ports, ntp_ports, test_connect_ports]
|
|
|
|
- macro: expected_udp_traffic
|
|
condition: fd.port in (expected_udp_ports)
|
|
|
|
- rule: Unexpected UDP Traffic
|
|
desc: UDP traffic not on port 53 (DNS) or other commonly used ports
|
|
condition: (inbound_outbound) and do_unexpected_udp_check and fd.l4proto=udp and not expected_udp_traffic
|
|
output: >
|
|
Unexpected UDP Traffic Seen
|
|
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline connection=%fd.name proto=%fd.l4proto evt=%evt.type %evt.args container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [network, mitre_exfiltration]
|
|
|
|
# With the current restriction on system calls handled by falco
|
|
# (e.g. excluding read/write/sendto/recvfrom/etc, this rule won't
|
|
# trigger).
|
|
# - rule: Ssh error in syslog
|
|
# desc: any ssh errors (failed logins, disconnects, ...) sent to syslog
|
|
# condition: syslog and ssh_error_message and evt.dir = <
|
|
# output: "sshd sent error message to syslog (error=%evt.buffer)"
|
|
# priority: WARNING
|
|
|
|
- macro: somebody_becoming_themselves
|
|
condition: ((user.name=nobody and evt.arg.uid=nobody) or
|
|
(user.name=www-data and evt.arg.uid=www-data) or
|
|
(user.name=_apt and evt.arg.uid=_apt) or
|
|
(user.name=postfix and evt.arg.uid=postfix) or
|
|
(user.name=pki-agent and evt.arg.uid=pki-agent) or
|
|
(user.name=pki-acme and evt.arg.uid=pki-acme) or
|
|
(user.name=nfsnobody and evt.arg.uid=nfsnobody) or
|
|
(user.name=postgres and evt.arg.uid=postgres))
|
|
|
|
- macro: nrpe_becoming_nagios
|
|
condition: (proc.name=nrpe and evt.arg.uid=nagios)
|
|
|
|
# In containers, the user name might be for a uid that exists in the
|
|
# container but not on the host. (See
|
|
# https://github.com/draios/sysdig/issues/954). So in that case, allow
|
|
# a setuid.
|
|
- macro: known_user_in_container
|
|
condition: (container and user.name != "N/A")
|
|
|
|
# Add conditions to this macro (probably in a separate file,
|
|
# overwriting this macro) to allow for specific combinations of
|
|
# programs changing users by calling setuid.
|
|
#
|
|
# In this file, it just takes one of the condition in the base macro
|
|
# and repeats it.
|
|
- macro: user_known_non_sudo_setuid_conditions
|
|
condition: user.name=root
|
|
|
|
# sshd, mail programs attempt to setuid to root even when running as non-root. Excluding here to avoid meaningless FPs
|
|
- rule: Non sudo setuid
|
|
desc: >
|
|
an attempt to change users by calling setuid. sudo/su are excluded. users "root" and "nobody"
|
|
suing to itself are also excluded, as setuid calls typically involve dropping privileges.
|
|
condition: >
|
|
evt.type=setuid and evt.dir=>
|
|
and (known_user_in_container or not container)
|
|
and not (user.name=root or user.uid=0)
|
|
and not somebody_becoming_themselves
|
|
and not proc.name in (known_setuid_binaries, userexec_binaries, mail_binaries, docker_binaries,
|
|
nomachine_binaries)
|
|
and not proc.name startswith "runc:"
|
|
and not java_running_sdjagent
|
|
and not nrpe_becoming_nagios
|
|
and not user_known_non_sudo_setuid_conditions
|
|
output: >
|
|
Unexpected setuid call by non-sudo, non-root program (user=%user.name user_loginuid=%user.loginuid cur_uid=%user.uid parent=%proc.pname
|
|
command=%proc.cmdline uid=%evt.arg.uid container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [users, mitre_privilege_escalation]
|
|
|
|
- macro: user_known_user_management_activities
|
|
condition: (never_true)
|
|
|
|
- macro: chage_list
|
|
condition: (proc.name=chage and (proc.cmdline contains "-l" or proc.cmdline contains "--list"))
|
|
|
|
- rule: User mgmt binaries
|
|
desc: >
|
|
activity by any programs that can manage users, passwords, or permissions. sudo and su are excluded.
|
|
Activity in containers is also excluded--some containers create custom users on top
|
|
of a base linux distribution at startup.
|
|
Some innocuous command lines that don't actually change anything are excluded.
|
|
condition: >
|
|
spawned_process and proc.name in (user_mgmt_binaries) and
|
|
not proc.name in (su, sudo, lastlog, nologin, unix_chkpwd) and not container and
|
|
not proc.pname in (cron_binaries, systemd, systemd.postins, udev.postinst, run-parts) and
|
|
not proc.cmdline startswith "passwd -S" and
|
|
not proc.cmdline startswith "useradd -D" and
|
|
not proc.cmdline startswith "systemd --version" and
|
|
not run_by_qualys and
|
|
not run_by_sumologic_securefiles and
|
|
not run_by_yum and
|
|
not run_by_ms_oms and
|
|
not run_by_google_accounts_daemon and
|
|
not chage_list and
|
|
not user_known_user_management_activities
|
|
output: >
|
|
User management binary command run outside of container
|
|
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4])
|
|
priority: NOTICE
|
|
tags: [host, users, mitre_persistence]
|
|
|
|
- list: allowed_dev_files
|
|
items: [
|
|
/dev/null, /dev/stdin, /dev/stdout, /dev/stderr,
|
|
/dev/random, /dev/urandom, /dev/console, /dev/kmsg
|
|
]
|
|
|
|
- macro: user_known_create_files_below_dev_activities
|
|
condition: (never_true)
|
|
|
|
# (we may need to add additional checks against false positives, see:
|
|
# https://bugs.launchpad.net/ubuntu/+source/rkhunter/+bug/86153)
|
|
- rule: Create files below dev
|
|
desc: creating any files below /dev other than known programs that manage devices. Some rootkits hide files in /dev.
|
|
condition: >
|
|
fd.directory = /dev and
|
|
(evt.type = creat or (evt.type in (open,openat,openat2) and evt.arg.flags contains O_CREAT))
|
|
and not proc.name in (dev_creation_binaries)
|
|
and not fd.name in (allowed_dev_files)
|
|
and not fd.name startswith /dev/tty
|
|
and not user_known_create_files_below_dev_activities
|
|
output: "File created below /dev by untrusted program (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)"
|
|
priority: ERROR
|
|
tags: [filesystem, mitre_persistence]
|
|
|
|
|
|
# In a local/user rules file, you could override this macro to
|
|
# explicitly enumerate the container images that you want to allow
|
|
# access to EC2 metadata. In this main falco rules file, there isn't
|
|
# any way to know all the containers that should have access, so any
|
|
# container is allowed, by repeating the "container" macro. In the
|
|
# overridden macro, the condition would look something like
|
|
# (container.image.repository = vendor/container-1 or
|
|
# container.image.repository = vendor/container-2 or ...)
|
|
- macro: ec2_metadata_containers
|
|
condition: container
|
|
|
|
# On EC2 instances, 169.254.169.254 is a special IP used to fetch
|
|
# metadata about the instance. It may be desirable to prevent access
|
|
# to this IP from containers.
|
|
- rule: Contact EC2 Instance Metadata Service From Container
|
|
desc: Detect attempts to contact the EC2 Instance Metadata Service from a container
|
|
condition: outbound and fd.sip="169.254.169.254" and container and not ec2_metadata_containers
|
|
output: Outbound connection to EC2 instance metadata service (command=%proc.cmdline connection=%fd.name %container.info image=%container.image.repository:%container.image.tag)
|
|
priority: NOTICE
|
|
tags: [network, aws, container, mitre_discovery]
|
|
|
|
|
|
# This rule is not enabled by default, since this rule is for cloud environment(GCP, AWS and Azure) only.
|
|
# If you want to enable this rule, overwrite the first macro,
|
|
# And you can filter the container that you want to allow access to metadata by overwriting the second macro.
|
|
- macro: consider_metadata_access
|
|
condition: (never_true)
|
|
|
|
- macro: user_known_metadata_access
|
|
condition: (k8s.ns.name = "kube-system")
|
|
|
|
# On GCP, AWS and Azure, 169.254.169.254 is a special IP used to fetch
|
|
# metadata about the instance. The metadata could be used to get credentials by attackers.
|
|
- rule: Contact cloud metadata service from container
|
|
desc: Detect attempts to contact the Cloud Instance Metadata Service from a container
|
|
condition: outbound and fd.sip="169.254.169.254" and container and consider_metadata_access and not user_known_metadata_access
|
|
output: Outbound connection to cloud instance metadata service (command=%proc.cmdline connection=%fd.name %container.info image=%container.image.repository:%container.image.tag)
|
|
priority: NOTICE
|
|
tags: [network, container, mitre_discovery]
|
|
|
|
# Containers from IBM Cloud
|
|
- list: ibm_cloud_containers
|
|
items:
|
|
- icr.io/ext/sysdig/agent
|
|
- registry.ng.bluemix.net/armada-master/metrics-server-amd64
|
|
- registry.ng.bluemix.net/armada-master/olm
|
|
|
|
# In a local/user rules file, list the namespace or container images that are
|
|
# allowed to contact the K8s API Server from within a container. This
|
|
# might cover cases where the K8s infrastructure itself is running
|
|
# within a container.
|
|
- macro: k8s_containers
|
|
condition: >
|
|
(container.image.repository in (gcr.io/google_containers/hyperkube-amd64,
|
|
gcr.io/google_containers/kube2sky,
|
|
docker.io/sysdig/sysdig, docker.io/falcosecurity/falco,
|
|
sysdig/sysdig, falcosecurity/falco,
|
|
fluent/fluentd-kubernetes-daemonset, prom/prometheus,
|
|
ibm_cloud_containers,
|
|
public.ecr.aws/falcosecurity/falco)
|
|
or (k8s.ns.name = "kube-system"))
|
|
|
|
- macro: k8s_api_server
|
|
condition: (fd.sip.name="kubernetes.default.svc.cluster.local")
|
|
|
|
- macro: user_known_contact_k8s_api_server_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Contact K8S API Server From Container
|
|
desc: Detect attempts to contact the K8S API Server from a container
|
|
condition: >
|
|
evt.type=connect and evt.dir=< and
|
|
(fd.typechar=4 or fd.typechar=6) and
|
|
container and
|
|
not k8s_containers and
|
|
k8s_api_server and
|
|
not user_known_contact_k8s_api_server_activities
|
|
output: Unexpected connection to K8s API Server from container (command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag connection=%fd.name)
|
|
priority: NOTICE
|
|
tags: [network, k8s, container, mitre_discovery]
|
|
|
|
# In a local/user rules file, list the container images that are
|
|
# allowed to contact NodePort services from within a container. This
|
|
# might cover cases where the K8s infrastructure itself is running
|
|
# within a container.
|
|
#
|
|
# By default, all containers are allowed to contact NodePort services.
|
|
- macro: nodeport_containers
|
|
condition: container
|
|
|
|
- rule: Unexpected K8s NodePort Connection
|
|
desc: Detect attempts to use K8s NodePorts from a container
|
|
condition: (inbound_outbound) and fd.sport >= 30000 and fd.sport <= 32767 and container and not nodeport_containers
|
|
output: Unexpected K8s NodePort Connection (command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
|
|
priority: NOTICE
|
|
tags: [network, k8s, container, mitre_port_knocking]
|
|
|
|
- list: network_tool_binaries
|
|
items: [nc, ncat, nmap, dig, tcpdump, tshark, ngrep, telnet, mitmproxy, socat, zmap]
|
|
|
|
- macro: network_tool_procs
|
|
condition: (proc.name in (network_tool_binaries))
|
|
|
|
# In a local/user rules file, create a condition that matches legitimate uses
|
|
# of a package management process inside a container.
|
|
#
|
|
# For example:
|
|
# - macro: user_known_package_manager_in_container
|
|
# condition: proc.cmdline="dpkg -l"
|
|
- macro: user_known_package_manager_in_container
|
|
condition: (never_true)
|
|
|
|
# Container is supposed to be immutable. Package management should be done in building the image.
|
|
- rule: Launch Package Management Process in Container
|
|
desc: Package management process ran inside container
|
|
condition: >
|
|
spawned_process
|
|
and container
|
|
and user.name != "_apt"
|
|
and package_mgmt_procs
|
|
and not package_mgmt_ancestor_procs
|
|
and not user_known_package_manager_in_container
|
|
output: >
|
|
Package management process launched in container (user=%user.name user_loginuid=%user.loginuid
|
|
command=%proc.cmdline container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
|
priority: ERROR
|
|
tags: [process, mitre_persistence]
|
|
|
|
- rule: Netcat Remote Code Execution in Container
|
|
desc: Netcat Program runs inside container that allows remote code execution
|
|
condition: >
|
|
spawned_process and container and
|
|
((proc.name = "nc" and (proc.args contains "-e" or proc.args contains "-c")) or
|
|
(proc.name = "ncat" and (proc.args contains "--sh-exec" or proc.args contains "--exec" or proc.args contains "-e "
|
|
or proc.args contains "-c " or proc.args contains "--lua-exec"))
|
|
)
|
|
output: >
|
|
Netcat runs inside container that allows remote code execution (user=%user.name user_loginuid=%user.loginuid
|
|
command=%proc.cmdline container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
|
priority: WARNING
|
|
tags: [network, process, mitre_execution]
|
|
|
|
- macro: user_known_network_tool_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Launch Suspicious Network Tool in Container
|
|
desc: Detect network tools launched inside container
|
|
condition: >
|
|
spawned_process and container and network_tool_procs and not user_known_network_tool_activities
|
|
output: >
|
|
Network tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname
|
|
container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
|
priority: NOTICE
|
|
tags: [network, process, mitre_discovery, mitre_exfiltration]
|
|
|
|
# This rule is not enabled by default, as there are legitimate use
|
|
# cases for these tools on hosts. If you want to enable it, modify the
|
|
# following macro.
|
|
- macro: consider_network_tools_on_host
|
|
condition: (never_true)
|
|
|
|
- rule: Launch Suspicious Network Tool on Host
|
|
desc: Detect network tools launched on the host
|
|
condition: >
|
|
spawned_process and
|
|
not container and
|
|
consider_network_tools_on_host and
|
|
network_tool_procs and
|
|
not user_known_network_tool_activities
|
|
output: >
|
|
Network tool launched on host (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname)
|
|
priority: NOTICE
|
|
tags: [network, process, mitre_discovery, mitre_exfiltration]
|
|
|
|
- list: grep_binaries
|
|
items: [grep, egrep, fgrep]
|
|
|
|
- macro: grep_commands
|
|
condition: (proc.name in (grep_binaries))
|
|
|
|
# a less restrictive search for things that might be passwords/ssh/user etc.
|
|
- macro: grep_more
|
|
condition: (never_true)
|
|
|
|
- macro: private_key_or_password
|
|
condition: >
|
|
(proc.args icontains "BEGIN PRIVATE" or
|
|
proc.args icontains "BEGIN RSA PRIVATE" or
|
|
proc.args icontains "BEGIN DSA PRIVATE" or
|
|
proc.args icontains "BEGIN EC PRIVATE" or
|
|
(grep_more and
|
|
(proc.args icontains " pass " or
|
|
proc.args icontains " ssh " or
|
|
proc.args icontains " user "))
|
|
)
|
|
|
|
- rule: Search Private Keys or Passwords
|
|
desc: >
|
|
Detect grep private keys or passwords activity.
|
|
condition: >
|
|
(spawned_process and
|
|
((grep_commands and private_key_or_password) or
|
|
(proc.name = "find" and (proc.args contains "id_rsa" or proc.args contains "id_dsa")))
|
|
)
|
|
output: >
|
|
Grep private keys or passwords activities found
|
|
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline container_id=%container.id container_name=%container.name
|
|
image=%container.image.repository:%container.image.tag)
|
|
priority:
|
|
WARNING
|
|
tags: [process, mitre_credential_access]
|
|
|
|
- list: log_directories
|
|
items: [/var/log, /dev/log]
|
|
|
|
- list: log_files
|
|
items: [syslog, auth.log, secure, kern.log, cron, user.log, dpkg.log, last.log, yum.log, access_log, mysql.log, mysqld.log]
|
|
|
|
- macro: access_log_files
|
|
condition: (fd.directory in (log_directories) or fd.filename in (log_files))
|
|
|
|
# a placeholder for whitelist log files that could be cleared. Recommend the macro as (fd.name startswith "/var/log/app1*")
|
|
- macro: allowed_clear_log_files
|
|
condition: (never_true)
|
|
|
|
- macro: trusted_logging_images
|
|
condition: (container.image.repository endswith "splunk/fluentd-hec" or
|
|
container.image.repository endswith "fluent/fluentd-kubernetes-daemonset" or
|
|
container.image.repository endswith "openshift3/ose-logging-fluentd" or
|
|
container.image.repository endswith "containernetworking/azure-npm")
|
|
|
|
- rule: Clear Log Activities
|
|
desc: Detect clearing of critical log files
|
|
condition: >
|
|
open_write and
|
|
access_log_files and
|
|
evt.arg.flags contains "O_TRUNC" and
|
|
not trusted_logging_images and
|
|
not allowed_clear_log_files
|
|
output: >
|
|
Log files were tampered (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
|
|
priority:
|
|
WARNING
|
|
tags: [file, mitre_defense_evasion]
|
|
|
|
- list: data_remove_commands
|
|
items: [shred, mkfs, mke2fs]
|
|
|
|
- macro: clear_data_procs
|
|
condition: (proc.name in (data_remove_commands))
|
|
|
|
- macro: user_known_remove_data_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Remove Bulk Data from Disk
|
|
desc: Detect process running to clear bulk data from disk
|
|
condition: spawned_process and clear_data_procs and not user_known_remove_data_activities
|
|
output: >
|
|
Bulk data has been removed from disk (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
|
|
priority:
|
|
WARNING
|
|
tags: [process, mitre_persistence]
|
|
|
|
- macro: modify_shell_history
|
|
condition: >
|
|
(modify and (
|
|
evt.arg.name contains "bash_history" or
|
|
evt.arg.name endswith "zsh_history" or
|
|
evt.arg.name contains "fish_read_history" or
|
|
evt.arg.name endswith "fish_history" or
|
|
evt.arg.oldpath contains "bash_history" or
|
|
evt.arg.oldpath endswith "zsh_history" or
|
|
evt.arg.oldpath contains "fish_read_history" or
|
|
evt.arg.oldpath endswith "fish_history" or
|
|
evt.arg.path contains "bash_history" or
|
|
evt.arg.path endswith "zsh_history" or
|
|
evt.arg.path contains "fish_read_history" or
|
|
evt.arg.path endswith "fish_history"))
|
|
|
|
- macro: truncate_shell_history
|
|
condition: >
|
|
(open_write and (
|
|
fd.name contains "bash_history" or
|
|
fd.name endswith "zsh_history" or
|
|
fd.name contains "fish_read_history" or
|
|
fd.name endswith "fish_history") and evt.arg.flags contains "O_TRUNC")
|
|
|
|
- macro: var_lib_docker_filepath
|
|
condition: (evt.arg.name startswith /var/lib/docker or fd.name startswith /var/lib/docker)
|
|
|
|
- rule: Delete or rename shell history
|
|
desc: Detect shell history deletion
|
|
condition: >
|
|
(modify_shell_history or truncate_shell_history) and
|
|
not var_lib_docker_filepath and
|
|
not proc.name in (docker_binaries)
|
|
output: >
|
|
Shell history had been deleted or renamed (user=%user.name user_loginuid=%user.loginuid type=%evt.type command=%proc.cmdline fd.name=%fd.name name=%evt.arg.name path=%evt.arg.path oldpath=%evt.arg.oldpath %container.info)
|
|
priority:
|
|
WARNING
|
|
tags: [process, mitre_defense_evasion]
|
|
|
|
# This rule is deprecated and will/should never be triggered. Keep it here for backport compatibility.
|
|
# Rule Delete or rename shell history is the preferred rule to use now.
|
|
- rule: Delete Bash History
|
|
desc: Detect bash history deletion
|
|
condition: >
|
|
((spawned_process and proc.name in (shred, rm, mv) and proc.args contains "bash_history") or
|
|
(open_write and fd.name contains "bash_history" and evt.arg.flags contains "O_TRUNC"))
|
|
output: >
|
|
Shell history had been deleted or renamed (user=%user.name user_loginuid=%user.loginuid type=%evt.type command=%proc.cmdline fd.name=%fd.name name=%evt.arg.name path=%evt.arg.path oldpath=%evt.arg.oldpath %container.info)
|
|
priority:
|
|
WARNING
|
|
tags: [process, mitre_defense_evasion]
|
|
|
|
- macro: consider_all_chmods
|
|
condition: (always_true)
|
|
|
|
- list: user_known_chmod_applications
|
|
items: [hyperkube, kubelet, k3s-agent]
|
|
|
|
# This macro should be overridden in user rules as needed. This is useful if a given application
|
|
# should not be ignored altogether with the user_known_chmod_applications list, but only in
|
|
# specific conditions.
|
|
- macro: user_known_set_setuid_or_setgid_bit_conditions
|
|
condition: (never_true)
|
|
|
|
- rule: Set Setuid or Setgid bit
|
|
desc: >
|
|
When the setuid or setgid bits are set for an application,
|
|
this means that the application will run with the privileges of the owning user or group respectively.
|
|
Detect setuid or setgid bits set via chmod
|
|
condition: >
|
|
consider_all_chmods and chmod and (evt.arg.mode contains "S_ISUID" or evt.arg.mode contains "S_ISGID")
|
|
and not proc.name in (user_known_chmod_applications)
|
|
and not exe_running_docker_save
|
|
and not user_known_set_setuid_or_setgid_bit_conditions
|
|
enabled: false
|
|
output: >
|
|
Setuid or setgid bit is set via chmod (fd=%evt.arg.fd filename=%evt.arg.filename mode=%evt.arg.mode user=%user.name user_loginuid=%user.loginuid process=%proc.name
|
|
command=%proc.cmdline container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
|
priority:
|
|
NOTICE
|
|
tags: [process, mitre_persistence]
|
|
|
|
- list: exclude_hidden_directories
|
|
items: [/root/.cassandra]
|
|
|
|
# To use this rule, you should modify consider_hidden_file_creation.
|
|
- macro: consider_hidden_file_creation
|
|
condition: (never_true)
|
|
|
|
- macro: user_known_create_hidden_file_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Create Hidden Files or Directories
|
|
desc: Detect hidden files or directories created
|
|
condition: >
|
|
((modify and evt.arg.newpath contains "/.") or
|
|
(mkdir and evt.arg.path contains "/.") or
|
|
(open_write and evt.arg.flags contains "O_CREAT" and fd.name contains "/." and not fd.name pmatch (exclude_hidden_directories))) and
|
|
consider_hidden_file_creation and
|
|
not user_known_create_hidden_file_activities
|
|
and not exe_running_docker_save
|
|
output: >
|
|
Hidden file or directory created (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline
|
|
file=%fd.name newpath=%evt.arg.newpath container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
|
priority:
|
|
NOTICE
|
|
tags: [file, mitre_persistence]
|
|
|
|
- list: remote_file_copy_binaries
|
|
items: [rsync, scp, sftp, dcp]
|
|
|
|
- macro: remote_file_copy_procs
|
|
condition: (proc.name in (remote_file_copy_binaries))
|
|
|
|
# Users should overwrite this macro to specify conditions under which a
|
|
# Custom condition for use of remote file copy tool in container
|
|
- macro: user_known_remote_file_copy_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Launch Remote File Copy Tools in Container
|
|
desc: Detect remote file copy tools launched in container
|
|
condition: >
|
|
spawned_process
|
|
and container
|
|
and remote_file_copy_procs
|
|
and not user_known_remote_file_copy_activities
|
|
output: >
|
|
Remote file copy tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname
|
|
container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
|
priority: NOTICE
|
|
tags: [network, process, mitre_lateral_movement, mitre_exfiltration]
|
|
|
|
- rule: Create Symlink Over Sensitive Files
|
|
desc: Detect symlink created over sensitive files
|
|
condition: >
|
|
create_symlink and
|
|
(evt.arg.target in (sensitive_file_names) or evt.arg.target in (sensitive_directory_names))
|
|
output: >
|
|
Symlinks created over sensitive files (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline target=%evt.arg.target linkpath=%evt.arg.linkpath parent_process=%proc.pname)
|
|
priority: WARNING
|
|
tags: [file, mitre_exfiltration]
|
|
|
|
- rule: Create Hardlink Over Sensitive Files
|
|
desc: Detect hardlink created over sensitive files
|
|
condition: >
|
|
create_hardlink and
|
|
(evt.arg.oldpath in (sensitive_file_names))
|
|
output: >
|
|
Hardlinks created over sensitive files (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline target=%evt.arg.oldpath linkpath=%evt.arg.newpath parent_process=%proc.pname)
|
|
priority: WARNING
|
|
tags: [file, mitre_exfiltration]
|
|
|
|
- list: miner_ports
|
|
items: [
|
|
25, 3333, 3334, 3335, 3336, 3357, 4444,
|
|
5555, 5556, 5588, 5730, 6099, 6666, 7777,
|
|
7778, 8000, 8001, 8008, 8080, 8118, 8333,
|
|
8888, 8899, 9332, 9999, 14433, 14444,
|
|
45560, 45700
|
|
]
|
|
|
|
- list: miner_domains
|
|
items: [
|
|
"asia1.ethpool.org","ca.minexmr.com",
|
|
"cn.stratum.slushpool.com","de.minexmr.com",
|
|
"eth-ar.dwarfpool.com","eth-asia.dwarfpool.com",
|
|
"eth-asia1.nanopool.org","eth-au.dwarfpool.com",
|
|
"eth-au1.nanopool.org","eth-br.dwarfpool.com",
|
|
"eth-cn.dwarfpool.com","eth-cn2.dwarfpool.com",
|
|
"eth-eu.dwarfpool.com","eth-eu1.nanopool.org",
|
|
"eth-eu2.nanopool.org","eth-hk.dwarfpool.com",
|
|
"eth-jp1.nanopool.org","eth-ru.dwarfpool.com",
|
|
"eth-ru2.dwarfpool.com","eth-sg.dwarfpool.com",
|
|
"eth-us-east1.nanopool.org","eth-us-west1.nanopool.org",
|
|
"eth-us.dwarfpool.com","eth-us2.dwarfpool.com",
|
|
"eu.stratum.slushpool.com","eu1.ethermine.org",
|
|
"eu1.ethpool.org","fr.minexmr.com",
|
|
"mine.moneropool.com","mine.xmrpool.net",
|
|
"pool.minexmr.com","pool.monero.hashvault.pro",
|
|
"pool.supportxmr.com","sg.minexmr.com",
|
|
"sg.stratum.slushpool.com","stratum-eth.antpool.com",
|
|
"stratum-ltc.antpool.com","stratum-zec.antpool.com",
|
|
"stratum.antpool.com","us-east.stratum.slushpool.com",
|
|
"us1.ethermine.org","us1.ethpool.org",
|
|
"us2.ethermine.org","us2.ethpool.org",
|
|
"xmr-asia1.nanopool.org","xmr-au1.nanopool.org",
|
|
"xmr-eu1.nanopool.org","xmr-eu2.nanopool.org",
|
|
"xmr-jp1.nanopool.org","xmr-us-east1.nanopool.org",
|
|
"xmr-us-west1.nanopool.org","xmr.crypto-pool.fr",
|
|
"xmr.pool.minergate.com", "rx.unmineable.com",
|
|
"ss.antpool.com","dash.antpool.com",
|
|
"eth.antpool.com","zec.antpool.com",
|
|
"xmc.antpool.com","btm.antpool.com",
|
|
"stratum-dash.antpool.com","stratum-xmc.antpool.com",
|
|
"stratum-btm.antpool.com"
|
|
]
|
|
|
|
- list: https_miner_domains
|
|
items: [
|
|
"ca.minexmr.com",
|
|
"cn.stratum.slushpool.com",
|
|
"de.minexmr.com",
|
|
"fr.minexmr.com",
|
|
"mine.moneropool.com",
|
|
"mine.xmrpool.net",
|
|
"pool.minexmr.com",
|
|
"sg.minexmr.com",
|
|
"stratum-eth.antpool.com",
|
|
"stratum-ltc.antpool.com",
|
|
"stratum-zec.antpool.com",
|
|
"stratum.antpool.com",
|
|
"xmr.crypto-pool.fr",
|
|
"ss.antpool.com",
|
|
"stratum-dash.antpool.com",
|
|
"stratum-xmc.antpool.com",
|
|
"stratum-btm.antpool.com",
|
|
"btm.antpool.com"
|
|
]
|
|
|
|
- list: http_miner_domains
|
|
items: [
|
|
"ca.minexmr.com",
|
|
"de.minexmr.com",
|
|
"fr.minexmr.com",
|
|
"mine.moneropool.com",
|
|
"mine.xmrpool.net",
|
|
"pool.minexmr.com",
|
|
"sg.minexmr.com",
|
|
"xmr.crypto-pool.fr"
|
|
]
|
|
|
|
# Add rule based on crypto mining IOCs
|
|
- macro: minerpool_https
|
|
condition: (fd.sport="443" and fd.sip.name in (https_miner_domains))
|
|
|
|
- macro: minerpool_http
|
|
condition: (fd.sport="80" and fd.sip.name in (http_miner_domains))
|
|
|
|
- macro: minerpool_other
|
|
condition: (fd.sport in (miner_ports) and fd.sip.name in (miner_domains))
|
|
|
|
- macro: net_miner_pool
|
|
condition: (evt.type in (sendto, sendmsg) and evt.dir=< and (fd.net != "127.0.0.0/8" and not fd.snet in (rfc_1918_addresses)) and ((minerpool_http) or (minerpool_https) or (minerpool_other)))
|
|
|
|
- macro: trusted_images_query_miner_domain_dns
|
|
condition: (container.image.repository in (docker.io/falcosecurity/falco, falcosecurity/falco, public.ecr.aws/falcosecurity/falco))
|
|
append: false
|
|
|
|
# The rule is disabled by default.
|
|
# Note: falco will send DNS request to resolve miner pool domain which may trigger alerts in your environment.
|
|
- rule: Detect outbound connections to common miner pool ports
|
|
desc: Miners typically connect to miner pools on common ports.
|
|
condition: net_miner_pool and not trusted_images_query_miner_domain_dns
|
|
enabled: false
|
|
output: Outbound connection to IP/Port flagged by https://cryptoioc.ch (command=%proc.cmdline port=%fd.rport ip=%fd.rip container=%container.info image=%container.image.repository)
|
|
priority: CRITICAL
|
|
tags: [network, mitre_execution]
|
|
|
|
- rule: Detect crypto miners using the Stratum protocol
|
|
desc: Miners typically specify the mining pool to connect to with a URI that begins with 'stratum+tcp'
|
|
condition: spawned_process and (proc.cmdline contains "stratum+tcp" or proc.cmdline contains "stratum2+tcp" or proc.cmdline contains "stratum+ssl" or proc.cmdline contains "stratum2+ssl")
|
|
output: Possible miner running (command=%proc.cmdline container=%container.info image=%container.image.repository)
|
|
priority: CRITICAL
|
|
tags: [process, mitre_execution]
|
|
|
|
- list: k8s_client_binaries
|
|
items: [docker, kubectl, crictl]
|
|
|
|
- list: user_known_k8s_ns_kube_system_images
|
|
items: [
|
|
k8s.gcr.io/fluentd-gcp-scaler,
|
|
k8s.gcr.io/node-problem-detector/node-problem-detector
|
|
]
|
|
|
|
- list: user_known_k8s_images
|
|
items: [
|
|
mcr.microsoft.com/aks/hcp/hcp-tunnel-front
|
|
]
|
|
|
|
# Whitelist for known docker client binaries run inside container
|
|
# - k8s.gcr.io/fluentd-gcp-scaler in GCP/GKE
|
|
- macro: user_known_k8s_client_container
|
|
condition: >
|
|
(k8s.ns.name="kube-system" and container.image.repository in (user_known_k8s_ns_kube_system_images)) or container.image.repository in (user_known_k8s_images)
|
|
|
|
- macro: user_known_k8s_client_container_parens
|
|
condition: (user_known_k8s_client_container)
|
|
|
|
- rule: The docker client is executed in a container
|
|
desc: Detect a k8s client tool executed inside a container
|
|
condition: spawned_process and container and not user_known_k8s_client_container_parens and proc.name in (k8s_client_binaries)
|
|
output: "Docker or kubernetes client executed in container (user=%user.name user_loginuid=%user.loginuid %container.info parent=%proc.pname cmdline=%proc.cmdline image=%container.image.repository:%container.image.tag)"
|
|
priority: WARNING
|
|
tags: [container, mitre_execution]
|
|
|
|
|
|
# This rule is enabled by default.
|
|
# If you want to disable it, modify the following macro.
|
|
- macro: consider_packet_socket_communication
|
|
condition: (always_true)
|
|
|
|
- list: user_known_packet_socket_binaries
|
|
items: []
|
|
|
|
- rule: Packet socket created in container
|
|
desc: Detect new packet socket at the device driver (OSI Layer 2) level in a container. Packet socket could be used for ARP Spoofing and privilege escalation(CVE-2020-14386) by attacker.
|
|
condition: evt.type=socket and evt.arg[0]=AF_PACKET and consider_packet_socket_communication and container and not proc.name in (user_known_packet_socket_binaries)
|
|
output: Packet socket was created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline socket_info=%evt.args container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
|
priority: NOTICE
|
|
tags: [network, mitre_discovery]
|
|
|
|
# Change to (always_true) to enable rule 'Network connection outside local subnet'
|
|
- macro: enabled_rule_network_only_subnet
|
|
condition: (never_true)
|
|
|
|
# Namespaces where the rule is enforce
|
|
- list: namespace_scope_network_only_subnet
|
|
items: []
|
|
|
|
- macro: network_local_subnet
|
|
condition: >
|
|
fd.rnet in (rfc_1918_addresses) or
|
|
fd.ip = "0.0.0.0" or
|
|
fd.net = "127.0.0.0/8"
|
|
|
|
# # How to test:
|
|
# # Change macro enabled_rule_network_only_subnet to condition: always_true
|
|
# # Add 'default' to namespace_scope_network_only_subnet
|
|
# # Run:
|
|
# kubectl run --generator=run-pod/v1 -n default -i --tty busybox --image=busybox --rm -- wget google.com -O /var/google.html
|
|
# # Check logs running
|
|
|
|
- rule: Network Connection outside Local Subnet
|
|
desc: Detect traffic to image outside local subnet.
|
|
condition: >
|
|
enabled_rule_network_only_subnet and
|
|
inbound_outbound and
|
|
container and
|
|
not network_local_subnet and
|
|
k8s.ns.name in (namespace_scope_network_only_subnet)
|
|
output: >
|
|
Network connection outside local subnet
|
|
(command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id
|
|
image=%container.image.repository namespace=%k8s.ns.name
|
|
fd.rip.name=%fd.rip.name fd.lip.name=%fd.lip.name fd.cip.name=%fd.cip.name fd.sip.name=%fd.sip.name)
|
|
priority: WARNING
|
|
tags: [network]
|
|
|
|
- macro: allowed_port
|
|
condition: (never_true)
|
|
|
|
- list: allowed_image
|
|
items: [] # add image to monitor, i.e.: bitnami/nginx
|
|
|
|
- list: authorized_server_port
|
|
items: [] # add port to allow, i.e.: 80
|
|
|
|
# # How to test:
|
|
# kubectl run --image=nginx nginx-app --port=80 --env="DOMAIN=cluster"
|
|
# kubectl expose deployment nginx-app --port=80 --name=nginx-http --type=LoadBalancer
|
|
# # On minikube:
|
|
# minikube service nginx-http
|
|
# # On general K8s:
|
|
# kubectl get services
|
|
# kubectl cluster-info
|
|
# # Visit the Nginx service and port, should not fire.
|
|
# # Change rule to different port, then different process name, and test again that it fires.
|
|
|
|
- rule: Outbound or Inbound Traffic not to Authorized Server Process and Port
|
|
desc: Detect traffic that is not to authorized server process and port.
|
|
condition: >
|
|
allowed_port and
|
|
inbound_outbound and
|
|
container and
|
|
container.image.repository in (allowed_image) and
|
|
not proc.name in (authorized_server_binary) and
|
|
not fd.sport in (authorized_server_port)
|
|
output: >
|
|
Network connection outside authorized port and binary
|
|
(command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id
|
|
image=%container.image.repository)
|
|
priority: WARNING
|
|
tags: [network]
|
|
|
|
- macro: user_known_stand_streams_redirect_activities
|
|
condition: (never_true)
|
|
|
|
- rule: Redirect STDOUT/STDIN to Network Connection in Container
|
|
desc: Detect redirecting stdout/stdin to network connection in container (potential reverse shell).
|
|
condition: evt.type=dup and evt.dir=> and container and fd.num in (0, 1, 2) and fd.type in ("ipv4", "ipv6") and not user_known_stand_streams_redirect_activities
|
|
output: >
|
|
Redirect stdout/stdin to network connection (user=%user.name user_loginuid=%user.loginuid %container.info process=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty container_id=%container.id image=%container.image.repository fd.name=%fd.name fd.num=%fd.num fd.type=%fd.type fd.sip=%fd.sip)
|
|
priority: WARNING
|
|
|
|
# The two Container Drift rules below will fire when a new executable is created in a container.
|
|
# There are two ways to create executables - file is created with execution permissions or permissions change of existing file.
|
|
# We will use a new filter, is_open_exec, to find all files creations with execution permission, and will trace all chmods in a container.
|
|
# The use case we are targeting here is an attempt to execute code that was not shipped as part of a container (drift) -
|
|
# an activity that might be malicious or non-compliant.
|
|
# Two things to pay attention to:
|
|
# 1) In most cases, 'docker cp' will not be identified, but the assumption is that if an attacker gained access to the container runtime daemon, they are already privileged
|
|
# 2) Drift rules will be noisy in environments in which containers are built (e.g. docker build)
|
|
# These two rules are not enabled by default. Use `never_true` in macro condition to enable them.
|
|
|
|
- macro: user_known_container_drift_activities
|
|
condition: (always_true)
|
|
|
|
- rule: Container Drift Detected (chmod)
|
|
desc: New executable created in a container due to chmod
|
|
condition: >
|
|
chmod and
|
|
consider_all_chmods and
|
|
container and
|
|
not runc_writing_exec_fifo and
|
|
not runc_writing_var_lib_docker and
|
|
not user_known_container_drift_activities and
|
|
evt.rawres>=0 and
|
|
((evt.arg.mode contains "S_IXUSR") or
|
|
(evt.arg.mode contains "S_IXGRP") or
|
|
(evt.arg.mode contains "S_IXOTH"))
|
|
output: Drift detected (chmod), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type)
|
|
priority: ERROR
|
|
|
|
# ****************************************************************************
|
|
# * "Container Drift Detected (open+create)" requires FALCO_ENGINE_VERSION 6 *
|
|
# ****************************************************************************
|
|
- rule: Container Drift Detected (open+create)
|
|
desc: New executable created in a container due to open+create
|
|
condition: >
|
|
evt.type in (open,openat,openat2,creat) and
|
|
evt.is_open_exec=true and
|
|
container and
|
|
not runc_writing_exec_fifo and
|
|
not runc_writing_var_lib_docker and
|
|
not user_known_container_drift_activities and
|
|
evt.rawres>=0
|
|
output: Drift detected (open+create), new executable created in a container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline filename=%evt.arg.filename name=%evt.arg.name mode=%evt.arg.mode event=%evt.type)
|
|
priority: ERROR
|
|
|
|
- list: c2_server_ip_list
|
|
items: []
|
|
|
|
- rule: Outbound Connection to C2 Servers
|
|
desc: Detect outbound connection to command & control servers
|
|
condition: outbound and fd.sip in (c2_server_ip_list)
|
|
output: Outbound connection to C2 server (command=%proc.cmdline connection=%fd.name user=%user.name user_loginuid=%user.loginuid container_id=%container.id image=%container.image.repository)
|
|
priority: WARNING
|
|
tags: [network]
|
|
|
|
- list: white_listed_modules
|
|
items: []
|
|
|
|
- rule: Linux Kernel Module Injection Detected
|
|
desc: Detect kernel module was injected (from container).
|
|
condition: spawned_process and container and proc.name=insmod and not proc.args in (white_listed_modules)
|
|
output: Linux Kernel Module injection using insmod detected (user=%user.name user_loginuid=%user.loginuid parent_process=%proc.pname module=%proc.args %container.info image=%container.image.repository:%container.image.tag)
|
|
priority: WARNING
|
|
tags: [process]
|
|
|
|
- list: run_as_root_image_list
|
|
items: []
|
|
|
|
- macro: user_known_run_as_root_container
|
|
condition: (container.image.repository in (run_as_root_image_list))
|
|
|
|
# The rule is disabled by default and should be enabled when non-root container policy has been applied.
|
|
# Note the rule will not work as expected when usernamespace is applied, e.g. userns-remap is enabled.
|
|
- rule: Container Run as Root User
|
|
desc: Detected container running as root user
|
|
condition: spawned_process and container and proc.vpid=1 and user.uid=0 and not user_known_run_as_root_container
|
|
enabled: false
|
|
output: Container launched with root user privilege (uid=%user.uid container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
|
priority: INFO
|
|
tags: [container, process]
|
|
|
|
# This rule helps detect CVE-2021-3156:
|
|
# A privilege escalation to root through heap-based buffer overflow
|
|
- rule: Sudo Potential Privilege Escalation
|
|
desc: Privilege escalation vulnerability affecting sudo (<= 1.9.5p2). Executing sudo using sudoedit -s or sudoedit -i command with command-line argument that ends with a single backslash character from an unprivileged user it's possible to elevate the user privileges to root.
|
|
condition: spawned_process and user.uid != 0 and (proc.name=sudoedit or proc.name = sudo) and (proc.args contains -s or proc.args contains -i or proc.args contains --login) and (proc.args contains "\ " or proc.args endswith \)
|
|
output: "Detect Sudo Privilege Escalation Exploit (CVE-2021-3156) (user=%user.name parent=%proc.pname cmdline=%proc.cmdline %container.info)"
|
|
priority: CRITICAL
|
|
tags: [filesystem, mitre_privilege_escalation]
|
|
|
|
- rule: Debugfs Launched in Privileged Container
|
|
desc: Detect file system debugger debugfs launched inside a privileged container which might lead to container escape.
|
|
condition: >
|
|
spawned_process and container
|
|
and container.privileged=true
|
|
and proc.name=debugfs
|
|
output: Debugfs launched started in a privileged container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
|
|
priority: WARNING
|
|
tags: [container, cis, mitre_lateral_movement]
|
|
|
|
- macro: mount_info
|
|
condition: (proc.args="" or proc.args intersects ("-V", "-l", "-h"))
|
|
|
|
- rule: Mount Launched in Privileged Container
|
|
desc: Detect file system mount happened inside a privileged container which might lead to container escape.
|
|
condition: >
|
|
spawned_process and container
|
|
and container.privileged=true
|
|
and proc.name=mount
|
|
and not mount_info
|
|
output: Mount was executed inside a privileged container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
|
|
priority: WARNING
|
|
tags: [container, cis, mitre_lateral_movement]
|
|
|
|
- macro: consider_userfaultfd_activities
|
|
condition: (always_true)
|
|
|
|
- list: user_known_userfaultfd_processes
|
|
items: []
|
|
|
|
- rule: Unprivileged Delegation of Page Faults Handling to a Userspace Process
|
|
desc: Detect a successful unprivileged userfaultfd syscall which might act as an attack primitive to exploit other bugs
|
|
condition: >
|
|
consider_userfaultfd_activities and
|
|
evt.type = userfaultfd and
|
|
user.uid != 0 and
|
|
(evt.rawres >= 0 or evt.res != -1) and
|
|
not proc.name in (user_known_userfaultfd_processes)
|
|
output: An userfaultfd syscall was successfully executed by an unprivileged user (user=%user.name user_loginuid=%user.loginuid process=%proc.name command=%proc.cmdline %container.info image=%container.image.repository:%container.image.tag)
|
|
priority: CRITICAL
|
|
tags: [syscall, mitre_defense_evasion]
|
|
|
|
- list: ingress_remote_file_copy_binaries
|
|
items: [wget]
|
|
|
|
- macro: ingress_remote_file_copy_procs
|
|
condition: (proc.name in (ingress_remote_file_copy_binaries))
|
|
|
|
# Users should overwrite this macro to specify conditions under which a
|
|
# Custom condition for use of ingress remote file copy tool in container
|
|
- macro: user_known_ingress_remote_file_copy_activities
|
|
condition: (never_true)
|
|
|
|
- macro: curl_download
|
|
condition: proc.name = curl and
|
|
(proc.cmdline contains " > " or
|
|
proc.cmdline contains " >> " or
|
|
proc.cmdline contains " | " or
|
|
proc.cmdline contains " -o " or
|
|
proc.cmdline contains " --output " or
|
|
proc.cmdline contains " -O " or
|
|
proc.cmdline contains " --remote-name ")
|
|
|
|
- rule: Launch Ingress Remote File Copy Tools in Container
|
|
desc: Detect ingress remote file copy tools launched in container
|
|
condition: >
|
|
spawned_process and
|
|
container and
|
|
(ingress_remote_file_copy_procs or curl_download) and
|
|
not user_known_ingress_remote_file_copy_activities
|
|
output: >
|
|
Ingress remote file copy tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname
|
|
container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
|
|
priority: NOTICE
|
|
tags: [network, process, mitre_command_and_control]
|
|
|
|
# This rule helps detect CVE-2021-4034:
|
|
# A privilege escalation to root through memory corruption
|
|
- rule: Polkit Local Privilege Escalation Vulnerability (CVE-2021-4034)
|
|
desc: "This rule detects an attempt to exploit a privilege escalation vulnerability in Polkit's pkexec. By running specially crafted code, a local user can leverage this flaw to gain root privileges on a compromised system"
|
|
condition:
|
|
spawned_process and user.uid != 0 and proc.name=pkexec and proc.args = ''
|
|
output:
|
|
"Detect Polkit pkexec Local Privilege Escalation Exploit (CVE-2021-4034) (user=%user.loginname uid=%user.loginuid command=%proc.cmdline args=%proc.args)"
|
|
priority: CRITICAL
|
|
tags: [process, mitre_privilege_escalation]
|
|
|
|
# Application rules have moved to application_rules.yaml. Please look
|
|
# there if you want to enable them by adding to
|
|
# falco_rules.local.yaml.
|
|
k8s_audit_rules.yaml: |
|
|
#
|
|
# Copyright (C) 2019 The Falco Authors.
|
|
#
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
- required_engine_version: 2
|
|
|
|
# Like always_true/always_false, but works with k8s audit events
|
|
- macro: k8s_audit_always_true
|
|
condition: (jevt.rawtime exists)
|
|
|
|
- macro: k8s_audit_never_true
|
|
condition: (jevt.rawtime=0)
|
|
|
|
# Generally only consider audit events once the response has completed
|
|
- list: k8s_audit_stages
|
|
items: ["ResponseComplete"]
|
|
|
|
# Generally exclude users starting with "system:"
|
|
- macro: non_system_user
|
|
condition: (not ka.user.name startswith "system:")
|
|
|
|
# This macro selects the set of Audit Events used by the below rules.
|
|
- macro: kevt
|
|
condition: (jevt.value[/stage] in (k8s_audit_stages))
|
|
|
|
- macro: kevt_started
|
|
condition: (jevt.value[/stage]=ResponseStarted)
|
|
|
|
# If you wish to restrict activity to a specific set of users, override/append to this list.
|
|
# users created by kops are included
|
|
- list: vertical_pod_autoscaler_users
|
|
items: ["vpa-recommender", "vpa-updater"]
|
|
|
|
- list: allowed_k8s_users
|
|
items: [
|
|
"minikube", "minikube-user", "kubelet", "kops", "admin", "kube", "kube-proxy", "kube-apiserver-healthcheck",
|
|
"kubernetes-admin",
|
|
vertical_pod_autoscaler_users,
|
|
cluster-autoscaler,
|
|
"system:addon-manager",
|
|
"cloud-controller-manager",
|
|
"eks:node-manager",
|
|
"system:kube-controller-manager"
|
|
]
|
|
|
|
- rule: Disallowed K8s User
|
|
desc: Detect any k8s operation by users outside of an allowed set of users.
|
|
condition: kevt and non_system_user and not ka.user.name in (allowed_k8s_users)
|
|
output: K8s Operation performed by user not in allowed list of users (user=%ka.user.name target=%ka.target.name/%ka.target.resource verb=%ka.verb uri=%ka.uri resp=%ka.response.code)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
# In a local/user rules file, you could override this macro to
|
|
# explicitly enumerate the container images that you want to run in
|
|
# your environment. In this main falco rules file, there isn't any way
|
|
# to know all the containers that can run, so any container is
|
|
# allowed, by using the always_true macro. In the overridden macro, the condition
|
|
# would look something like (ka.req.pod.containers.image.repository in (my-repo/my-image))
|
|
- macro: allowed_k8s_containers
|
|
condition: (k8s_audit_always_true)
|
|
|
|
- macro: response_successful
|
|
condition: (ka.response.code startswith 2)
|
|
|
|
- macro: kcreate
|
|
condition: ka.verb=create
|
|
|
|
- macro: kmodify
|
|
condition: (ka.verb in (create,update,patch))
|
|
|
|
- macro: kdelete
|
|
condition: ka.verb=delete
|
|
|
|
- macro: pod
|
|
condition: ka.target.resource=pods and not ka.target.subresource exists
|
|
|
|
- macro: pod_subresource
|
|
condition: ka.target.resource=pods and ka.target.subresource exists
|
|
|
|
- macro: deployment
|
|
condition: ka.target.resource=deployments
|
|
|
|
- macro: service
|
|
condition: ka.target.resource=services
|
|
|
|
- macro: configmap
|
|
condition: ka.target.resource=configmaps
|
|
|
|
- macro: namespace
|
|
condition: ka.target.resource=namespaces
|
|
|
|
- macro: serviceaccount
|
|
condition: ka.target.resource=serviceaccounts
|
|
|
|
- macro: clusterrole
|
|
condition: ka.target.resource=clusterroles
|
|
|
|
- macro: clusterrolebinding
|
|
condition: ka.target.resource=clusterrolebindings
|
|
|
|
- macro: role
|
|
condition: ka.target.resource=roles
|
|
|
|
- macro: secret
|
|
condition: ka.target.resource=secrets
|
|
|
|
- macro: health_endpoint
|
|
condition: ka.uri=/healthz
|
|
|
|
- rule: Create Disallowed Pod
|
|
desc: >
|
|
Detect an attempt to start a pod with a container image outside of a list of allowed images.
|
|
condition: kevt and pod and kcreate and not allowed_k8s_containers
|
|
output: Pod started with container not in allowed list (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: Create Privileged Pod
|
|
desc: >
|
|
Detect an attempt to start a pod with a privileged container
|
|
condition: kevt and pod and kcreate and ka.req.pod.containers.privileged intersects (true) and not ka.req.pod.containers.image.repository in (falco_privileged_images)
|
|
output: Pod started with privileged container (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- macro: sensitive_vol_mount
|
|
condition: >
|
|
(ka.req.pod.volumes.hostpath intersects (/proc, /var/run/docker.sock, /, /etc, /root, /var/run/crio/crio.sock, /home/admin, /var/lib/kubelet, /var/lib/kubelet/pki, /etc/kubernetes, /etc/kubernetes/manifests))
|
|
|
|
- rule: Create Sensitive Mount Pod
|
|
desc: >
|
|
Detect an attempt to start a pod with a volume from a sensitive host directory (i.e. /proc).
|
|
Exceptions are made for known trusted images.
|
|
condition: kevt and pod and kcreate and sensitive_vol_mount and not ka.req.pod.containers.image.repository in (falco_sensitive_mount_images)
|
|
output: Pod started with sensitive mount (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image volumes=%jevt.value[/requestObject/spec/volumes])
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
# These container images are allowed to run with hostnetwork=true
|
|
- list: falco_hostnetwork_images
|
|
items: [
|
|
gcr.io/google-containers/prometheus-to-sd,
|
|
gcr.io/projectcalico-org/typha,
|
|
gcr.io/projectcalico-org/node,
|
|
gke.gcr.io/gke-metadata-server,
|
|
gke.gcr.io/kube-proxy,
|
|
gke.gcr.io/netd-amd64,
|
|
k8s.gcr.io/ip-masq-agent-amd64
|
|
k8s.gcr.io/prometheus-to-sd,
|
|
]
|
|
|
|
# Corresponds to K8s CIS Benchmark 1.7.4
|
|
- rule: Create HostNetwork Pod
|
|
desc: Detect an attempt to start a pod using the host network.
|
|
condition: kevt and pod and kcreate and ka.req.pod.host_network intersects (true) and not ka.req.pod.containers.image.repository in (falco_hostnetwork_images)
|
|
output: Pod started using host network (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- macro: user_known_node_port_service
|
|
condition: (k8s_audit_never_true)
|
|
|
|
- rule: Create NodePort Service
|
|
desc: >
|
|
Detect an attempt to start a service with a NodePort service type
|
|
condition: kevt and service and kcreate and ka.req.service.type=NodePort and not user_known_node_port_service
|
|
output: NodePort Service Created (user=%ka.user.name service=%ka.target.name ns=%ka.target.namespace ports=%ka.req.service.ports)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- macro: contains_private_credentials
|
|
condition: >
|
|
(ka.req.configmap.obj contains "aws_access_key_id" or
|
|
ka.req.configmap.obj contains "aws-access-key-id" or
|
|
ka.req.configmap.obj contains "aws_s3_access_key_id" or
|
|
ka.req.configmap.obj contains "aws-s3-access-key-id" or
|
|
ka.req.configmap.obj contains "password" or
|
|
ka.req.configmap.obj contains "passphrase")
|
|
|
|
- rule: Create/Modify Configmap With Private Credentials
|
|
desc: >
|
|
Detect creating/modifying a configmap containing a private credential (aws key, password, etc.)
|
|
condition: kevt and configmap and kmodify and contains_private_credentials
|
|
output: K8s configmap with private credential (user=%ka.user.name verb=%ka.verb configmap=%ka.req.configmap.name config=%ka.req.configmap.obj)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
# Corresponds to K8s CIS Benchmark, 1.1.1.
|
|
- rule: Anonymous Request Allowed
|
|
desc: >
|
|
Detect any request made by the anonymous user that was allowed
|
|
condition: kevt and ka.user.name=system:anonymous and ka.auth.decision="allow" and not health_endpoint
|
|
output: Request by anonymous user allowed (user=%ka.user.name verb=%ka.verb uri=%ka.uri reason=%ka.auth.reason))
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
# Roughly corresponds to K8s CIS Benchmark, 1.1.12. In this case,
|
|
# notifies an attempt to exec/attach to a privileged container.
|
|
|
|
# Ideally, we'd add a more stringent rule that detects attaches/execs
|
|
# to a privileged pod, but that requires the engine for k8s audit
|
|
# events to be stateful, so it could know if a container named in an
|
|
# attach request was created privileged or not. For now, we have a
|
|
# less severe rule that detects attaches/execs to any pod.
|
|
#
|
|
# For the same reason, you can't use things like image names/prefixes,
|
|
# as the event that creates the pod (which has the images) is a
|
|
# separate event than the actual exec/attach to the pod.
|
|
|
|
- macro: user_known_exec_pod_activities
|
|
condition: (k8s_audit_never_true)
|
|
|
|
- rule: Attach/Exec Pod
|
|
desc: >
|
|
Detect any attempt to attach/exec to a pod
|
|
condition: kevt_started and pod_subresource and kcreate and ka.target.subresource in (exec,attach) and not user_known_exec_pod_activities
|
|
output: Attach/Exec to pod (user=%ka.user.name pod=%ka.target.name ns=%ka.target.namespace action=%ka.target.subresource command=%ka.uri.param[command])
|
|
priority: NOTICE
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- macro: user_known_pod_debug_activities
|
|
condition: (k8s_audit_never_true)
|
|
|
|
# Only works when feature gate EphemeralContainers is enabled
|
|
- rule: EphemeralContainers Created
|
|
desc: >
|
|
Detect any ephemeral container created
|
|
condition: kevt and pod_subresource and kmodify and ka.target.subresource in (ephemeralcontainers) and not user_known_pod_debug_activities
|
|
output: Ephemeral container is created in pod (user=%ka.user.name pod=%ka.target.name ns=%ka.target.namespace ephemeral_container_name=%jevt.value[/requestObject/ephemeralContainers/0/name] ephemeral_container_image=%jevt.value[/requestObject/ephemeralContainers/0/image])
|
|
priority: NOTICE
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
# In a local/user rules fie, you can append to this list to add additional allowed namespaces
|
|
- list: allowed_namespaces
|
|
items: [kube-system, kube-public, default]
|
|
|
|
- rule: Create Disallowed Namespace
|
|
desc: Detect any attempt to create a namespace outside of a set of known namespaces
|
|
condition: kevt and namespace and kcreate and not ka.target.name in (allowed_namespaces)
|
|
output: Disallowed namespace created (user=%ka.user.name ns=%ka.target.name)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
# Only defined for backwards compatibility. Use the more specific
|
|
# user_allowed_kube_namespace_image_list instead.
|
|
- list: user_trusted_image_list
|
|
items: []
|
|
|
|
- list: user_allowed_kube_namespace_image_list
|
|
items: [user_trusted_image_list]
|
|
|
|
# Only defined for backwards compatibility. Use the more specific
|
|
# allowed_kube_namespace_image_list instead.
|
|
- list: k8s_image_list
|
|
items: []
|
|
|
|
- list: allowed_kube_namespace_image_list
|
|
items: [
|
|
gcr.io/google-containers/prometheus-to-sd,
|
|
gcr.io/projectcalico-org/node,
|
|
gke.gcr.io/addon-resizer,
|
|
gke.gcr.io/heapster,
|
|
gke.gcr.io/gke-metadata-server,
|
|
k8s.gcr.io/ip-masq-agent-amd64,
|
|
k8s.gcr.io/kube-apiserver,
|
|
gke.gcr.io/kube-proxy,
|
|
gke.gcr.io/netd-amd64,
|
|
k8s.gcr.io/addon-resizer,
|
|
k8s.gcr.io/prometheus-to-sd,
|
|
k8s.gcr.io/k8s-dns-dnsmasq-nanny-amd64,
|
|
k8s.gcr.io/k8s-dns-kube-dns-amd64,
|
|
k8s.gcr.io/k8s-dns-sidecar-amd64,
|
|
k8s.gcr.io/metrics-server-amd64,
|
|
kope/kube-apiserver-healthcheck,
|
|
k8s_image_list
|
|
]
|
|
|
|
- macro: allowed_kube_namespace_pods
|
|
condition: (ka.req.pod.containers.image.repository in (user_allowed_kube_namespace_image_list) or
|
|
ka.req.pod.containers.image.repository in (allowed_kube_namespace_image_list))
|
|
|
|
# Detect any new pod created in the kube-system namespace
|
|
- rule: Pod Created in Kube Namespace
|
|
desc: Detect any attempt to create a pod in the kube-system or kube-public namespaces
|
|
condition: kevt and pod and kcreate and ka.target.namespace in (kube-system, kube-public) and not allowed_kube_namespace_pods
|
|
output: Pod created in kube namespace (user=%ka.user.name pod=%ka.resp.name ns=%ka.target.namespace images=%ka.req.pod.containers.image)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- list: user_known_sa_list
|
|
items: []
|
|
|
|
- list: known_sa_list
|
|
items: [
|
|
coredns,
|
|
coredns-autoscaler,
|
|
cronjob-controller,
|
|
daemon-set-controller,
|
|
deployment-controller,
|
|
disruption-controller,
|
|
endpoint-controller,
|
|
endpointslice-controller,
|
|
endpointslicemirroring-controller,
|
|
generic-garbage-collector,
|
|
horizontal-pod-autoscaler,
|
|
job-controller,
|
|
namespace-controller,
|
|
node-controller,
|
|
persistent-volume-binder,
|
|
pod-garbage-collector,
|
|
pv-protection-controller,
|
|
pvc-protection-controller,
|
|
replicaset-controller,
|
|
resourcequota-controller,
|
|
root-ca-cert-publisher,
|
|
service-account-controller,
|
|
statefulset-controller
|
|
]
|
|
|
|
- macro: trusted_sa
|
|
condition: (ka.target.name in (known_sa_list, user_known_sa_list))
|
|
|
|
# Detect creating a service account in the kube-system/kube-public namespace
|
|
- rule: Service Account Created in Kube Namespace
|
|
desc: Detect any attempt to create a serviceaccount in the kube-system or kube-public namespaces
|
|
condition: kevt and serviceaccount and kcreate and ka.target.namespace in (kube-system, kube-public) and response_successful and not trusted_sa
|
|
output: Service account created in kube namespace (user=%ka.user.name serviceaccount=%ka.target.name ns=%ka.target.namespace)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
# Detect any modify/delete to any ClusterRole starting with
|
|
# "system:". "system:coredns" is excluded as changes are expected in
|
|
# normal operation.
|
|
- rule: System ClusterRole Modified/Deleted
|
|
desc: Detect any attempt to modify/delete a ClusterRole/Role starting with system
|
|
condition: kevt and (role or clusterrole) and (kmodify or kdelete) and (ka.target.name startswith "system:") and
|
|
not ka.target.name in (system:coredns, system:managed-certificate-controller)
|
|
output: System ClusterRole/Role modified or deleted (user=%ka.user.name role=%ka.target.name ns=%ka.target.namespace action=%ka.verb)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
# Detect any attempt to create a ClusterRoleBinding to the cluster-admin user
|
|
# (expand this to any built-in cluster role that does "sensitive" things)
|
|
- rule: Attach to cluster-admin Role
|
|
desc: Detect any attempt to create a ClusterRoleBinding to the cluster-admin user
|
|
condition: kevt and clusterrolebinding and kcreate and ka.req.binding.role=cluster-admin
|
|
output: Cluster Role Binding to cluster-admin role (user=%ka.user.name subject=%ka.req.binding.subjects)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: ClusterRole With Wildcard Created
|
|
desc: Detect any attempt to create a Role/ClusterRole with wildcard resources or verbs
|
|
condition: kevt and (role or clusterrole) and kcreate and (ka.req.role.rules.resources intersects ("*") or ka.req.role.rules.verbs intersects ("*"))
|
|
output: Created Role/ClusterRole with wildcard (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- macro: writable_verbs
|
|
condition: >
|
|
(ka.req.role.rules.verbs intersects (create, update, patch, delete, deletecollection))
|
|
|
|
- rule: ClusterRole With Write Privileges Created
|
|
desc: Detect any attempt to create a Role/ClusterRole that can perform write-related actions
|
|
condition: kevt and (role or clusterrole) and kcreate and writable_verbs
|
|
output: Created Role/ClusterRole with write privileges (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules)
|
|
priority: NOTICE
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: ClusterRole With Pod Exec Created
|
|
desc: Detect any attempt to create a Role/ClusterRole that can exec to pods
|
|
condition: kevt and (role or clusterrole) and kcreate and ka.req.role.rules.resources intersects ("pods/exec")
|
|
output: Created Role/ClusterRole with pod exec privileges (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
# The rules below this point are less discriminatory and generally
|
|
# represent a stream of activity for a cluster. If you wish to disable
|
|
# these events, modify the following macro.
|
|
- macro: consider_activity_events
|
|
condition: (k8s_audit_always_true)
|
|
|
|
- macro: kactivity
|
|
condition: (kevt and consider_activity_events)
|
|
|
|
- rule: K8s Deployment Created
|
|
desc: Detect any attempt to create a deployment
|
|
condition: (kactivity and kcreate and deployment and response_successful)
|
|
output: K8s Deployment Created (user=%ka.user.name deployment=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Deployment Deleted
|
|
desc: Detect any attempt to delete a deployment
|
|
condition: (kactivity and kdelete and deployment and response_successful)
|
|
output: K8s Deployment Deleted (user=%ka.user.name deployment=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Service Created
|
|
desc: Detect any attempt to create a service
|
|
condition: (kactivity and kcreate and service and response_successful)
|
|
output: K8s Service Created (user=%ka.user.name service=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Service Deleted
|
|
desc: Detect any attempt to delete a service
|
|
condition: (kactivity and kdelete and service and response_successful)
|
|
output: K8s Service Deleted (user=%ka.user.name service=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s ConfigMap Created
|
|
desc: Detect any attempt to create a configmap
|
|
condition: (kactivity and kcreate and configmap and response_successful)
|
|
output: K8s ConfigMap Created (user=%ka.user.name configmap=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s ConfigMap Deleted
|
|
desc: Detect any attempt to delete a configmap
|
|
condition: (kactivity and kdelete and configmap and response_successful)
|
|
output: K8s ConfigMap Deleted (user=%ka.user.name configmap=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Namespace Created
|
|
desc: Detect any attempt to create a namespace
|
|
condition: (kactivity and kcreate and namespace and response_successful)
|
|
output: K8s Namespace Created (user=%ka.user.name namespace=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Namespace Deleted
|
|
desc: Detect any attempt to delete a namespace
|
|
condition: (kactivity and non_system_user and kdelete and namespace and response_successful)
|
|
output: K8s Namespace Deleted (user=%ka.user.name namespace=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Serviceaccount Created
|
|
desc: Detect any attempt to create a service account
|
|
condition: (kactivity and kcreate and serviceaccount and response_successful)
|
|
output: K8s Serviceaccount Created (user=%ka.user.name user=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Serviceaccount Deleted
|
|
desc: Detect any attempt to delete a service account
|
|
condition: (kactivity and kdelete and serviceaccount and response_successful)
|
|
output: K8s Serviceaccount Deleted (user=%ka.user.name user=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Role/Clusterrole Created
|
|
desc: Detect any attempt to create a cluster role/role
|
|
condition: (kactivity and kcreate and (clusterrole or role) and response_successful)
|
|
output: K8s Cluster Role Created (user=%ka.user.name role=%ka.target.name rules=%ka.req.role.rules resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Role/Clusterrole Deleted
|
|
desc: Detect any attempt to delete a cluster role/role
|
|
condition: (kactivity and kdelete and (clusterrole or role) and response_successful)
|
|
output: K8s Cluster Role Deleted (user=%ka.user.name role=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Role/Clusterrolebinding Created
|
|
desc: Detect any attempt to create a clusterrolebinding
|
|
condition: (kactivity and kcreate and clusterrolebinding and response_successful)
|
|
output: K8s Cluster Role Binding Created (user=%ka.user.name binding=%ka.target.name subjects=%ka.req.binding.subjects role=%ka.req.binding.role resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Role/Clusterrolebinding Deleted
|
|
desc: Detect any attempt to delete a clusterrolebinding
|
|
condition: (kactivity and kdelete and clusterrolebinding and response_successful)
|
|
output: K8s Cluster Role Binding Deleted (user=%ka.user.name binding=%ka.target.name resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Secret Created
|
|
desc: Detect any attempt to create a secret. Service account tokens are excluded.
|
|
condition: (kactivity and kcreate and secret and ka.target.namespace!=kube-system and non_system_user and response_successful)
|
|
output: K8s Secret Created (user=%ka.user.name secret=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: K8s Secret Deleted
|
|
desc: Detect any attempt to delete a secret Service account tokens are excluded.
|
|
condition: (kactivity and kdelete and secret and ka.target.namespace!=kube-system and non_system_user and response_successful)
|
|
output: K8s Secret Deleted (user=%ka.user.name secret=%ka.target.name ns=%ka.target.namespace resp=%ka.response.code decision=%ka.auth.decision reason=%ka.auth.reason)
|
|
priority: INFO
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
# This rule generally matches all events, and as a result is disabled
|
|
# by default. If you wish to enable these events, modify the
|
|
# following macro.
|
|
# condition: (jevt.rawtime exists)
|
|
- macro: consider_all_events
|
|
condition: (k8s_audit_never_true)
|
|
|
|
- macro: kall
|
|
condition: (kevt and consider_all_events)
|
|
|
|
- rule: All K8s Audit Events
|
|
desc: Match all K8s Audit Events
|
|
condition: kall
|
|
output: K8s Audit Event received (user=%ka.user.name verb=%ka.verb uri=%ka.uri obj=%jevt.obj)
|
|
priority: DEBUG
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
|
|
# This macro disables following rule, change to k8s_audit_never_true to enable it
|
|
- macro: allowed_full_admin_users
|
|
condition: (k8s_audit_always_true)
|
|
|
|
# This list includes some of the default user names for an administrator in several K8s installations
|
|
- list: full_admin_k8s_users
|
|
items: ["admin", "kubernetes-admin", "kubernetes-admin@kubernetes", "kubernetes-admin@cluster.local", "minikube-user"]
|
|
|
|
# This rules detect an operation triggered by an user name that is
|
|
# included in the list of those that are default administrators upon
|
|
# cluster creation. This may signify a permission setting too broader.
|
|
# As we can't check for role of the user on a general ka.* event, this
|
|
# may or may not be an administrator. Customize the full_admin_k8s_users
|
|
# list to your needs, and activate at your discretion.
|
|
|
|
# # How to test:
|
|
# # Execute any kubectl command connected using default cluster user, as:
|
|
# kubectl create namespace rule-test
|
|
|
|
- rule: Full K8s Administrative Access
|
|
desc: Detect any k8s operation by a user name that may be an administrator with full access.
|
|
condition: >
|
|
kevt
|
|
and non_system_user
|
|
and ka.user.name in (full_admin_k8s_users)
|
|
and not allowed_full_admin_users
|
|
output: K8s Operation performed by full admin user (user=%ka.user.name target=%ka.target.name/%ka.target.resource verb=%ka.verb uri=%ka.uri resp=%ka.response.code)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- macro: ingress
|
|
condition: ka.target.resource=ingresses
|
|
|
|
- macro: ingress_tls
|
|
condition: (jevt.value[/requestObject/spec/tls] exists)
|
|
|
|
# # How to test:
|
|
# # Create an ingress.yaml file with content:
|
|
# apiVersion: networking.k8s.io/v1beta1
|
|
# kind: Ingress
|
|
# metadata:
|
|
# name: test-ingress
|
|
# annotations:
|
|
# nginx.ingress.kubernetes.io/rewrite-target: /
|
|
# spec:
|
|
# rules:
|
|
# - http:
|
|
# paths:
|
|
# - path: /testpath
|
|
# backend:
|
|
# serviceName: test
|
|
# servicePort: 80
|
|
# # Execute: kubectl apply -f ingress.yaml
|
|
|
|
- rule: Ingress Object without TLS Certificate Created
|
|
desc: Detect any attempt to create an ingress without TLS certification.
|
|
condition: >
|
|
(kactivity and kcreate and ingress and response_successful and not ingress_tls)
|
|
output: >
|
|
K8s Ingress Without TLS Cert Created (user=%ka.user.name ingress=%ka.target.name
|
|
namespace=%ka.target.namespace)
|
|
source: k8s_audit
|
|
priority: WARNING
|
|
tags: [k8s, network]
|
|
|
|
- macro: node
|
|
condition: ka.target.resource=nodes
|
|
|
|
- macro: allow_all_k8s_nodes
|
|
condition: (k8s_audit_always_true)
|
|
|
|
- list: allowed_k8s_nodes
|
|
items: []
|
|
|
|
# # How to test:
|
|
# # Create a Falco monitored cluster with Kops
|
|
# # Increase the number of minimum nodes with:
|
|
# kops edit ig nodes
|
|
# kops apply --yes
|
|
|
|
- rule: Untrusted Node Successfully Joined the Cluster
|
|
desc: >
|
|
Detect a node successfully joined the cluster outside of the list of allowed nodes.
|
|
condition: >
|
|
kevt and node
|
|
and kcreate
|
|
and response_successful
|
|
and not allow_all_k8s_nodes
|
|
and not ka.target.name in (allowed_k8s_nodes)
|
|
output: Node not in allowed list successfully joined the cluster (user=%ka.user.name node=%ka.target.name)
|
|
priority: ERROR
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
|
|
- rule: Untrusted Node Unsuccessfully Tried to Join the Cluster
|
|
desc: >
|
|
Detect an unsuccessful attempt to join the cluster for a node not in the list of allowed nodes.
|
|
condition: >
|
|
kevt and node
|
|
and kcreate
|
|
and not response_successful
|
|
and not allow_all_k8s_nodes
|
|
and not ka.target.name in (allowed_k8s_nodes)
|
|
output: Node not in allowed list tried unsuccessfully to join the cluster (user=%ka.user.name node=%ka.target.name reason=%ka.response.reason)
|
|
priority: WARNING
|
|
source: k8s_audit
|
|
tags: [k8s]
|
|
---
|
|
# Source: falco/templates/clusterrole.yaml
|
|
kind: ClusterRole
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
metadata:
|
|
name: falcosecurity
|
|
labels:
|
|
app: falcosecurity
|
|
chart: "falco-1.18.5"
|
|
release: "falcosecurity"
|
|
heritage: "Helm"
|
|
rules:
|
|
- apiGroups:
|
|
- extensions
|
|
- ""
|
|
resources:
|
|
- nodes
|
|
- namespaces
|
|
- pods
|
|
- replicationcontrollers
|
|
- replicasets
|
|
- services
|
|
- daemonsets
|
|
- deployments
|
|
- events
|
|
- configmaps
|
|
verbs:
|
|
- get
|
|
- list
|
|
- watch
|
|
- apiGroups:
|
|
- apps
|
|
resources:
|
|
- daemonsets
|
|
- deployments
|
|
- replicasets
|
|
- statefulsets
|
|
verbs:
|
|
- get
|
|
- list
|
|
- watch
|
|
- nonResourceURLs:
|
|
- /healthz
|
|
- /healthz/*
|
|
verbs:
|
|
- get
|
|
---
|
|
# Source: falco/templates/clusterrolebinding.yaml
|
|
kind: ClusterRoleBinding
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
metadata:
|
|
name: falcosecurity
|
|
labels:
|
|
app: falcosecurity
|
|
chart: "falco-1.18.5"
|
|
release: "falcosecurity"
|
|
heritage: "Helm"
|
|
subjects:
|
|
- kind: ServiceAccount
|
|
name: falcosecurity
|
|
namespace: falco
|
|
roleRef:
|
|
kind: ClusterRole
|
|
name: falcosecurity
|
|
apiGroup: rbac.authorization.k8s.io
|
|
---
|
|
# Source: falco/templates/daemonset.yaml
|
|
apiVersion: apps/v1
|
|
kind: DaemonSet
|
|
metadata:
|
|
name: falcosecurity
|
|
namespace: falco
|
|
labels:
|
|
app: falcosecurity
|
|
chart: "falco-1.18.5"
|
|
release: "falcosecurity"
|
|
heritage: "Helm"
|
|
spec:
|
|
selector:
|
|
matchLabels:
|
|
app: falcosecurity
|
|
role: security
|
|
template:
|
|
metadata:
|
|
name: falcosecurity
|
|
labels:
|
|
app: falcosecurity
|
|
role: security
|
|
annotations:
|
|
checksum/config: 91b5d08d170b402d54fd9b40f92c975b658900ed588f29e106b9020a57fba545
|
|
checksum/rules: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b
|
|
checksum/certs: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b
|
|
spec:
|
|
serviceAccountName: falcosecurity
|
|
hostNetwork: true
|
|
dnsPolicy: ClusterFirstWithHostNet
|
|
tolerations:
|
|
- effect: NoSchedule
|
|
key: node-role.kubernetes.io/master
|
|
containers:
|
|
- name: falco
|
|
image: docker.io/falcosecurity/falco:0.31.1
|
|
imagePullPolicy: IfNotPresent
|
|
resources:
|
|
limits:
|
|
cpu: 1000m
|
|
memory: 1024Mi
|
|
requests:
|
|
cpu: 100m
|
|
memory: 512Mi
|
|
securityContext:
|
|
privileged: true
|
|
args:
|
|
- /usr/bin/falco
|
|
- --cri
|
|
- /run/containerd/containerd.sock
|
|
- --cri
|
|
- /run/crio/crio.sock
|
|
- -K
|
|
- /var/run/secrets/kubernetes.io/serviceaccount/token
|
|
- -k
|
|
- https://$(KUBERNETES_SERVICE_HOST)
|
|
- --k8s-node
|
|
- "$(FALCO_K8S_NODE_NAME)"
|
|
- -pk
|
|
env:
|
|
- name: FALCO_K8S_NODE_NAME
|
|
valueFrom:
|
|
fieldRef:
|
|
fieldPath: spec.nodeName
|
|
- name: FALCO_BPF_PROBE
|
|
value:
|
|
livenessProbe:
|
|
initialDelaySeconds: 60
|
|
timeoutSeconds: 5
|
|
periodSeconds: 15
|
|
httpGet:
|
|
path: /healthz
|
|
port: 8765
|
|
readinessProbe:
|
|
initialDelaySeconds: 30
|
|
timeoutSeconds: 5
|
|
periodSeconds: 15
|
|
httpGet:
|
|
path: /healthz
|
|
port: 8765
|
|
volumeMounts:
|
|
- mountPath: /host/var/run/docker.sock
|
|
name: docker-socket
|
|
- mountPath: /host/run/containerd/containerd.sock
|
|
name: containerd-socket
|
|
- mountPath: /host/run/crio/crio.sock
|
|
name: crio-socket
|
|
- mountPath: /host/dev
|
|
name: dev-fs
|
|
readOnly: true
|
|
- mountPath: /host/proc
|
|
name: proc-fs
|
|
readOnly: true
|
|
- mountPath: /host/boot
|
|
name: boot-fs
|
|
readOnly: true
|
|
- mountPath: /host/lib/modules
|
|
name: lib-modules
|
|
- mountPath: /host/usr
|
|
name: usr-fs
|
|
readOnly: true
|
|
- mountPath: /host/etc
|
|
name: etc-fs
|
|
readOnly: true
|
|
- mountPath: /etc/falco
|
|
name: config-volume
|
|
volumes:
|
|
- name: docker-socket
|
|
hostPath:
|
|
path: /var/run/docker.sock
|
|
- name: containerd-socket
|
|
hostPath:
|
|
path: /run/containerd/containerd.sock
|
|
- name: crio-socket
|
|
hostPath:
|
|
path: /run/crio/crio.sock
|
|
- name: dev-fs
|
|
hostPath:
|
|
path: /dev
|
|
- name: proc-fs
|
|
hostPath:
|
|
path: /proc
|
|
- name: boot-fs
|
|
hostPath:
|
|
path: /boot
|
|
- name: lib-modules
|
|
hostPath:
|
|
path: /lib/modules
|
|
- name: usr-fs
|
|
hostPath:
|
|
path: /usr
|
|
- name: etc-fs
|
|
hostPath:
|
|
path: /etc
|
|
- name: config-volume
|
|
configMap:
|
|
name: falcosecurity
|
|
items:
|
|
- key: falco.yaml
|
|
path: falco.yaml
|
|
- key: falco_rules.yaml
|
|
path: falco_rules.yaml
|
|
- key: falco_rules.local.yaml
|
|
path: falco_rules.local.yaml
|
|
- key: application_rules.yaml
|
|
path: rules.available/application_rules.yaml
|
|
- key: aws_cloudtrail_rules.yaml
|
|
path: aws_cloudtrail_rules.yaml
|
|
updateStrategy:
|
|
type: RollingUpdate
|