Skip to content

Commit

Permalink
Add additional tests related to pipe installers.
Browse files Browse the repository at this point in the history
Add additional tests related to using pipe installers within a fbash
session:

 - Modify write_etc to only trigger if *not* in a fbash session. There's
   a new rule write_etc_installer which has the same conditions when in
   a fbash session, logging at INFO severity.

 - A new rule write_rpm_database warns if any non package management
   program tries to write below /var/lib/rpm.

 - Add a new warning if any program below a fbash session tries to open
   an outbound network connection on ports other than http(s) and dns.

 - Add INFO level messages when programs in a fbash session try to run
   package management binaries (rpm,yum,etc) or service
   management (systemctl,chkconfig,etc) binaries.

In order to test these new INFO level rules, make up a third class of
trace files traces-info.zip containing trace files that should result in
info-level messages.

To differentiate warning and info level detection, add an attribute to
the multiplex file "detect_level", which is "Warning" for the files in
traces-positive and "Info" for the files in traces-info. Modify
falco_test.py to look specifically for a non-zero count for the given
detect_level.

Doing this exposed a bug in the way the level-specific counts were being
recorded--they were keeping counts by level name, not number. Fix that.
  • Loading branch information
mstemm committed Jun 1, 2016
1 parent 31c87c2 commit 10f821c
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 20 deletions.
54 changes: 48 additions & 6 deletions rules/falco_rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
# 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, yum)
condition: proc.name in (dpkg, dpkg-preconfigu, rpm, rpmkey, yum)

# A canonical set of processes that run other programs with different
# privileges or as a different user.
Expand All @@ -141,7 +141,7 @@
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 in (/etc/sudoers.d, /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.
Expand Down Expand Up @@ -194,11 +194,18 @@
priority: WARNING

- rule: write_etc
desc: an attempt to write to any file below /etc
condition: evt.dir = < and open_write and not shadowutils_binaries and not sysdigcloud_binaries_parent and not package_mgmt_binaries and etc_dir
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
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
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
Expand All @@ -211,6 +218,13 @@
output: "Sensitive file opened for reading by trusted program after startup (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: WARNING

# Only let rpm-related programs write to the rpm database
- rule: write_rpm_database
desc: an attempt to write to the rpm database by any non-rpm related program
condition: open_write and not proc.name in (rpm,rpmkey,yum) and fd.directory=/var/lib/rpm
output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline file=%fd.name)"
priority: WARNING

- 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
Expand Down Expand Up @@ -312,17 +326,45 @@

# fbash is a small shell script that runs bash, and is suitable for use in curl <curl> | fbash installers.
- rule: installer_bash_starts_network_server
desc: an attempt by any program that is in a session led by fbash to start listening for network connections
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

- rule: installer_bash_starts_session
desc: an attempt by any program that is in a session led by fbash to start a new 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

- 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: outbound and not fd.sport in (80, 443, 53) and proc.sname=fbash
output: "Outbound connection on non-http(s) port by a process in a fbash session (command=%proc.cmdline connection=%fd.name)"
priority: WARNING

# It'd be nice if we could warn when processes in a fbash session try
# to download from any nonstandard location? This is probably blocked
# on https://github.com/draios/falco/issues/88 though.

# Notice when processes try to run chkconfig/systemctl.... to install a service.
# Note: this is not a WARNING, as you'd expect some service management
# as a part of doing the installation.
- rule: installer_bash_manages_service
desc: an attempt by a program in a pipe installer session to manage a system service (systemd/chkconfig)
condition: evt.type=execve and proc.name in (chkconfig, systemctl) and proc.sname=fbash
output: "Service management program run by process in a fbash session (command=%proc.cmdline)"
priority: INFO

# Notice when processes try to run any package management binary within a fbash session.
# Note: this is not a WARNING, as you'd expect some package management
# 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
output: "Package management program run by process in a fbash session (command=%proc.cmdline)"
priority: INFO

###########################
# Application-Related Rules
###########################
Expand Down
25 changes: 20 additions & 5 deletions test/falco_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ def setUp(self):
self.should_detect = self.params.get('detect', '*')
self.trace_file = self.params.get('trace_file', '*')

if self.should_detect:
self.detect_level = self.params.get('detect_level', '*')

# Doing this in 2 steps instead of simply using
# module_is_loaded to avoid logging lsmod output to the log.
lsmod_output = process.system_output("lsmod", verbose=False)
Expand All @@ -44,17 +47,29 @@ def test(self):
cmd, res.exit_status))

# Get the number of events detected.
res = re.search('Events detected: (\d+)', res.stdout)
if res is None:
match = re.search('Events detected: (\d+)', res.stdout)
if match is None:
self.fail("Could not find a line 'Events detected: <count>' in falco output")

events_detected = int(res.group(1))
events_detected = int(match.group(1))

if not self.should_detect and events_detected > 0:
self.fail("Detected {} events when should have detected none".format(events_detected))

if self.should_detect and events_detected == 0:
self.fail("Detected {} events when should have detected > 0".format(events_detected))
if self.should_detect:
if events_detected == 0:
self.fail("Detected {} events when should have detected > 0".format(events_detected))

level_line = '{}: (\d+)'.format(self.detect_level)
match = re.search(level_line, res.stdout)

if match is None:
self.fail("Could not find a line '{}: <count>' in falco output".format(self.detect_level))

events_detected = int(match.group(1))

if not events_detected > 0:
self.fail("Detected {} events at level {} when should have detected > 0".format(events_detected, self.detect_level))

pass

Expand Down
15 changes: 14 additions & 1 deletion test/run_regression_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ SCRIPTDIR=$(dirname $SCRIPT)
MULT_FILE=$SCRIPTDIR/falco_tests.yaml

function download_trace_files() {
for TRACE in traces-positive traces-negative ; do
for TRACE in traces-positive traces-negative traces-info ; do
rm -rf $SCRIPTDIR/$TRACE
curl -so $SCRIPTDIR/$TRACE.zip https://s3.amazonaws.com/download.draios.com/falco-tests/$TRACE.zip &&
unzip -d $SCRIPTDIR $SCRIPTDIR/$TRACE.zip &&
rm -rf $SCRIPTDIR/$TRACE.zip
Expand All @@ -21,6 +22,7 @@ function prepare_multiplex_file() {
cat << EOF >> $MULT_FILE
$NAME:
detect: True
detect_level: Warning
trace_file: $trace
EOF
done
Expand All @@ -35,6 +37,17 @@ EOF
EOF
done

for trace in $SCRIPTDIR/traces-info/*.scap ; do
[ -e "$trace" ] || continue
NAME=`basename $trace .scap`
cat << EOF >> $MULT_FILE
$NAME:
detect: True
detect_level: Informational
trace_file: $trace
EOF
done

echo "Contents of $MULT_FILE:"
cat $MULT_FILE
}
Expand Down
15 changes: 7 additions & 8 deletions userspace/falco/lua/rule_loader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,13 @@ function set_output(output_format, state)
end

local function priority(s)
valid_levels = {"emergency", "alert", "critical", "error", "warning", "notice", "informational", "debug"}
s = string.lower(s)
for i,v in ipairs(valid_levels) do
if (string.find(v, "^"..s)) then
for i,v in ipairs(output.levels) do
if (string.find(string.lower(v), "^"..s)) then
return i - 1 -- (syslog levels start at 0, lua indices start at 1)
end
end
error("Invalid severity level: "..level)
error("Invalid severity level: "..s)
end

-- Note that the rules_by_name and rules_by_idx refer to the same rule
Expand Down Expand Up @@ -232,8 +231,8 @@ end

local rule_output_counts = {total=0, by_level={}, by_name={}}

for idx, level in ipairs(output.levels) do
rule_output_counts[level] = 0
for idx=0,table.getn(output.levels)-1,1 do
rule_output_counts.by_level[idx] = 0
end

function on_event(evt_, rule_id)
Expand Down Expand Up @@ -265,8 +264,8 @@ function print_stats()
print("Rule counts by severity:")
for idx, level in ipairs(output.levels) do
-- To keep the output concise, we only print 0 counts for error, warning, and info levels
if rule_output_counts[level] > 0 or level == "Error" or level == "Warning" or level == "Informational" then
print (" "..level..": "..rule_output_counts[level])
if rule_output_counts.by_level[idx-1] > 0 or level == "Error" or level == "Warning" or level == "Informational" then
print (" "..level..": "..rule_output_counts.by_level[idx-1])
end
end

Expand Down

0 comments on commit 10f821c

Please sign in to comment.