def test_net_taint(bpf_program: BPFProgram, caplog): Commands.add_profile(NET_PATH, False) Commands.add_net_rule(NET_PATH, NET_ACCESS.CREATE, NET_FAMILY.INET, BPFBOX_ACTION.TAINT) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([NET_PATH, 'create-inet6'])
def load(self, bpf): """ Load policy into the kernel. """ Commands.add_profile(self.profile, self.taint_on_exec) for rule in self.rules: rule.load(self) for (sym, is_kfunc), state_idx in self.funcs.items(): fn_name = f'bpfbox_state_probe_{state_idx}' ret_fn_name = f'bpfbox_state_retprobe_{state_idx}' if is_kfunc: bpf.attach_kprobe( sym=sym, fn_name=fn_name, ) bpf.attach_kretprobe( sym=sym, fn_name=ret_fn_name, ) else: bpf.attach_uprobe( name=self.profile, sym=sym, fn_name=fn_name, ) bpf.attach_uretprobe( name=self.profile, sym=sym, fn_name=ret_fn_name, )
def test_non_malicious_symlink_can_read_original(bpf_program: BPFProgram, caplog, setup_testdir): Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox', FS_ACCESS.WRITE) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.LINK) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ) subprocess.check_call([OPEN_PATH, 'malicious-symlink-read'])
def test_rename_allowed(bpf_program: BPFProgram, caplog, setup_testdir): os.mkdir('/tmp/bpfbox/new_dir') Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox', FS_ACCESS.WRITE) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/new_dir', FS_ACCESS.WRITE | FS_ACCESS.EXEC) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.RM) subprocess.check_call([OPEN_PATH, 'rename'])
def test_rename_no_newdir_write(bpf_program: BPFProgram, caplog, setup_testdir): os.mkdir('/tmp/bpfbox/new_dir') Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox', FS_ACCESS.WRITE) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/new_dir', FS_ACCESS.EXEC) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.RM) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([OPEN_PATH, 'rename'])
def load(self, policy: Policy): super().load(policy) state = self.calculate_state_number(policy) for _file in self.file: Commands.add_fs_rule( policy.profile, _file, FS_ACCESS.from_list(self.access), BPFBOX_ACTION.from_list(self.action), state=state, )
def load(self, policy: Policy): super().load(policy) state = self.calculate_state_number(policy) for target in self.target: Commands.add_ipc_rule( policy.profile, target, IPC_ACCESS.from_list(self.signal), BPFBOX_ACTION.from_list(self.action), state, )
def load(self, policy: Policy): super().load(policy) state = self.calculate_state_number(policy) for family in self.family: Commands.add_net_rule( policy.profile, NET_ACCESS.from_list(self.operation), NET_FAMILY.from_string(family), BPFBOX_ACTION.from_list(self.action), state, )
def process_policy_text(cls, txt: str): policy = cls.parse_policy_text(txt) try: Commands.add_profile(policy.profile, True) except Exception as e: logger.error('Failed to create profile for {policy.profile}', exc_info=e) for rule in policy.rules: try: rule(policy.profile) except Exception as e: logger.error(f'Error applying rule {rule}', exc_info=e)
def test_net_create_rules(bpf_program: BPFProgram, caplog): Commands.add_profile(NET_PATH, False) Commands.add_net_rule(NET_PATH, NET_ACCESS.CREATE, NET_FAMILY.INET, BPFBOX_ACTION.TAINT) # Creating an INET6 socket should fail with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([NET_PATH, 'create-inet6']) # Creating a UNIX socket should fail with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([NET_PATH, 'create-unix']) # Allow the creation of an INET6 socket Commands.add_net_rule(NET_PATH, NET_ACCESS.CREATE, NET_FAMILY.INET6, BPFBOX_ACTION.ALLOW) # Creating an INET6 socket should succeed subprocess.check_call([NET_PATH, 'create-inet6']) # Creating a UNIX socket should still fail with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([NET_PATH, 'create-unix']) # Allow the creation of a UNIX socket Commands.add_net_rule(NET_PATH, NET_ACCESS.CREATE, NET_FAMILY.UNIX, BPFBOX_ACTION.ALLOW) # Both should now succeed subprocess.check_call([NET_PATH, 'create-inet6']) subprocess.check_call([NET_PATH, 'create-unix'])
def test_link_allowed(bpf_program: BPFProgram, caplog, setup_testdir): Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox', FS_ACCESS.WRITE) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.LINK) subprocess.check_call([OPEN_PATH, 'link'])
def test_unlink_allowed(bpf_program: BPFProgram, caplog, setup_testdir): open('/tmp/bpfbox/e', 'a').close() Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox', FS_ACCESS.WRITE | FS_ACCESS.EXEC) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/e', FS_ACCESS.RM) subprocess.check_call([OPEN_PATH, 'unlink'])
def __call__(self, profile: str) -> int: if self.other_exe == 'self': other_exe = profile else: other_exe = self.other_exe return Commands.add_ipc_rule(profile, other_exe, self.access, self.action)
def test_unlink_no_write(bpf_program: BPFProgram, caplog, setup_testdir): open('/tmp/bpfbox/e', 'a').close() Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox', FS_ACCESS.EXEC) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/e', FS_ACCESS.RM) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([OPEN_PATH, 'unlink'])
def test_procfs_other_process(bpf_program: BPFProgram, caplog, setup_testdir): sleep_path = which('sleep') Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/proc', FS_ACCESS.EXEC) Commands.add_procfs_rule(OPEN_PATH, sleep_path, FS_ACCESS.READ | FS_ACCESS.EXEC) subprocess.check_call([OPEN_PATH, 'proc-self']) sleep_pid = subprocess.Popen([sleep_path, '10']).pid subprocess.check_call([OPEN_PATH, 'proc-other', str(sleep_pid)])
def test_parent_child(bpf_program: BPFProgram, caplog, setup_testdir): Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([OPEN_PATH, 'parent-child'])
def test_malicious_symlink_cannot_add_link(bpf_program: BPFProgram, caplog, setup_testdir): Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox', FS_ACCESS.WRITE) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([OPEN_PATH, 'malicious-symlink-read'])
def test_symlink_disallowed(bpf_program: BPFProgram, caplog, setup_testdir): Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox', FS_ACCESS.READ | FS_ACCESS.EXEC) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([OPEN_PATH, 'symlink'])
def test_create_dir_no_write(bpf_program: BPFProgram, caplog, setup_testdir): Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox', FS_ACCESS.EXEC) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([OPEN_PATH, 'create-dir'])
def test_fs_allow_write_and_append(bpf_program: BPFProgram, caplog, setup_testdir): Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.WRITE | FS_ACCESS.APPEND) subprocess.check_call([OPEN_PATH, 'simple-write-append']) subprocess.check_call([OPEN_PATH, 'simple-write-no-append'])
def test_fs_allow_append_only(bpf_program: BPFProgram, caplog, setup_testdir): Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.APPEND) subprocess.check_call([OPEN_PATH, 'simple-write-append']) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([OPEN_PATH, 'simple-write-no-append'])
def test_procfs(bpf_program: BPFProgram, caplog, setup_testdir): Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/proc', FS_ACCESS.EXEC) subprocess.check_call([OPEN_PATH, 'proc-self']) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([OPEN_PATH, 'proc-other', '1'])
def test_fs_allow_read_write(bpf_program: BPFProgram, caplog, setup_testdir): Commands.add_profile(OPEN_PATH, False) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ, BPFBOX_ACTION.TAINT) Commands.add_fs_rule(OPEN_PATH, '/tmp/bpfbox/a', FS_ACCESS.READ | FS_ACCESS.WRITE) subprocess.check_call([OPEN_PATH, 'simple-read']) subprocess.check_call([OPEN_PATH, 'simple-read-and-write']) subprocess.check_call([OPEN_PATH, 'simple-read-and-readwrite'])
def test_ipc_usr1_self(bpf_program: BPFProgram, caplog): Commands.add_profile(IPC_PATH, False) Commands.add_ipc_rule(IPC_PATH, IPC_PATH, IPC_ACCESS.SIGCHECK, BPFBOX_ACTION.TAINT) rc = subprocess.Popen([IPC_PATH, 'usr1-self']).wait() assert rc == 1 Commands.add_ipc_rule(IPC_PATH, IPC_PATH, IPC_ACCESS.SIGMISC) rc = subprocess.Popen([IPC_PATH, 'usr1-self']).wait() assert rc == -signal.SIGUSR1
def test_ipc_check_self(bpf_program: BPFProgram, caplog): Commands.add_profile(IPC_PATH, False) Commands.add_ipc_rule(IPC_PATH, IPC_PATH, IPC_ACCESS.SIGCHECK, BPFBOX_ACTION.TAINT) rc = subprocess.Popen([IPC_PATH, 'check-self']).wait() assert rc == 1 Commands.add_ipc_rule(IPC_PATH, IPC_PATH, IPC_ACCESS.SIGCHECK) rc = subprocess.Popen([IPC_PATH, 'check-self']).wait() assert rc == 0
def test_net_connect_rules(bpf_program: BPFProgram, caplog): Commands.add_profile(NET_PATH, False) Commands.add_net_rule(NET_PATH, NET_ACCESS.CREATE, NET_FAMILY.INET, BPFBOX_ACTION.TAINT) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([NET_PATH, 'inet-create-and-connect']) Commands.add_net_rule(NET_PATH, NET_ACCESS.CREATE, NET_FAMILY.INET6, BPFBOX_ACTION.ALLOW) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([NET_PATH, 'inet-create-and-connect']) Commands.add_net_rule(NET_PATH, NET_ACCESS.CONNECT, NET_FAMILY.INET6, BPFBOX_ACTION.ALLOW) subprocess.check_call([NET_PATH, 'inet-create-and-connect'])
def test_net_socketpair(bpf_program: BPFProgram, caplog): Commands.add_profile(NET_PATH, False) Commands.add_net_rule(NET_PATH, NET_ACCESS.CREATE, NET_FAMILY.INET, BPFBOX_ACTION.TAINT) with pytest.raises(subprocess.CalledProcessError): subprocess.check_call([NET_PATH, 'create-unix-socketpair']) Commands.add_net_rule(NET_PATH, NET_ACCESS.CREATE, NET_FAMILY.UNIX, BPFBOX_ACTION.ALLOW) subprocess.check_call([NET_PATH, 'create-unix-socketpair'])
def test_ipc_check_target(bpf_program: BPFProgram, caplog): sleep_path = which('sleep') Commands.add_profile(IPC_PATH, False) Commands.add_ipc_rule(IPC_PATH, IPC_PATH, IPC_ACCESS.SIGCHECK, BPFBOX_ACTION.TAINT) target_pid = subprocess.Popen([sleep_path, '10']).pid rc = subprocess.Popen([IPC_PATH, 'check-target', str(target_pid)]).wait() assert rc == 1 Commands.add_ipc_rule(IPC_PATH, sleep_path, IPC_ACCESS.SIGCHECK) target_pid = subprocess.Popen([sleep_path, '10']).pid rc = subprocess.Popen([IPC_PATH, 'check-target', str(target_pid)]).wait() assert rc == 0
def test_ipc_stop_self(bpf_program: BPFProgram, caplog): Commands.add_profile(IPC_PATH, False) Commands.add_ipc_rule(IPC_PATH, IPC_PATH, IPC_ACCESS.SIGCHECK, BPFBOX_ACTION.TAINT) p = subprocess.Popen([IPC_PATH, 'stop-self']) try: rc = p.wait(1) except subprocess.TimeoutExpired: os.kill(p.pid, signal.SIGCONT) rc = p.wait(1) assert rc == 1 Commands.add_ipc_rule(IPC_PATH, IPC_PATH, IPC_ACCESS.SIGSTOP) p = subprocess.Popen([IPC_PATH, 'stop-self']) try: rc = p.wait(1) except subprocess.TimeoutExpired: os.kill(p.pid, signal.SIGCONT) rc = p.wait(1) assert rc == 0
def test_ipc_stop_target(bpf_program: BPFProgram, caplog): sleep_path = which('sleep') Commands.add_profile(IPC_PATH, False) Commands.add_ipc_rule(IPC_PATH, IPC_PATH, IPC_ACCESS.SIGCHECK, BPFBOX_ACTION.TAINT) target_pid = subprocess.Popen([sleep_path, '10']).pid rc = subprocess.Popen([IPC_PATH, 'stop-target', str(target_pid)]).wait() try: os.kill(target_pid, signal.SIGCONT) except: pass assert rc == 1 Commands.add_ipc_rule(IPC_PATH, sleep_path, IPC_ACCESS.SIGSTOP) target_pid = subprocess.Popen([sleep_path, '10']).pid rc = subprocess.Popen([IPC_PATH, 'stop-target', str(target_pid)]).wait() try: os.kill(target_pid, signal.SIGCONT) except: pass assert rc == 0