diff --git a/docker/event-generator/event_generator.cpp b/docker/event-generator/event_generator.cpp index 5bbe844f..3311b6b0 100644 --- a/docker/event-generator/event_generator.cpp +++ b/docker/event-generator/event_generator.cpp @@ -97,6 +97,8 @@ void exfiltration() shadow.open("/etc/shadow"); + printf("Reading /etc/shadow and sending to 10.5.2.6:8197...\n"); + if(!shadow.is_open()) { fprintf(stderr, "Could not open /etc/shadow for reading: %s", strerror(errno)); @@ -219,7 +221,7 @@ void write_rpm_database() { } void spawn_shell() { - printf("Spawning a shell using system()...\n"); + printf("Spawning a shell to run \"ls > /dev/null\" using system()...\n"); int rc; if ((rc = system("ls > /dev/null")) != 0) @@ -259,6 +261,7 @@ void mkdir_binary_dirs() { void change_thread_namespace() { printf("Calling setns() to change namespaces...\n"); + printf("NOTE: does not result in a falco notification in containers, unless container run with --privileged or --security-opt seccomp=unconfined\n"); // It doesn't matter that the arguments to setns are // bogus. It's the attempt to call it that will trigger the // rule. @@ -268,6 +271,7 @@ void change_thread_namespace() { void system_user_interactive() { pid_t child; + printf("Forking a child that becomes user=daemon and then tries to run /bin/login...\n"); // Fork a child and do everything in the child. if ((child = fork()) == 0) { @@ -313,6 +317,8 @@ void system_procs_network_activity() { void non_sudo_setuid() { pid_t child; + printf("Forking a child that becomes \"daemon\" user and then \"root\"...\n"); + // Fork a child and do everything in the child. if ((child = fork()) == 0) { @@ -367,6 +373,9 @@ map defined_actions = {{"write_binary_dir", write_binary_dir}, {"user_mgmt_binaries", user_mgmt_binaries}, {"exfiltration", exfiltration}}; +// Some actions don't directly result in suspicious behavior. These +// actions are excluded from the ones run with -a all. +set exclude_from_all_actions = {"exec_ls", "network_activity"}; void create_symlinks(const char *program) { @@ -394,9 +403,9 @@ void run_actions(map &actions, int interval, bool once) { for (auto action : actions) { - sleep(interval); printf("***Action %s\n", action.first.c_str()); action.second(); + sleep(interval); } if(once) { @@ -428,7 +437,7 @@ int main(int argc, char **argv) // Parse the args // while((op = getopt_long(argc, argv, - "ha:i:l:", + "ha:i:l:o", long_options, &long_index)) != -1) { switch(op) @@ -437,12 +446,16 @@ int main(int argc, char **argv) usage(argv[0]); exit(1); case 'a': - if((it = defined_actions.find(optarg)) == defined_actions.end()) + // "all" is already implied + if (strcmp(optarg, "all") != 0) { - fprintf(stderr, "No action with name \"%s\" known, exiting.\n", optarg); - exit(1); + if((it = defined_actions.find(optarg)) == defined_actions.end()) + { + fprintf(stderr, "No action with name \"%s\" known, exiting.\n", optarg); + exit(1); + } + actions.insert(*it); } - actions.insert(*it); break; case 'i': interval = atoi(optarg); @@ -482,7 +495,13 @@ int main(int argc, char **argv) if(actions.size() == 0) { - actions = defined_actions; + for(auto &act : defined_actions) + { + if(exclude_from_all_actions.find(act.first) == exclude_from_all_actions.end()) + { + actions.insert(act); + } + } } setvbuf(stdout, NULL, _IONBF, 0); diff --git a/examples/nodejs-bad-rest-api/demo.yml b/examples/nodejs-bad-rest-api/demo.yml index a826ab6e..ab0b4988 100644 --- a/examples/nodejs-bad-rest-api/demo.yml +++ b/examples/nodejs-bad-rest-api/demo.yml @@ -20,5 +20,4 @@ falco: - /boot:/host/boot:ro - /lib/modules:/host/lib/modules:ro - /usr:/host/usr:ro - - ${PWD}/../../rules/falco_rules.yaml:/etc/falco_rules.yaml tty: true diff --git a/rules/falco_rules.yaml b/rules/falco_rules.yaml index 4e411648..13cb0e68 100644 --- a/rules/falco_rules.yaml +++ b/rules/falco_rules.yaml @@ -345,7 +345,8 @@ - macro: trusted_containers condition: (container.image startswith sysdig/agent or - container.image startswith sysdig/falco or + (container.image startswith sysdig/falco and + not container.image startswith sysdig/falco-event-generator) or container.image startswith sysdig/sysdig or container.image startswith gcr.io/google_containers/hyperkube or container.image startswith gcr.io/google_containers/kube-proxy) diff --git a/test/falco_test.py b/test/falco_test.py index d6ce9b87..afb7c4f8 100644 --- a/test/falco_test.py +++ b/test/falco_test.py @@ -56,6 +56,16 @@ class FalcoTest(Test): for rule in self.disabled_rules: self.disabled_args = self.disabled_args + "-D " + rule + " " + self.detect_counts = self.params.get('detect_counts', '*', default=False) + if self.detect_counts == False: + self.detect_counts = {} + else: + detect_counts = {} + for item in self.detect_counts: + for item2 in item: + detect_counts[item2[0]] = item2[1] + self.detect_counts = detect_counts + self.rules_warning = self.params.get('rules_warning', '*', default=False) if self.rules_warning == False: self.rules_warning = sets.Set() @@ -161,6 +171,23 @@ class FalcoTest(Test): if not events_detected > 0: self.fail("Detected {} events at level {} when should have detected > 0".format(events_detected, level)) + def check_detections_by_rule(self, res): + # Get the number of events detected for each rule. Must match the expected counts. + match = re.search('Triggered rules by rule name:(.*)', res.stdout, re.DOTALL) + if match is None: + self.fail("Could not find a block 'Triggered rules by rule name: ...' in falco output") + + triggered_rules = match.group(1) + + for rule, count in self.detect_counts.iteritems(): + expected_line = '{}: {}'.format(rule, count) + match = re.search(expected_line, triggered_rules) + + if match is None: + self.fail("Could not find a line '{}' in triggered rule counts '{}'".format(expected_line, triggered_rules)) + else: + self.log.debug("Found expected count for {}: {}".format(rule, match.group())) + def check_outputs(self): for output in self.outputs: # Open the provided file and match each line against the @@ -222,6 +249,8 @@ class FalcoTest(Test): if len(self.rules_events) > 0: self.check_rules_events(res) self.check_detections(res) + if len(self.detect_counts) > 0: + self.check_detections_by_rule(res) self.check_json_output(res) self.check_outputs() pass diff --git a/test/falco_tests.yaml.in b/test/falco_tests.yaml.in index a973e8fe..977b7d4c 100644 --- a/test/falco_tests.yaml.in +++ b/test/falco_tests.yaml.in @@ -181,3 +181,22 @@ trace_files: !mux trace_file: trace_files/cat_write.scap outputs: - /tmp/falco_outputs/program_output.txt: Warning An open was seen + + detect_counts: + detect: True + detect_level: WARNING + trace_file: traces-positive/falco-event-generator.scap + 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