From edce729bd96f4fdca4a3351660ebf8470672aa21 Mon Sep 17 00:00:00 2001 From: Mark Stemm Date: Wed, 24 May 2017 18:54:14 -0700 Subject: [PATCH 1/2] Use a wider range of priorities in rules. Review the priorities used by each rule and try to use a consistent set that uses more of the possible priorities. The general guidelines I used were: - If a rule is related to a write of state (i.e. filesystem, etc.), its priority is ERROR. - If a rule is related to an unauthorized read of state (i.e. reading sensitive filees, etc.), its priority is WARNING. - If a rule is related to unexpected behavior (spawning an unexpected shell in a container, opening an unexpected network connection, etc.), its priority is NOTICE. - If a rule is related to behaving against good practices (unexpected privileged containers, containers with sensitive mounts, running interactive commands as root), its priority is INFO. One exception is that the most FP-prone rule (Run shell untrusted) has a priority of DEBUG. --- rules/falco_rules.yaml | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/rules/falco_rules.yaml b/rules/falco_rules.yaml index c393f240..dbdf37f8 100644 --- a/rules/falco_rules.yaml +++ b/rules/falco_rules.yaml @@ -252,7 +252,7 @@ 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 output: "File below a known binary directory opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)" - priority: WARNING + priority: ERROR tags: [filesystem] - macro: write_etc_common @@ -272,7 +272,7 @@ desc: an attempt to write to any file below /etc, not in a pipe installer session condition: write_etc_common and not proc.sname=fbash output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)" - priority: WARNING + priority: ERROR tags: [filesystem] # Within a fbash session, the severity is lowered to INFO @@ -313,28 +313,28 @@ 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 proc.name in (dnf,rpm,rpmkey,yum) and not ansible_running_python output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline file=%fd.name)" - priority: WARNING + priority: ERROR tags: [filesystem, software_mgmt] - 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) output: "Database-related program spawned process other than itself (user=%user.name program=%proc.cmdline parent=%proc.pname)" - priority: WARNING + priority: NOTICE tags: [process, database] - 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 output: "File below known binary directory renamed/removed (user=%user.name command=%proc.cmdline operation=%evt.type file=%fd.name %evt.args)" - priority: WARNING + priority: ERROR tags: [filesystem] - 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 output: "Directory below known binary directory created (user=%user.name command=%proc.cmdline directory=%evt.arg.path)" - priority: WARNING + priority: ERROR tags: [filesystem] # Don't load shared objects coming from unexpected places @@ -362,7 +362,7 @@ and not proc.pname in (sysdigcloud_binaries) and not java_running_sdjagent output: "Namespace change (setns) by unexpected program (user=%user.name command=%proc.cmdline parent=%proc.pname %container.info)" - priority: WARNING + priority: NOTICE tags: [process] - list: known_shell_spawn_binaries @@ -390,7 +390,7 @@ and not parent_python_running_denyhosts and not parent_linux_image_upgrade_script output: "Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline pcmdline=%proc.pcmdline)" - priority: WARNING + priority: DEBUG tags: [host, shell] - macro: trusted_containers @@ -406,7 +406,7 @@ desc: Any open by a privileged container. Exceptions are made for known trusted images. condition: (open_read or open_write) and container and container.privileged=true and not trusted_containers output: File opened for read/write by privileged container (user=%user.name command=%proc.cmdline %container.info file=%fd.name) - priority: WARNING + priority: INFO tags: [container, cis] - macro: sensitive_mount @@ -416,7 +416,7 @@ desc: Any open by a container that has a mount from a sensitive host directory (i.e. /proc). Exceptions are made for known trusted images. condition: (open_read or open_write) and container and sensitive_mount and not trusted_containers output: File opened for read/write by container mounting sensitive directory (user=%user.name command=%proc.cmdline %container.info file=%fd.name) - priority: WARNING + priority: INFO tags: [container, cis] # Anything run interactively by root @@ -428,7 +428,7 @@ desc: an attempt to run interactive commands by a system (i.e. non-login) user condition: spawned_process and system_users and interactive output: "System user ran an interactive command (user=%user.name command=%proc.cmdline)" - priority: WARNING + priority: INFO tags: [users] - rule: Terminal shell in container @@ -437,7 +437,7 @@ spawned_process and container and shell_procs and proc.tty != 0 output: "A shell was spawned in a container with an attached terminal (user=%user.name %container.info shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty)" - priority: WARNING + priority: NOTICE tags: [container, shell] - rule: Run shell in container @@ -450,7 +450,7 @@ monitoring_binaries, gitlab_binaries, initdb, pg_ctl, awk, falco, cron, erl_child_setup) and not trusted_containers output: "Shell spawned in a container other than entrypoint (user=%user.name %container.info shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)" - priority: WARNING + priority: NOTICE tags: [container, shell] # sockfamily ip is to exclude certain processes (like 'groups') that communicate on unix-domain sockets @@ -458,7 +458,7 @@ 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) and (inbound or outbound) output: "Known system binary sent/received network traffic (user=%user.name command=%proc.cmdline connection=%fd.name)" - priority: WARNING + priority: NOTICE tags: [network] # With the current restriction on system calls handled by falco @@ -475,14 +475,14 @@ 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 proc.name in (userexec_binaries, mail_binaries, sshd, dbus-daemon-lau, ping, ping6, critical-stack-) output: "Unexpected setuid call by non-sudo, non-root program (user=%user.name parent=%proc.pname command=%proc.cmdline uid=%evt.arg.uid)" - priority: WARNING + priority: NOTICE tags: [users] - 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 proc.name in (user_mgmt_binaries) and not proc.name in (su, sudo) and not container 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 + priority: NOTICE tags: [host, users] - list: allowed_dev_files @@ -498,7 +498,7 @@ and not fd.name in (allowed_dev_files) and not fd.name startswith /dev/tty output: "File created below /dev by untrusted program (user=%user.name command=%proc.cmdline file=%fd.name)" - priority: WARNING + priority: ERROR tags: [filesystem] # fbash is a small shell script that runs bash, and is suitable for use in curl | fbash installers. @@ -506,21 +506,21 @@ desc: an attempt by a program in a pipe installer session to start listening for network connections condition: evt.type=listen and proc.sname=fbash output: "Unexpected listen call by a process in a fbash session (command=%proc.cmdline)" - priority: WARNING + priority: NOTICE tags: [network] - rule: Installer bash starts session desc: an attempt by a program in a pipe installer session to start a new session condition: evt.type=setsid and proc.sname=fbash output: "Unexpected setsid call by a process in fbash session (command=%proc.cmdline)" - priority: WARNING + priority: NOTICE tags: [process] - rule: Installer bash non https connection desc: an attempt by a program in a pipe installer session to make an outgoing connection on a non-http(s) port condition: proc.sname=fbash and outbound and not fd.sport in (80, 443, 53) output: "Outbound connection on non-http(s) port by a process in a fbash session (command=%proc.cmdline connection=%fd.name)" - priority: WARNING + priority: NOTICE tags: [network] # It'd be nice if we could warn when processes in a fbash session try From 5bafa198c69b575cadf413b6c3f61d96d45aeab1 Mon Sep 17 00:00:00 2001 From: Mark Stemm Date: Thu, 25 May 2017 12:15:35 -0700 Subject: [PATCH 2/2] Update automated tests to handle new priority lvls The default falco ruleset now has a wider variety of priorities, so adjust the automated tests to match: - Instead of creating a generic test yaml entry for every trace file in traces-{positive,negative,info} with assumptions about detect levels, add a new falco_traces.yaml.in multiplex file that has specific information about the detect priorities and rule detect counts for each trace file. - If a given trace file doesn't have a corresponding entry in falco_traces.yaml.in, a generic entry is added with a simple detect: (True|False) value and level. That way you can get specific detect levels/counts for existing trace files, but if you forget to add a trace to falco_traces.yaml.in, you'll still get some coverage. - falco_tests.yaml.in isn't added to any longer, so rename it to falco_tests.yaml. - Avocado is now run twice--once on each yaml file. The final test passes if both avocado runs pass. --- .../{falco_tests.yaml.in => falco_tests.yaml} | 0 test/falco_traces.yaml.in | 203 ++++++++++++++++++ test/run_regression_tests.sh | 62 +++--- 3 files changed, 235 insertions(+), 30 deletions(-) rename test/{falco_tests.yaml.in => falco_tests.yaml} (100%) create mode 100644 test/falco_traces.yaml.in diff --git a/test/falco_tests.yaml.in b/test/falco_tests.yaml similarity index 100% rename from test/falco_tests.yaml.in rename to test/falco_tests.yaml diff --git a/test/falco_traces.yaml.in b/test/falco_traces.yaml.in new file mode 100644 index 00000000..2c581e9b --- /dev/null +++ b/test/falco_traces.yaml.in @@ -0,0 +1,203 @@ +has_json_output: !mux + yes: + json_output: True + no: + json_output: False + +traces: !mux + change-thread-namespace: + trace_file: traces-positive/change-thread-namespace.scap + detect: True + detect_level: NOTICE + detect_counts: + - "Change thread namespace": 2 + + container-privileged: + trace_file: traces-positive/container-privileged.scap + detect: True + detect_level: INFO + detect_counts: + - "File Open by Privileged Container": 19 + + container-sensitive-mount: + trace_file: traces-positive/container-sensitive-mount.scap + detect: True + detect_level: INFO + detect_counts: + - "Sensitive Mount by Container": 19 + + create-files-below-dev: + trace_file: traces-positive/create-files-below-dev.scap + detect: True + detect_level: ERROR + detect_counts: + - "Create files below dev": 1 + + db-program-spawned-process: + trace_file: traces-positive/db-program-spawned-process.scap + detect: True + detect_level: NOTICE + detect_counts: + - "DB program spawned process": 1 + + falco-event-generator: + trace_file: traces-positive/falco-event-generator.scap + detect: True + detect_level: [ERROR, WARNING, INFO, NOTICE] + detect_counts: + - "Write below binary dir": 1 + - "Read sensitive file untrusted": 3 + - "Run shell in container": 1 + - "Write below rpm database": 1 + - "Write below etc": 1 + - "System procs network activity": 1 + - "Mkdir binary dirs": 1 + - "System user interactive": 1 + - "DB program spawned process": 1 + - "Non sudo setuid": 1 + - "Create files below dev": 1 + - "Modify binary dirs": 2 + - "Change thread namespace": 2 + + installer-fbash-manages-service: + trace_file: traces-info/installer-fbash-manages-service.scap + detect: True + detect_level: INFO + detect_counts: + - "Installer bash manages service": 4 + + installer-bash-non-https-connection: + trace_file: traces-positive/installer-bash-non-https-connection.scap + detect: True + detect_level: NOTICE + detect_counts: + - "Installer bash non https connection": 1 + + installer-fbash-runs-pkgmgmt: + trace_file: traces-info/installer-fbash-runs-pkgmgmt.scap + detect: True + detect_level: [NOTICE, INFO] + detect_counts: + - "Installer bash runs pkgmgmt program": 4 + - "Installer bash non https connection": 4 + + installer-bash-starts-network-server: + trace_file: traces-positive/installer-bash-starts-network-server.scap + detect: True + detect_level: NOTICE + detect_counts: + - "Installer bash starts network server": 2 + - "Installer bash non https connection": 3 + + installer-bash-starts-session: + trace_file: traces-positive/installer-bash-starts-session.scap + detect: True + detect_level: NOTICE + detect_counts: + - "Installer bash starts session": 1 + - "Installer bash non https connection": 3 + + mkdir-binary-dirs: + trace_file: traces-positive/mkdir-binary-dirs.scap + detect: True + detect_level: ERROR + detect_counts: + - "Mkdir binary dirs": 1 + + modify-binary-dirs: + trace_file: traces-positive/modify-binary-dirs.scap + detect: True + detect_level: ERROR + detect_counts: + - "Modify binary dirs": 1 + + modify-package-repo-list-installer: + trace_file: traces-info/modify-package-repo-list-installer.scap + detect: True + detect_level: INFO + detect_counts: + - "Write below etc in installer": 1 + + non-sudo-setuid: + trace_file: traces-positive/non-sudo-setuid.scap + detect: True + detect_level: NOTICE + detect_counts: + - "Non sudo setuid": 1 + + read-sensitive-file-after-startup: + trace_file: traces-positive/read-sensitive-file-after-startup.scap + detect: True + detect_level: WARNING + detect_counts: + - "Read sensitive file untrusted": 1 + + read-sensitive-file-untrusted: + trace_file: traces-positive/read-sensitive-file-untrusted.scap + detect: True + detect_level: WARNING + detect_counts: + - "Read sensitive file untrusted": 1 + + run-shell-untrusted: + trace_file: traces-positive/run-shell-untrusted.scap + detect: True + detect_level: DEBUG + detect_counts: + - "Run shell untrusted": 1 + + shell-in-container: + trace_file: traces-positive/shell-in-container.scap + detect: True + detect_level: NOTICE + detect_counts: + - "Run shell in container": 1 + + system-binaries-network-activity: + trace_file: traces-positive/system-binaries-network-activity.scap + detect: True + detect_level: NOTICE + detect_counts: + - "System procs network activity": 1 + + system-user-interactive: + trace_file: traces-positive/system-user-interactive.scap + detect: True + detect_level: INFO + detect_counts: + - "System user interactive": 1 + + user-mgmt-binaries: + trace_file: traces-positive/user-mgmt-binaries.scap + detect: True + detect_level: NOTICE + detect_counts: + - "User mgmt binaries": 1 + + write-binary-dir: + trace_file: traces-positive/write-binary-dir.scap + detect: True + detect_level: ERROR + detect_counts: + - "Write below binary dir": 4 + + write-etc: + trace_file: traces-positive/write-etc.scap + detect: True + detect_level: ERROR + detect_counts: + - "Write below etc": 1 + + write-etc-installer: + trace_file: traces-info/write-etc-installer.scap + detect: True + detect_level: INFO + detect_counts: + - "Write below etc in installer": 1 + + write-rpm-database: + trace_file: traces-positive/write-rpm-database.scap + detect: True + detect_level: ERROR + detect_counts: + - "Write below rpm database": 1 diff --git a/test/run_regression_tests.sh b/test/run_regression_tests.sh index 2b707fe5..006397b7 100755 --- a/test/run_regression_tests.sh +++ b/test/run_regression_tests.sh @@ -2,7 +2,6 @@ SCRIPT=$(readlink -f $0) SCRIPTDIR=$(dirname $SCRIPT) -MULT_FILE=$SCRIPTDIR/falco_tests.yaml BRANCH=$1 function download_trace_files() { @@ -19,56 +18,59 @@ function prepare_multiplex_fileset() { dir=$1 detect=$2 - detect_level=$3 - json_output=$4 for trace in $SCRIPTDIR/$dir/*.scap ; do [ -e "$trace" ] || continue NAME=`basename $trace .scap` - cat << EOF >> $MULT_FILE - $NAME-detect-$detect-json-$json_output: + + # falco_traces.yaml might already have an entry for this trace + # file, with specific detection levels and counts. If so, skip + # it. Otherwise, add a generic entry showing whether or not to + # detect anything. + grep -q "$NAME:" $SCRIPTDIR/falco_traces.yaml && continue + + cat << EOF >> $SCRIPTDIR/falco_traces.yaml + $NAME: detect: $detect - detect_level: $detect_level + detect_level: WARNING trace_file: $trace - json_output: $json_output EOF done } function prepare_multiplex_file() { - cp $SCRIPTDIR/falco_tests.yaml.in $MULT_FILE + cp $SCRIPTDIR/falco_traces.yaml.in $SCRIPTDIR/falco_traces.yaml - prepare_multiplex_fileset traces-positive True WARNING False - prepare_multiplex_fileset traces-negative False WARNING True - prepare_multiplex_fileset traces-info True INFO False + prepare_multiplex_fileset traces-positive True + prepare_multiplex_fileset traces-negative False + prepare_multiplex_fileset traces-info True - prepare_multiplex_fileset traces-positive True WARNING True - prepare_multiplex_fileset traces-info True INFO True - - echo "Contents of $MULT_FILE:" - cat $MULT_FILE + echo "Contents of $SCRIPTDIR/falco_traces.yaml:" + cat $SCRIPTDIR/falco_traces.yaml } -function run_tests() { - rm -rf /tmp/falco_outputs - mkdir /tmp/falco_outputs - CMD="avocado run --multiplex $MULT_FILE --job-results-dir $SCRIPTDIR/job-results -- $SCRIPTDIR/falco_test.py" - echo "Running: $CMD" - $CMD - TEST_RC=$? -} - - function print_test_failure_details() { echo "Showing full job logs for any tests that failed:" jq '.tests[] | select(.status != "PASS") | .logfile' $SCRIPTDIR/job-results/latest/results.json | xargs cat } +function run_tests() { + rm -rf /tmp/falco_outputs + mkdir /tmp/falco_outputs + TEST_RC=0 + for mult in $SCRIPTDIR/falco_traces.yaml $SCRIPTDIR/falco_tests.yaml; do + CMD="avocado run --multiplex $mult --job-results-dir $SCRIPTDIR/job-results -- $SCRIPTDIR/falco_test.py" + echo "Running: $CMD" + $CMD + RC=$? + TEST_RC=$((TEST_RC+$RC)) + if [ $RC -ne 0 ]; then + print_test_failure_details + fi + done +} + download_trace_files prepare_multiplex_file run_tests -if [ $TEST_RC -ne 0 ]; then - print_test_failure_details -fi - exit $TEST_RC