Add name and description fields to all rules. The name field is actually
a field called 'rule', which corresponds to the 'macro' field for
macros.
Within the rule loader, the state changes slightly. There are two
indices into the set of rules 'rules_by_name' and
'rules_by_idx' (formerly 'outputs'). They both now contain the original
table from the yaml parse. One field 'level' is added which is the
priority mapped to a number.
Get rid of the notion of default priority or output. Every rule must now
provide both.
Go through all current rules and add names and descriptions.
Update rules to reduce FPs after running against some real-world
environments with and without containers. Summary of changes:
- Too many processes read /etc/passwd--it's world-readable and a
side-effect of getpwent. Switch to /etc/shadow instead.
- Add a mail_binaries group. This wasn't directly used, but it may be
handy for other rules and goes along with the changes in #54.
- not_cron was the only macro expressing a negative, so switch it to be
a positive 'cron'. Also add crond as a cron process.
- add dragent to the set of programs that can call setns.
- For the shell detection rules, change them to only look for the
specific exec/clone event rather than all follow-on activity. Also
allow docker to spawn shell scripts--this is required for entrypoints
that use the shell instead of a direct exec. Also add a few
additional programs that can spawn shells.
- In containers, shells are allowed as long as the parent process is
docker or bash. Like the outside of container case, only the initial
clone/exec is detected.
- Fix a typo Sytem -> System.
- Change the chmod rule to only protect imporant/sensitive files. I saw
lots of "regular" files being chmod()ed.
- Change the setuid test to allow root to setuid to anything, rather
than listing a bunch of programs run as root that drop privileges.
- Allow running su/sudo in containers. Some containers add users from a
base linux distribution before running.
It isn't being used yet, for now we're using the corresponding script
from the sysdig repo. Removing it to avoid confusion, we can later
re-add as necessary.
Instead of running bash as the sysdig container does, run falco. This
makes sense as falco doesn't have a general purpose use like sysdig
does.
To make it easier to run both in docker and as a daemon using the
default command line, enable both syslog and stdout/stderr output by
default. Now that falco dups stdout/stderr to /dev/null when
daemonizing, the stdout/stderr is just thrown away. And when running in
docker, the syslog output will just be discarded unless someone plumbs
the container's syslog output.
Update README.md to reflect that specifying the falco command is not
necessary.
This will detect the result of some sql injection attacks where the
injected query tries to spawn a process.
We don't include web servers in this list for now due to things like
mod_perl, mod_php, etc. Maybe we can add it once we make exceptions for
those modules.
Add back detection for mysql and sensitive files that was removed in the
previous commit. A new macro proc_is_new adds a condition on how long a
process has been running.
A new rule triggers if the process is not new and tries to open a
sensitive file. This handles cases like mysql, where it *does* read
/etc/passwd on startup but shouldn't really open it afterward.
Add some new groups of binary programs as macros and start using them in
the set of rules:
- docker_binaries: docker and exe (which is a temporary process name
for processes like docker-proxy)
- http_server_binaries: httpd, nginx, and similar
- db_server_binaries: mysql for now, we'll add more later
- server_binaries: all of the above
- userexec_binaries: sudo and su.
Start using these groups in the rules. Most of the time, changing from
the inline lists of processes to macros was a no-op. There are some
actual changes, though:
- docker and exe are now allowed to read 'sensitive' files. They may
not actually do so, but it's not really harmful.
- lighttpd is now allowed to read 'sensitive' files, via inclusion in
http_server_binaries.
- su, lighttpd, and docker can now setuid.
- http-foreground is included as a http server wrt non-port 80/443 ports.
I'm going to use these macros in some of the following rules.
This actually prevents detection of mysql reading sensitive files, which
is one of the demo scenarios (sql injection). I plan on adding this
detection back in the next commit.
Remove the old use of the '-o' command line option, it wasn't being
used.
Allow any config file option to be overridden on the command line, via
--option/-o. These options are applied to the configuration object after
reading the file, ensuring the command line options override anything in
the config file.
To support this, add some methods to yaml_configuration that allows you
to set the value for a top level key or key + subkey, and methods to
falco_configuration that allow providing a set of command line arguments
alongside the config file.
Ensure that any fatal error is always printed to stderr even if stderr
logging is not enabled. This makes sure that falco won't silently exit
on an error. This is especially important when daemonizing and when an
initial fatal error occurs first.
As a part of this, change all fatal errors to throw exceptions instead,
so all fatal errors get routed through the exception handler.
Improve daemonization by reopening stdin/stdout/stderr to /dev/null so
you don't have to worry about writing to a closed stderr on exit.
Make changes to falco_rules.yaml to make sure they work on the demo
scenarios without too many false positives. The specific changes are:
- Add /etc/ld.so.cache as an allowed shared library to open.
- Comment out the shared library check for now--there are lots of
locations below /usr/lib for things like python, perl, etc and I want
to get a fuller categorization first.
- Add a few additional parent processes that can spawn shells, write
sensitive files, and call setuid. Also allow bash shells with no
parent to spawn shells. We may want to disallow this but I suspect a
better place to detect is the parent-less bash shell becoming a
session leader.
- Add rules for fs-bash (falco-safe bash), which is used in the curl
<url> | bash installer demo. The idea is that fs-bash has restrictions
on what it and child proceses can do.
- Add trailing '/' characters to path names in bin_dir_* so paths like
/tmp/binary don't accidentally match '/bin'
Note that as process names are truncated to 15 characters, long process
names like 'httpd-foregroun' are intentionally truncated.
Henri pointed out that events may also be flagged as ignored. So
populate a second table with the set of ignored events, rename
check_for_ignored_syscalls to check_for_ignored_syscalls_events, and
separately check each table based on whether the LHS of the expression
is evt.type or syscall.type.
Add support for daemonizing via the --daemon flag. If daemonized, the
pid is written to the file provided via the --pidfile flag. When
daemonized, falco immediately returns an error if stderr output or
logging was chosen on the command line.
Clean up handling of outputs to match the expected use case (daemon):
- syslog output is enabled by default
- stdout output is disabled by default
- If not configured at all, both outputs are enabled.
Also fix some bugs I found while running via packages:
- There were still some references to the old rules filename
falco_rules.conf.
- The redhat package mistakenly defined some system directories like
/etc, /etc/init.d. Add them to the exclusion list (See
https://cmake.org/Bug/view.php?id=13609 for context).
- Clean up some of the error messages to be more consistent.
After this I was able to build and install debian and rpm
packages. Starting the falco service ran falco as a daemon with syslog
output.
Add init.d scripts to debian/redhat packages as well as
postinstall/remove scripts to enable the falco service on install and
disable it on uninstall.
I still need to add support for daemonization to falco, and change the
default output options to match the expected use of being daemonized.
The ignored syscalls in macros were:
- write: renamed to open_write to make its weaker resolution more
apparent. Checks for open with any flag that could change a file.
- read: renamed to open_read. Checks for open with any read flag.
- sendto: I couldn't think of any way to replace this, so I simply
removed it with a comment.
I kept the original read/write macros commented out with a note that
they use ignored syscalls.
I have not tested these changes yet other than verifying that falco
starts properly.
Create a table containing the filtered syscalls and set it as the lua
global m_lua_ignored_syscalls == ignored_syscalls.
In the parser, add a general purpose ast traversal function
traverse_ast that calls a callback for all nodes of a specific type.
In the compiler, add a new function check_for_ignored_syscalls that uses
the traversal function to be called back for all "BinaryRelOp"
nodes (i.e. X = Y, X in [a, b, c], etc). For those nodes, if the lhs is
a field 'evt.type' or 'syscall.type' and the rhs contains one of the
ignored syscalls, throw an error.
Call check_for_ignored_syscalls after parsing any macro or rule
filter. The thrown error will contain the macro or rule that had the
ignored syscall.
In the next commit I'll change the rules to skip the ignored syscalls.