From b3ae480facc6eaf6ed4b588e665beafa23b7ea3d Mon Sep 17 00:00:00 2001 From: Mark Stemm Date: Mon, 23 May 2016 15:32:12 -0700 Subject: [PATCH] Another round of rule cleanups. Do another round of rule cleanups now that we have a larger set of positive and negative trace files to work with. Outside of this commit, there are now trace files for all the positive rules, a docker-compose startup and teardown, and some trace files from the sysdig cloud staging environment. Also add a script that runs sysdig with a filter that removes all the syscalls not handled by falco as well as a few other high-volume, low-information syscalls. This script was used to create the staging environment trace files. Notable rule changes: - The direction for write_binary_dir/write_etc needs to be exit instead of enter, as the bin_dir clause works on the file descriptor returned by the open/openat call. - Add login as a trusted binary that can read sensitive files (occurs for direct console logins). - sshd can read sensitive files well after startup, so exclude it from the set of binaries that can trigger read_sensitive_file_trusted_after_startup. - limit run_shell_untrusted to non-containers. - Disable the ssh_error_syslog rule for now. With the current restriction on system calls (no read/write/sendto/recvfrom/etc), you won't see the ssh error messages. Nevertheless, add a string to look for to indicate ssh errors and add systemd's true location for the syslog device. - Sshd attemps to setuid even when it's not running as root, so exclude it from the set of binaries to monitor for now. - Let programs that are direct decendants of systemd spawn user management tasks for now. - Temporarily disable the EACCESS rule. This rule is exposing a bug in sysdig in debug mode, https://github.com/draios/sysdig/issues/598. The rule is also pretty noisy so I'll keep it disabled until the sysdig bug is fixed. - The etc_dir and bin_dir macros both have the problem that they match pathnames with /etc/, /bin/, etc in the middle of the path, as sysdig doesn't have a "begins with" comparison. Add notes for that. - Change spawn_process to spawned_process to indicate that it's for the exit side of the execve. Also use it in a few places that were looking for the same conditions without any macro. - Get rid of adduser_binaries and fold any programs not already present into shadowutils_binaries. - Add new groups sysdigcloud_binaries and sysdigcloud_binaries_parent and add them as exceptions for write_etc/write_binary_dir. - Add yum as a package management binary and add it as an exception to write_etc/write_binary_dir. - Change how db_program_spawned_process works. Since all of the useful information is on the exit side of the event, you can't really add a condition based on the process being new. Isntead, have the rule check for a non-database-related program being spawned by a database-related program. - Allow dragent to run shells. - Add sendmail, sendmail-msp as a program that attempts to setuid. - Some of the *_binaries macros that were based on dpkg -L accidentally contained directories in addition to end files. Trim those. - Add systemd-logind as a login_binary. - Add unix_chkpwd as a shadowutils_binary. - Add parentheses around any macros that group items using or. I found this necessary when the macro is used in the middle of a list of and conditions. - Break out system_binaries into a new subset user_mgmt_binaries containing login_, passwd_, and shadowutils_ binaries. That way you don't have to pull in all of system_binaries when looking for sensisitive files or user management activity. - Rename fs-bash to fbash, thinking ahead to its more likely name. --- rules/falco_rules.yaml | 135 +++++++++++++++++++++++---------------- test/utils/run_sysdig.sh | 9 +++ 2 files changed, 89 insertions(+), 55 deletions(-) create mode 100644 test/utils/run_sysdig.sh diff --git a/rules/falco_rules.yaml b/rules/falco_rules.yaml index b0cdb0ab..1d451af9 100644 --- a/rules/falco_rules.yaml +++ b/rules/falco_rules.yaml @@ -38,12 +38,16 @@ - macro: modify condition: rename or remove -- macro: spawn_process - condition: syscall.type = execve and evt.dir=< +- macro: spawned_process + condition: evt.type = execve and evt.dir=< # File categories - macro: terminal_file_fd condition: fd.name=/dev/ptmx or fd.directory=/dev/pts + +# This really should be testing that the directory begins with these +# prefixes but sysdig's filter doesn't have a "starts with" operator +# (yet). - macro: bin_dir condition: fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin) @@ -52,6 +56,8 @@ - macro: bin_dir_rename condition: evt.arg[1] contains /bin/ or evt.arg[1] contains /sbin/ or evt.arg[1] contains /usr/bin/ or evt.arg[1] contains /usr/sbin/ +# This really should be testing that the directory begins with /etc, +# but sysdig's filter doesn't have a "starts with" operator (yet). - macro: etc_dir condition: fd.directory contains /etc @@ -74,25 +80,31 @@ 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) -- macro: adduser_binaries - condition: proc.name in (adduser, deluser, addgroup, delgroup) -- macro: login_binaries - condition: proc.name in (bin, login, su, sbin, nologin, bin, faillog, lastlog, newgrp, sg) -# dpkg -L passwd | grep bin | xargs -L 1 basename | tr "\\n" "," +# 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) + +# 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 (sbin, shadowconfig, sbin, grpck, pwunconv, grpconv, pwck, + proc.name in (shadowconfig, grpck, pwunconv, grpconv, pwck, groupmod, vipw, pwconv, useradd, newusers, cppw, chpasswd, usermod, - groupadd, groupdel, grpunconv, chgpasswd, userdel, bin, chage, chsh, + groupadd, groupdel, grpunconv, chgpasswd, userdel, chage, chsh, gpasswd, chfn, expiry, passwd, vigr, cpgr) -# repoquery -l shadow-utils | grep bin | xargs -L 1 basename | tr "\\n" "," +# 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, chpasswd, - groupadd, groupdel, groupmems, groupmod, grpck, grpconv, grpunconv, - newusers, pwck, pwconv, pwunconv, useradd, userdel, usermod, vigr, vipw) + proc.name in (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) + +- macro: sysdigcloud_binaries + condition: proc.name in (setup-backend, dragent) + +- macro: sysdigcloud_binaries_parent + condition: proc.pname in (setup-backend, dragent) - macro: docker_binaries condition: proc.name in (docker, exe) @@ -103,25 +115,33 @@ - macro: db_server_binaries condition: proc.name in (mysqld) -- macro: server_binaries - condition: http_server_binaries or db_server_binaries or docker_binaries or proc.name in (sshd) +- 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)) + +# The truncated dpkg-preconfigu is intentional, process names are +# truncated at the sysdig level. - macro: package_mgmt_binaries - condition: proc.name in (dpkg, rpm) + condition: proc.name in (dpkg, dpkg-preconfigu, rpm, yum) # 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) +- macro: user_mgmt_binaries + condition: (login_binaries or passwd_binaries or shadowutils_binaries) + - macro: system_binaries - condition: coreutils_binaries or adduser_binaries or login_binaries or passwd_binaries or shadowutils_binaries + condition: (coreutils_binaries or user_mgmt_binaries) - macro: mail_binaries - condition: proc.name in (sendmail, postfix, procmail) + condition: proc.name in (sendmail, sendmail-msp, postfix, procmail) - macro: sensitive_files - condition: fd.name contains /etc/shadow or fd.name = /etc/sudoers or fd.directory = /etc/sudoers.d or fd.directory = /etc/pam.d or fd.name = /etc/pam.conf + condition: (fd.name contains /etc/shadow or fd.name = /etc/sudoers or fd.directory = /etc/sudoers.d or fd.directory = /etc/pam.d or fd.name = /etc/pam.conf) # Indicates that the process is new. Currently detected using time # since process was started, using a threshold of 5 seconds. @@ -130,7 +150,7 @@ # Network - macro: inbound - condition: (syscall.type=listen and evt.dir=>) or (syscall.type=accept and evt.dir=<) + condition: ((syscall.type=listen and evt.dir=>) or (syscall.type=accept and evt.dir=<)) # Currently sendto is an ignored syscall, otherwise this could also check for (syscall.type=sendto and evt.dir=>) - macro: outbound @@ -141,7 +161,7 @@ # Ssh - macro: ssh_error_message - condition: evt.arg.data contains "Invalid user" or evt.arg.data contains "preauth" + condition: (evt.arg.data contains "Invalid user" or evt.arg.data contains "preauth" or evt.arg.data contains "Failed password") # System - macro: modules @@ -149,9 +169,9 @@ - macro: container condition: container.id != host - macro: interactive - condition: (proc.aname=sshd and proc.name != sshd) or proc.name=systemd-logind + condition: ((proc.aname=sshd and proc.name != sshd) or proc.name=systemd-logind) - macro: syslog - condition: fd.name = /dev/log + condition: fd.name in (/dev/log, /run/systemd/journal/syslog) - macro: cron condition: proc.name in (cron, crond) - macro: parent_cron @@ -169,32 +189,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 bin_dir + condition: evt.dir = < and open_write and not package_mgmt_binaries 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 - condition: evt.dir = > and open_write and etc_dir + condition: evt.dir = < and open_write and not shadowutils_binaries and not sysdigcloud_binaries_parent and not package_mgmt_binaries and etc_dir output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)" priority: WARNING - 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 server_binaries and not userexec_binaries and not proc.name in (iptables, ps, systemd-logind, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, bash) and not cron and sensitive_files + 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 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 + condition: open_read and server_binaries 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 -- rule: db_program_spawn_process - desc: a database-server related program spawning a new process after startup. This shouldn\'t occur and is a follow on from some SQL injection attacks. - condition: db_server_binaries and not proc_is_new and spawn_process - output: "Database-related program spawned new process after startup (user=%user.name command=%proc.cmdline)" +- 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 + output: "Database-related program spawned process other than itself (user=%user.name program=%proc.cmdline parent=%proc.pname)" priority: WARNING - rule: modify_binary_dirs @@ -218,11 +238,12 @@ # output: "Loaded .so from unexpected dir (%user.name %proc.name %evt.dir %evt.type %evt.args %fd.name)" # priority: WARNING -- rule: syscall_returns_eaccess - desc: any system call that returns EACCESS. This is not always a strong indication of a problem, hence the INFO priority. - condition: evt.res = EACCESS - output: "System call returned EACCESS (user=%user.name command=%proc.cmdline syscall=%evt.type args=%evt.args)" - priority: INFO +# Temporarily disabling this rule as it's tripping over https://github.com/draios/sysdig/issues/598 +# - rule: syscall_returns_eaccess +# desc: any system call that returns EACCESS. This is not always a strong indication of a problem, hence the INFO priority. +# condition: evt.res = EACCESS +# output: "System call returned EACCESS (user=%user.name command=%proc.cmdline syscall=%evt.type args=%evt.args)" +# priority: INFO - 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. @@ -232,7 +253,7 @@ - rule: run_shell_untrusted desc: an attempt to spawn a shell by a non-shell program. Exceptions are made for trusted binaries. - condition: proc.name = bash and evt.dir=< and evt.type=execve and proc.pname exists and not parent_cron and not proc.pname in (bash, sshd, sudo, docker, su, tmux, screen, emacs, systemd, flock, fs-bash, nginx, monit, supervisord) + 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) output: "Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)" priority: WARNING @@ -243,13 +264,13 @@ - rule: system_user_interactive desc: an attempt to run interactive commands by a system (i.e. non-login) user - condition: spawn_process and system_users and interactive + condition: spawned_process and system_users and interactive output: "System user ran an interactive command (user=%user.name command=%proc.cmdline)" priority: WARNING - rule: run_shell_in_container - desc: an attempt to spawn a shell by a non-shell program in a container. Container entrypoints are excluded. - condition: container and proc.name = bash and evt.dir=< and evt.type=execve and proc.pname exists and not proc.pname in (bash, docker) + desc: a shell was spawned by a non-shell program in a container. Container entrypoints are excluded. + condition: container and proc.name = bash and spawned_process and proc.pname exists and not proc.pname in (bash, docker) output: "Shell spawned in a container other than entrypoint (user=%user.name container_id=%container.id container_name=%container.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)" priority: WARNING @@ -260,22 +281,26 @@ output: "Known system binary sent/received network traffic (user=%user.name command=%proc.cmdline connection=%fd.name)" priority: WARNING -- rule: ssh_error_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 +# 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_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 +# 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 + 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) 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: spawn_process and not proc.name in (su, sudo) and not container and (adduser_binaries or login_binaries or passwd_binaries or shadowutils_binaries) - output: "User management binary command run outside of container (user=%user.name command=%proc.cmdline)" + 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) + output: "User management binary command run outside of container (user=%user.name command=%proc.cmdline parent=%proc.pname)" priority: WARNING # (we may need to add additional checks against false positives, see: https://bugs.launchpad.net/ubuntu/+source/rkhunter/+bug/86153) @@ -285,17 +310,17 @@ output: "File created below /dev by untrusted program (user=%user.name command=%proc.cmdline file=%fd.name)" priority: WARNING -# fs-bash is a restricted version of bash suitable for use in curl | sh installers. +# fbash is a small shell script that runs bash, and is suitable for use in curl | fbash installers. - rule: installer_bash_starts_network_server - desc: an attempt by any program that is a child of fs-bash to start listening for network connections - condition: evt.type=listen and proc.aname=fs-bash - output: "Unexpected listen call by a child process of fs-bash (command=%proc.cmdline)" + desc: an attempt by any program that is a child of fbash to start listening for network connections + condition: evt.type=listen and proc.aname=fbash + output: "Unexpected listen call by a child process of fbash (command=%proc.cmdline)" priority: WARNING - rule: installer_bash_starts_session - desc: an attempt by any program that is a child of fs-bash to start a new session (process group) - condition: evt.type=setsid and proc.aname=fs-bash - output: "Unexpected setsid call by a child process of fs-bash (command=%proc.cmdline)" + desc: an attempt by any program that is a child of fbash to start a new session (process group) + condition: evt.type=setsid and proc.aname=fbash + output: "Unexpected setsid call by a child process of fbash (command=%proc.cmdline)" priority: WARNING ########################### diff --git a/test/utils/run_sysdig.sh b/test/utils/run_sysdig.sh new file mode 100644 index 00000000..9a1a611f --- /dev/null +++ b/test/utils/run_sysdig.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Run sysdig excluding all events that aren't used by falco and also +# excluding other high-volume events that aren't essential. This +# results in smaller trace files. + +# The remaining arguments are taken from the command line. + +exec sudo sysdig not evt.type in '(mprotect,brk,mq_timedreceive,mq_receive,mq_timedsend,mq_send,getrusage,procinfo,rt_sigprocmask,rt_sigaction,ioctl,clock_getres,clock_gettime,clock_nanosleep,clock_settime,close,epoll_create,epoll_create1,epoll_ctl,epoll_pwait,epoll_wait,eventfd,fcntl,fcntl64,fstat,fstat64,fstatat64,fstatfs,fstatfs64,futex,getitimer,gettimeofday,ioprio_get,ioprio_set,llseek,lseek,lstat,lstat64,mmap,mmap2,munmap,nanosleep,poll,ppoll,pread,pread64,preadv,procinfo,pselect6,pwrite,pwrite64,pwritev,read,readv,recv,recvfrom,recvmmsg,recvmsg,sched_yield,select,send,sendfile,sendfile64,sendmmsg,sendmsg,sendto,setitimer,settimeofday,shutdown,splice,stat,stat64,statfs,statfs64,switch,tee,timer_create,timer_delete,timerfd_create,timerfd_gettime,timerfd_settime,timer_getoverrun,timer_gettime,timer_settime,wait4,write,writev) and user.name!=ec2-user' $@