mirror of
https://github.com/falcosecurity/falco.git
synced 2025-08-01 14:37:49 +00:00
Add list support to rules file.
Once sysdig adds support for handling "in (...)" filter expressions as set membership tests, it will be advantageous to combine lists of items together into a single list so they can all be checked in a single set membership test. This commit adds support for a new yaml item type "list" containing a field "name" and field "items" containing a list of items. These are represented as a yaml list, which allows yaml to handle some of the initial parsing with the list items maintained natively in lua. Allow lists to contain list references by expanding any references to the items in the list, before storing the list items in state.lists. When parsing macro or rule conditions, replace all references to a list name with the list items as a comma separated string. Modify the falco rules to switch to lists whenever possible. The new convention is to use the suffix _binaries for lists of program names and _procs for macros that define a filter expression using the list.
This commit is contained in:
parent
d16bb8fd2c
commit
502941b804
@ -68,76 +68,76 @@
|
||||
- macro: linux_so_dirs
|
||||
condition: ubuntu_so_dirs or centos_so_dirs or fd.name=/etc/ld.so.cache
|
||||
|
||||
- macro: coreutils_binaries
|
||||
condition: >
|
||||
proc.name in (truncate, sha1sum, numfmt, fmt, fold, uniq, cut, who,
|
||||
- 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,
|
||||
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)
|
||||
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" ","
|
||||
- macro: login_binaries
|
||||
condition: proc.name in (login, systemd-logind, su, nologin, faillog, lastlog, newgrp, sg)
|
||||
- list: login_binaries
|
||||
items: [login, 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" ","
|
||||
- macro: passwd_binaries
|
||||
condition: >
|
||||
proc.name in (shadowconfig, grpck, pwunconv, grpconv, pwck,
|
||||
- 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)
|
||||
gpasswd, chfn, expiry, passwd, vigr, cpgr
|
||||
]
|
||||
|
||||
# repoquery -l shadow-utils | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
|
||||
- macro: shadowutils_binaries
|
||||
condition: >
|
||||
proc.name in (chage, gpasswd, lastlog, newgrp, sg, adduser, deluser, chpasswd,
|
||||
- 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)
|
||||
newusers, pwck, pwconv, pwunconv, useradd, userdel, usermod, vigr, vipw, unix_chkpwd
|
||||
]
|
||||
|
||||
- macro: sysdigcloud_binaries
|
||||
condition: proc.name in (setup-backend, dragent)
|
||||
- list: sysdigcloud_binaries
|
||||
items: [setup-backend, dragent]
|
||||
|
||||
- macro: sysdigcloud_binaries_parent
|
||||
condition: proc.pname in (setup-backend, dragent)
|
||||
- list: docker_binaries
|
||||
items: [docker, exe]
|
||||
|
||||
- macro: docker_binaries
|
||||
condition: proc.name in (docker, exe)
|
||||
- list: http_server_binaries
|
||||
items: [nginx, httpd, httpd-foregroun, lighttpd]
|
||||
|
||||
- macro: http_server_binaries
|
||||
condition: proc.name in (nginx, httpd, httpd-foregroun, lighttpd)
|
||||
- list: db_server_binaries
|
||||
items: [mysqld]
|
||||
|
||||
- macro: db_server_binaries
|
||||
condition: proc.name in (mysqld)
|
||||
|
||||
- macro: db_server_binaries_parent
|
||||
condition: proc.pname in (mysqld)
|
||||
|
||||
- macro: server_binaries
|
||||
condition: (http_server_binaries or db_server_binaries or docker_binaries or proc.name in (sshd))
|
||||
- macro: server_procs
|
||||
condition: proc.name in (http_server_binaries, db_server_binaries, docker_binaries, sshd)
|
||||
|
||||
# The truncated dpkg-preconfigu is intentional, process names are
|
||||
# truncated at the sysdig level.
|
||||
- macro: package_mgmt_binaries
|
||||
condition: proc.name in (dpkg, dpkg-preconfigu, rpm, rpmkey, yum)
|
||||
- list: package_mgmt_binaries
|
||||
items: [dpkg, dpkg-preconfigu, rpm, rpmkey, yum]
|
||||
|
||||
- macro: package_mgmt_procs
|
||||
condition: proc.name in (package_mgmt_binaries)
|
||||
|
||||
# A canonical set of processes that run other programs with different
|
||||
# privileges or as a different user.
|
||||
- macro: userexec_binaries
|
||||
condition: proc.name in (sudo, su)
|
||||
- list: userexec_binaries
|
||||
items: [sudo, su]
|
||||
|
||||
- macro: user_mgmt_binaries
|
||||
condition: (login_binaries or passwd_binaries or shadowutils_binaries)
|
||||
- list: user_mgmt_binaries
|
||||
items: [login_binaries, passwd_binaries, shadowutils_binaries]
|
||||
|
||||
- macro: system_binaries
|
||||
condition: (coreutils_binaries or user_mgmt_binaries)
|
||||
- macro: system_procs
|
||||
condition: proc.name in (coreutils_binaries, user_mgmt_binaries)
|
||||
|
||||
- macro: mail_binaries
|
||||
- macro: mail_procs
|
||||
condition: proc.name in (sendmail, sendmail-msp, postfix, procmail)
|
||||
|
||||
- macro: sensitive_files
|
||||
@ -172,10 +172,8 @@
|
||||
condition: ((proc.aname=sshd and proc.name != sshd) or proc.name=systemd-logind)
|
||||
- macro: syslog
|
||||
condition: fd.name in (/dev/log, /run/systemd/journal/syslog)
|
||||
- macro: cron
|
||||
condition: proc.name in (cron, crond)
|
||||
- macro: parent_cron
|
||||
condition: proc.pname in (cron, crond)
|
||||
- list: cron_binaries
|
||||
items: [cron, crond]
|
||||
|
||||
# System users that should never log into a system. Consider adding your own
|
||||
# service users (e.g. 'apache' or 'mysqld') here.
|
||||
@ -189,32 +187,32 @@
|
||||
|
||||
- rule: write_binary_dir
|
||||
desc: an attempt to write to any file below a set of binary directories
|
||||
condition: evt.dir = < and open_write and not package_mgmt_binaries and bin_dir
|
||||
condition: evt.dir = < and open_write and not package_mgmt_procs and bin_dir
|
||||
output: "File below a known binary directory opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
|
||||
- rule: write_etc
|
||||
desc: an attempt to write to any file below /etc, not in a pipe installer session
|
||||
condition: evt.dir = < and open_write and not shadowutils_binaries and not sysdigcloud_binaries_parent and not package_mgmt_binaries and etc_dir and not proc.sname=fbash
|
||||
condition: evt.dir = < and open_write and not proc.name in (shadowutils_binaries, sysdigcloud_binaries, package_mgmt_binaries) and etc_dir and not proc.pname in (sysdigcloud_binaries) and not proc.sname=fbash
|
||||
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
|
||||
# Within a fbash session, the severity is lowered to INFO
|
||||
- rule: write_etc_installer
|
||||
desc: an attempt to write to any file below /etc, in a pipe installer session
|
||||
condition: evt.dir = < and open_write and not shadowutils_binaries and not sysdigcloud_binaries_parent and not package_mgmt_binaries and etc_dir and proc.sname=fbash
|
||||
condition: evt.dir = < and open_write and not proc.name in (shadowutils_binaries, sysdigcloud_binaries, package_mgmt_binaries) and etc_dir and not proc.pname in (sysdigcloud_binaries) and proc.sname=fbash
|
||||
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name) within pipe installer session"
|
||||
priority: INFO
|
||||
|
||||
- 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: open_read and not user_mgmt_binaries and not userexec_binaries and not proc.name in (iptables, ps, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, bash, sshd) and not cron and sensitive_files
|
||||
condition: open_read and not proc.name in (user_mgmt_binaries, userexec_binaries, cron_binaries, iptables, ps, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, bash, sshd) and sensitive_files
|
||||
output: "Sensitive file opened for reading by non-trusted program (user=%user.name command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
|
||||
- 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: open_read and server_binaries and not proc_is_new and sensitive_files and proc.name!="sshd"
|
||||
condition: open_read and server_procs and not proc_is_new and sensitive_files and proc.name!="sshd"
|
||||
output: "Sensitive file opened for reading by trusted program after startup (user=%user.name command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
|
||||
@ -227,19 +225,19 @@
|
||||
|
||||
- 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: db_server_binaries_parent and not db_server_binaries and spawned_process
|
||||
condition: proc.pname in (db_server_binaries) and not proc.name in (db_server_binaries) and spawned_process
|
||||
output: "Database-related program spawned process other than itself (user=%user.name program=%proc.cmdline parent=%proc.pname)"
|
||||
priority: WARNING
|
||||
|
||||
- rule: modify_binary_dirs
|
||||
desc: an attempt to modify any file below a set of binary directories.
|
||||
condition: modify and bin_dir_rename and not package_mgmt_binaries
|
||||
condition: modify and bin_dir_rename and not package_mgmt_procs
|
||||
output: "File below known binary directory renamed/removed (user=%user.name command=%proc.cmdline operation=%evt.type file=%fd.name %evt.args)"
|
||||
priority: WARNING
|
||||
|
||||
- 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_binaries
|
||||
condition: mkdir and bin_dir_mkdir and not package_mgmt_procs
|
||||
output: "Directory below known binary directory created (user=%user.name command=%proc.cmdline directory=%evt.arg.path)"
|
||||
priority: WARNING
|
||||
|
||||
@ -267,7 +265,7 @@
|
||||
|
||||
- rule: run_shell_untrusted
|
||||
desc: an attempt to spawn a shell by a non-shell program. Exceptions are made for trusted binaries.
|
||||
condition: not container and proc.name = bash and spawned_process and proc.pname exists and not parent_cron and not proc.pname in (bash, sshd, sudo, docker, su, tmux, screen, emacs, systemd, login, flock, fbash, nginx, monit, supervisord, dragent)
|
||||
condition: not container and proc.name = bash and spawned_process and proc.pname exists and not proc.pname in (cron_binaries, bash, sshd, sudo, docker, su, tmux, screen, emacs, systemd, login, flock, fbash, nginx, monit, supervisord, dragent)
|
||||
output: "Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)"
|
||||
priority: WARNING
|
||||
|
||||
@ -289,9 +287,9 @@
|
||||
priority: WARNING
|
||||
|
||||
# sockfamily ip is to exclude certain processes (like 'groups') that communicate on unix-domain sockets
|
||||
- rule: system_binaries_network_activity
|
||||
- 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: (inbound or outbound) and (fd.sockfamily = ip and system_binaries)
|
||||
condition: (inbound or outbound) and (fd.sockfamily = ip and system_procs)
|
||||
output: "Known system binary sent/received network traffic (user=%user.name command=%proc.cmdline connection=%fd.name)"
|
||||
priority: WARNING
|
||||
|
||||
@ -307,13 +305,13 @@
|
||||
# sshd, sendmail-msp, sendmail 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. user "root" is also excluded, as setuid calls typically involve dropping privileges.
|
||||
condition: evt.type=setuid and evt.dir=> and not user.name=root and not userexec_binaries and not proc.name in (sshd, sendmail-msp, sendmail)
|
||||
condition: evt.type=setuid and evt.dir=> and not user.name=root and not proc.name in (userexec_binaries, sshd, sendmail-msp, sendmail)
|
||||
output: "Unexpected setuid call by non-sudo, non-root program (user=%user.name command=%proc.cmdline uid=%evt.arg.uid)"
|
||||
priority: WARNING
|
||||
|
||||
- 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.
|
||||
condition: spawned_process and not proc.name in (su, sudo) and not container and user_mgmt_binaries and not parent_cron and not proc.pname in (systemd, run-parts)
|
||||
condition: spawned_process and not proc.name in (su, sudo) and not container and proc.name in (user_mgmt_binaries) and not proc.pname in (cron_binaries, systemd, run-parts)
|
||||
output: "User management binary command run outside of container (user=%user.name command=%proc.cmdline parent=%proc.pname)"
|
||||
priority: WARNING
|
||||
|
||||
@ -361,7 +359,7 @@
|
||||
# as a part of doing the installation
|
||||
- rule: installer_bash_runs_pkgmgmt
|
||||
desc: an attempt by a program in a pipe installer session to run a package management binary
|
||||
condition: evt.type=execve and package_mgmt_binaries and proc.sname=fbash
|
||||
condition: evt.type=execve and package_mgmt_procs and proc.sname=fbash
|
||||
output: "Package management program run by process in a fbash session (command=%proc.cmdline)"
|
||||
priority: INFO
|
||||
|
||||
@ -530,6 +528,6 @@
|
||||
|
||||
# - rule: http_server_unexpected_network_inbound
|
||||
# desc: inbound network traffic to a http server program on a port other than the standard ports
|
||||
# condition: http_server_binaries and inbound and fd.sport != 80 and fd.sport != 443
|
||||
# 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
|
||||
|
@ -156,7 +156,12 @@ function check_for_ignored_syscalls_events(ast, filter_type, source)
|
||||
parser.traverse_ast(ast, "BinaryRelOp", cb)
|
||||
end
|
||||
|
||||
function compiler.compile_macro(line)
|
||||
function compiler.compile_macro(line, list_defs)
|
||||
|
||||
for name, items in pairs(list_defs) do
|
||||
line = string.gsub(line, name, table.concat(items, ", "))
|
||||
end
|
||||
|
||||
local ast, error_msg = parser.parse_filter(line)
|
||||
|
||||
if (error_msg) then
|
||||
@ -174,7 +179,12 @@ end
|
||||
--[[
|
||||
Parses a single filter, then expands macros using passed-in table of definitions. Returns resulting AST.
|
||||
--]]
|
||||
function compiler.compile_filter(source, macro_defs)
|
||||
function compiler.compile_filter(source, macro_defs, list_defs)
|
||||
|
||||
for name, items in pairs(list_defs) do
|
||||
source = string.gsub(source, name, table.concat(items, ", "))
|
||||
end
|
||||
|
||||
local ast, error_msg = parser.parse_filter(source)
|
||||
|
||||
if (error_msg) then
|
||||
|
@ -115,7 +115,7 @@ end
|
||||
-- object. The by_name index is used for things like describing rules,
|
||||
-- and the by_idx index is used to map the relational node index back
|
||||
-- to a rule.
|
||||
local state = {macros={}, filter_ast=nil, rules_by_name={}, n_rules=0, rules_by_idx={}}
|
||||
local state = {macros={}, lists={}, filter_ast=nil, rules_by_name={}, n_rules=0, rules_by_idx={}}
|
||||
|
||||
function load_rules(filename)
|
||||
|
||||
@ -131,9 +131,28 @@ function load_rules(filename)
|
||||
end
|
||||
|
||||
if (v['macro']) then
|
||||
local ast = compiler.compile_macro(v['condition'])
|
||||
local ast = compiler.compile_macro(v['condition'], state.lists)
|
||||
state.macros[v['macro']] = ast.filter.value
|
||||
|
||||
elseif (v['list']) then
|
||||
-- list items are represented in yaml as a native list, so no
|
||||
-- parsing necessary
|
||||
local items = {}
|
||||
|
||||
-- List items may be references to other lists, so go through
|
||||
-- the items and expand any references to the items in the list
|
||||
for i, item in ipairs(v['items']) do
|
||||
if (state.lists[item] == nil) then
|
||||
items[#items+1] = item
|
||||
else
|
||||
for i, exp_item in ipairs(state.lists[item]) do
|
||||
items[#items+1] = exp_item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
state.lists[v['list']] = items
|
||||
|
||||
else -- rule
|
||||
|
||||
if (v['rule'] == nil) then
|
||||
@ -150,7 +169,7 @@ function load_rules(filename)
|
||||
v['level'] = priority(v['priority'])
|
||||
state.rules_by_name[v['rule']] = v
|
||||
|
||||
local filter_ast = compiler.compile_filter(v['condition'], state.macros)
|
||||
local filter_ast = compiler.compile_filter(v['condition'], state.macros, state.lists)
|
||||
|
||||
if (filter_ast.type == "Rule") then
|
||||
state.n_rules = state.n_rules + 1
|
||||
|
Loading…
Reference in New Issue
Block a user