diff --git a/rules/falco_rules.yaml b/rules/falco_rules.yaml index 557f11dd..b619f171 100644 --- a/rules/falco_rules.yaml +++ b/rules/falco_rules.yaml @@ -3,10 +3,31 @@ ############# # File actions -- macro: write - condition: (syscall.type=write and fd.type in (file, directory)) -- macro: read - condition: (syscall.type=read and evt.dir=> and fd.type in (file, directory)) + + +# Currently disabled as read/write are ignored syscalls. The nearly +# similar open_write/open_read check for files being opened for +# reading/writing. +# - macro: write +# condition: (syscall.type=write and fd.type in (file, directory)) +# - macro: read +# condition: (syscall.type=read and evt.dir=> and fd.type in (file, directory)) + +- macro: open_write + condition: > + (evt.type=open or evt.type=openat) and + fd.typechar='f' and + (evt.arg.flags contains O_WRONLY or + evt.arg.flags contains O_RDWR or + evt.arg.flags contains O_CREAT or + evt.arg.flags contains O_TRUNC) +- macro: open_read + condition: > + (evt.type=open or evt.type=openat) and + fd.typechar='f' and + (evt.arg.flags contains O_RDONLY or + evt.arg.flags contains O_RDWR) + - macro: rename condition: syscall.type = rename - macro: mkdir @@ -79,8 +100,10 @@ # Network - macro: inbound 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 - condition: ((syscall.type=connect and evt.dir=<) or (syscall.type=sendto and evt.dir=>)) and (fd.typechar=4 or fd.typechar=6) + condition: syscall.type=connect and evt.dir=< and (fd.typechar=4 or fd.typechar=6) - macro: ssh_port condition: fd.lport=22 @@ -112,17 +135,17 @@ ####### # Don't write to binary dirs -- condition: evt.dir = > and write and bin_dir +- condition: evt.dir = > and open_write and bin_dir output: "Write to bin dir (%user.name %proc.name %evt.dir %evt.type %evt.args %fd.name)" priority: WARNING # Don't write to /etc -- condition: evt.dir = > and write and etc_dir +- condition: evt.dir = > and open_write and etc_dir output: "Write to etc dir (%user.name %proc.name %evt.dir %evt.type %evt.args %fd.name)" priority: WARNING # Don't read 'sensitive' files -- condition: read and not proc.name in (sshd, sudo, su) and not_cron and sensitive_files +- condition: open_read and not proc.name in (sshd, sudo, su) and not_cron and sensitive_files output: "Read sensitive file (%user.name %proc.name %evt.dir %evt.type %evt.args %fd.name)" priority: WARNING @@ -132,7 +155,7 @@ priority: WARNING # Don't load shared objects coming from unexpected places -- condition: read and fd.name contains .so and not (ubuntu_so_dirs or centos_so_dirs) +- condition: open_read and fd.name contains .so and not (ubuntu_so_dirs or centos_so_dirs) output: "Loaded .so from unexpected dir (%user.name %proc.name %evt.dir %evt.type %evt.args %fd.name)" priority: WARNING diff --git a/userspace/falco/lua/compiler.lua b/userspace/falco/lua/compiler.lua index ac7793a5..0e809dd6 100644 --- a/userspace/falco/lua/compiler.lua +++ b/userspace/falco/lua/compiler.lua @@ -111,6 +111,51 @@ function get_macros(ast, set) return set end +function check_for_ignored_syscalls_events(ast, filter_type, source) + + function check_syscall(val) + if ignored_syscalls[val] then + error("Ignored syscall \""..val.."\" in "..filter_type..": "..source) + end + + end + + function check_event(val) + if ignored_events[val] then + error("Ignored event \""..val.."\" in "..filter_type..": "..source) + end + end + + function cb(node) + if node.left.type == "FieldName" and + (node.left.value == "evt.type" or + node.left.value == "syscall.type") then + + if node.operator == "in" then + for i, v in ipairs(node.right.elements) do + if v.type == "BareString" then + if node.left.value == "evt.type" then + check_event(v.value) + else + check_syscall(v.value) + end + end + end + else + if node.right.type == "BareString" then + if node.left.value == "evt.type" then + check_event(node.right.value) + else + check_syscall(node.right.value) + end + end + end + end + end + + parser.traverse_ast(ast, "BinaryRelOp", cb) +end + function compiler.compile_macro(line) local ast, error_msg = parser.parse_filter(line) @@ -119,6 +164,10 @@ function compiler.compile_macro(line) error(error_msg) end + -- Traverse the ast looking for events/syscalls in the ignored + -- syscalls table. If any are found, return an error. + check_for_ignored_syscalls_events(ast, 'macro', line) + return ast end @@ -133,6 +182,10 @@ function compiler.compile_filter(source, macro_defs) error(error_msg) end + -- Traverse the ast looking for events/syscalls in the ignored + -- syscalls table. If any are found, return an error. + check_for_ignored_syscalls_events(ast, 'rule', source) + if (ast.type == "Rule") then -- Line is a filter, so expand macro references repeat diff --git a/userspace/falco/lua/parser.lua b/userspace/falco/lua/parser.lua index a54f3210..b43a9cfe 100644 --- a/userspace/falco/lua/parser.lua +++ b/userspace/falco/lua/parser.lua @@ -291,4 +291,46 @@ function print_ast(ast, level) end parser.print_ast = print_ast +-- Traverse the provided ast and call the provided callback function +-- for any nodes of the specified type. The callback function should +-- have the signature: +-- cb(ast_node, ctx) +-- ctx is optional. +function traverse_ast(ast, node_type, cb, ctx) + local t = ast.type + + if t == node_type then + cb(ast, ctx) + end + + if t == "Rule" then + traverse_ast(ast.filter, node_type, cb, ctx) + + elseif t == "Filter" then + traverse_ast(ast.value, node_type, cb, ctx) + + elseif t == "BinaryBoolOp" or t == "BinaryRelOp" then + traverse_ast(ast.left, node_type, cb, ctx) + traverse_ast(ast.right, node_type, cb, ctx) + + elseif t == "UnaryRelOp" or t == "UnaryBoolOp" then + traverse_ast(ast.argument, node_type, cb, ctx) + + elseif t == "List" then + for i, v in ipairs(ast.elements) do + traverse_ast(v, node_type, cb, ctx) + end + + elseif t == "MacroDef" then + traverse_ast(ast.value, node_type, cb, ctx) + + elseif t == "FieldName" or t == "Number" or t == "String" or t == "BareString" or t == "Macro" then + -- do nothing, no traversal needed + + else + error ("Unexpected type in traverse_ast: "..t) + end +end +parser.traverse_ast = traverse_ast + return parser diff --git a/userspace/falco/rules.cpp b/userspace/falco/rules.cpp index 07ef01a2..5e3fa45a 100644 --- a/userspace/falco/rules.cpp +++ b/userspace/falco/rules.cpp @@ -9,6 +9,7 @@ extern "C" { falco_rules::falco_rules(sinsp* inspector, lua_State *ls, string lua_main_filename) { + m_inspector = inspector; m_ls = ls; m_lua_parser = new lua_parser(inspector, m_ls); @@ -44,6 +45,42 @@ void falco_rules::load_rules(string rules_filename) lua_getglobal(m_ls, m_lua_load_rules.c_str()); if(lua_isfunction(m_ls, -1)) { + // Create a table containing the syscalls/events that + // are ignored by the kernel module. load_rules will + // return an error if any rule references one of these + // syscalls/events. + sinsp_evttables* einfo = m_inspector->get_event_info_tables(); + const struct ppm_event_info* etable = einfo->m_event_info; + const struct ppm_syscall_desc* stable = einfo->m_syscall_info_table; + + lua_newtable(m_ls); + + for(uint32_t j = 0; j < PPM_EVENT_MAX; j++) + { + if(etable[j].flags & EF_DROP_FALCO) + { + lua_pushstring(m_ls, etable[j].name); + lua_pushnumber(m_ls, 1); + lua_settable(m_ls, -3); + } + } + + lua_setglobal(m_ls, m_lua_ignored_events.c_str()); + + lua_newtable(m_ls); + + for(uint32_t j = 0; j < PPM_SC_MAX; j++) + { + if(stable[j].flags & EF_DROP_FALCO) + { + lua_pushstring(m_ls, stable[j].name); + lua_pushnumber(m_ls, 1); + lua_settable(m_ls, -3); + } + } + + lua_setglobal(m_ls, m_lua_ignored_syscalls.c_str()); + lua_pushstring(m_ls, rules_filename.c_str()); if(lua_pcall(m_ls, 1, 0, 0) != 0) { diff --git a/userspace/falco/rules.h b/userspace/falco/rules.h index 17d1b18b..0d5655e8 100644 --- a/userspace/falco/rules.h +++ b/userspace/falco/rules.h @@ -15,8 +15,11 @@ class falco_rules void load_compiler(string lua_main_filename); lua_parser* m_lua_parser; + sinsp* m_inspector; lua_State* m_ls; string m_lua_load_rules = "load_rules"; + string m_lua_ignored_syscalls = "ignored_syscalls"; + string m_lua_ignored_events = "ignored_events"; string m_lua_on_event = "on_event"; };