Skip to content

Commit

Permalink
revert: revert recently changed hooked_syscall event (#3597)
Browse files Browse the repository at this point in the history
One might re-revert this once a fix for #3595 has been done.

Revert "chore(events): rename syscall hooking arguments (#3594)"
Revert "feat(events): refactor hooked_syscall event"
  • Loading branch information
rafaeldtinoco authored Oct 20, 2023
1 parent c0f4b93 commit 1ecedcc
Show file tree
Hide file tree
Showing 21 changed files with 379 additions and 1,196 deletions.
26 changes: 0 additions & 26 deletions docs/docs/events/builtin/extra/hooked_syscall.md

This file was deleted.

71 changes: 71 additions & 0 deletions docs/docs/events/builtin/extra/hooked_syscalls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# hooked_syscalls

## Intro
`hooked_syscalls` is an event that checks the selected syscalls for any syscall hooking.

## Description
The purpose of the `hooked_syscalls` event is to monitor for system call hooking in the Linux kernel. It verifies the function pointer of the system call to ensure it lies between the etext and stext addresses. This helps identify instances of kernel code modifications, often used for malicious activities such as hiding processes, files, or network connections.

The `hooked_syscalls` event checks either user-specified syscalls or a default list of syscalls depending on the architecture of the system, with a different list for amd64 and arm64 respectively.

## Arguments
* `check_syscalls`:`[]string`[U] - the syscall checked for syscall hooking. Can be used to specify selected syscalls or use the default ones.
The default syscalls for amd64 are:

`read`
`write`
`open`
`close`
`ioctl`
`socket`
`sendto`
`recvfrom`
`sendmsg`
`recvmsg`
`execve`
`kill`
`getdents`
`ptrace`
`getdents64`
`openat`
`bpf`
`execveat`

The default syscalls for arm64 are:
`ioctl`
`openat`
`close`
`getdents64`
`read`
`write`
`ptrace`
`kill`
`socket`
`execveat`
`sendto`
`recvfrom`
`sendmsg`
`recvmsg`
`execve`
`bpf`
* `hooked_syscalls`:`[]trace.HookedSymbolData` [K] - The hooked syscalls that were found along with their owners. `Hidden` owner means that the pointed function owner is not a part of the kernel modules list.
## Hooks
### Various system calls
#### Type
Uprobe
#### Purpose
Detection of syscall hooking.

## Example Use Case
The `hooked_syscalls` event could be used as part of a broader system integrity monitoring solution. For example, a security engineer could use it to raise alerts or run further investigations if unexpected syscall hooking activities are detected. This could aid in the early detection and mitigation of malware or rootkit infections.
Example:

```console
tracee -e hooked_syscalls.args.check_syscalls=<syscall>,<syscall>,...`
```

## Issues
The `check_syscalls` argument is used as a parameter to specify the syscalls to be checked. This will change in the future to be an event parameter.

## Related Events

4 changes: 2 additions & 2 deletions pkg/cmd/initialize/sigs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func Test_CreateEventsFromSigs(t *testing.T) {
newFakeSignature(
"fake_event_0",
[]string{
"hooked_syscall",
"hooked_syscalls",
},
),
},
Expand All @@ -43,7 +43,7 @@ func Test_CreateEventsFromSigs(t *testing.T) {
false, // syscall
[]string{"signatures", "default"}, // sets
events.NewDependencies(
[]events.ID{events.HookedSyscall},
[]events.ID{events.HookedSyscalls},
[]events.KSymbol{},
[]events.Probe{},
[]events.TailCall{},
Expand Down
1 change: 0 additions & 1 deletion pkg/ebpf/c/common/consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#define ARGS_BUF_SIZE 32000
#define SEND_META_SIZE 28

#define MAX_SYS_CALL_TABLE_SIZE 500
#define MAX_MEM_DUMP_SIZE 127


Expand Down
1 change: 0 additions & 1 deletion pkg/ebpf/c/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ BPF_ARRAY(file_write_path_filter, path_filter_t, 3); // filter fil
BPF_ARRAY(file_read_path_filter, path_filter_t, 3); // filter file read captures
BPF_ARRAY(file_type_filter, file_type_filter_t, 2); // filter file types
BPF_ARRAY(netconfig_map, netconfig_entry_t, 1); // network related configurations
BPF_ARRAY(expected_sys_call_table, syscall_table_entry_t, MAX_SYS_CALL_TABLE_SIZE); // expected addresses of sys call table
BPF_PERCPU_ARRAY(bufs, buf_t, MAX_BUFFERS); // percpu global buffer variables
BPF_PROG_ARRAY(prog_array, MAX_TAIL_CALL); // store programs for tail calls
BPF_PROG_ARRAY(prog_array_tp, MAX_TAIL_CALL); // store programs for tail calls
Expand Down
70 changes: 38 additions & 32 deletions pkg/ebpf/c/tracee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1468,40 +1468,30 @@ int BPF_KPROBE(trace_do_exit)
return events_perf_submit(&p, DO_EXIT, code);
}

statfunc void syscall_table_check(program_data_t *p)
{
char sys_call_table_symbol[15] = "sys_call_table";
u64 *sys_call_table = (u64 *) get_symbol_addr(sys_call_table_symbol);

int index = 0; // For the verifier

#pragma unroll
for (int i = 0; i < MAX_SYS_CALL_TABLE_SIZE; i++) {
index = i;
syscall_table_entry_t *expected_entry =
bpf_map_lookup_elem(&expected_sys_call_table, &index);
// uprobe_syscall_trigger submit to the buff the syscalls function handlers
// address from the syscall table. the syscalls are checked in user mode for hooks.

if (!expected_entry || expected_entry->address == 0) {
continue;
}

u64 effective_address;
bpf_probe_read(&effective_address, sizeof(u64), sys_call_table + index);
SEC("uprobe/trigger_syscall_event")
int uprobe_syscall_trigger(struct pt_regs *ctx)
{
u64 table_count = 0;
u64 caller_ctx_id = 0;

if (expected_entry->address != effective_address) {
reset_event_args(p);
save_to_submit_buf(&(p->event->args_buf), &index, sizeof(int), 0);
save_to_submit_buf(&(p->event->args_buf), &effective_address, sizeof(u64), 1);
// clang-format off
//
// Golang calling convention per architecture

events_perf_submit(p, SYSCALL_TABLE_CHECK, 0);
}
}
}
#if defined(bpf_target_x86)
caller_ctx_id = ctx->bx; // 1st arg
table_count = ctx->cx; // 2nd arg
#elif defined(bpf_target_arm64)
caller_ctx_id = ctx->user_regs.regs[1]; // 1st arg
table_count = ctx->user_regs.regs[2]; // 2nd arg
#else
return 0;
#endif
// clang-format on

// syscall_table_check
SEC("uprobe/syscall_table_check")
int uprobe_syscall_table_check(struct pt_regs *ctx)
{
program_data_t p = {};
if (!init_program_data(&p, ctx))
return 0;
Expand All @@ -1515,9 +1505,25 @@ int uprobe_syscall_table_check(struct pt_regs *ctx)
p.event->context.syscall = NO_SYSCALL;
p.event->context.matched_policies = ULLONG_MAX;

syscall_table_check(&p);
char syscall_table_sym[15] = "sys_call_table";
u64 *syscall_table_addr = (u64 *) get_symbol_addr(syscall_table_sym);

return 0;
if (unlikely(syscall_table_addr == 0))
return 0;

// Get per-cpu string buffer
buf_t *string_p = get_buf(STRING_BUF_IDX);
if (string_p == NULL)
return 0;

u64 table_size = table_count * sizeof(u64);
if ((table_size > MAX_PERCPU_BUFSIZE) || (table_size <= 0)) // verify no wrap occurred
return 0;

save_u64_arr_to_buf(&p.event->args_buf, (const u64 *) syscall_table_addr, table_count, 0);
save_to_submit_buf(&p.event->args_buf, (void *) &caller_ctx_id, sizeof(uint64_t), 1);

return events_perf_submit(&p, PRINT_SYSCALL_TABLE, 0);
}

SEC("uprobe/trigger_seq_ops_event")
Expand Down
6 changes: 1 addition & 5 deletions pkg/ebpf/c/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ enum event_id_e
CALL_USERMODE_HELPER,
DIRTY_PIPE_SPLICE,
DEBUGFS_CREATE_FILE,
SYSCALL_TABLE_CHECK,
PRINT_SYSCALL_TABLE,
DEBUGFS_CREATE_DIR,
DEVICE_ADD,
REGISTER_CHRDEV,
Expand Down Expand Up @@ -351,10 +351,6 @@ typedef struct netconfig_entry {
u32 capture_length; // amount of network packet payload to capture (pcap)
} netconfig_entry_t;

typedef struct syscall_table_entry {
u64 address;
} syscall_table_entry_t;

typedef struct args_buffer {
u8 argnum;
char args[ARGS_BUF_SIZE];
Expand Down
104 changes: 0 additions & 104 deletions pkg/ebpf/hooked_syscall_table.go

This file was deleted.

2 changes: 1 addition & 1 deletion pkg/ebpf/probes/probe_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func NewDefaultProbeGroup(module *bpf.Module, netEnabled bool) (*ProbeGroup, err
LoadElfPhdrs: NewTraceProbe(KProbe, "load_elf_phdrs", "trace_load_elf_phdrs"),
Filldir64: NewTraceProbe(KProbe, "filldir64", "trace_filldir64"),
TaskRename: NewTraceProbe(RawTracepoint, "task:task_rename", "tracepoint__task__task_rename"),
SyscallTableCheck: NewUprobe("syscall_table_check", "uprobe_syscall_table_check", binaryPath, "github.com/aquasecurity/tracee/pkg/ebpf.(*Tracee).triggerSyscallTableIntegrityCheckCall"),
PrintSyscallTable: NewUprobe("print_syscall_table", "uprobe_syscall_trigger", binaryPath, "github.com/aquasecurity/tracee/pkg/ebpf.(*Tracee).triggerSyscallsIntegrityCheckCall"),
HiddenKernelModuleSeeker: NewUprobe("hidden_kernel_module", "uprobe_lkm_seeker", binaryPath, "github.com/aquasecurity/tracee/pkg/ebpf.(*Tracee).triggerKernelModuleSeeker"),
HiddenKernelModuleVerifier: NewUprobe("hidden_kernel_module", "uprobe_lkm_seeker_submitter", binaryPath, "github.com/aquasecurity/tracee/pkg/ebpf.(*Tracee).triggerKernelModuleSubmitter"),
PrintNetSeqOps: NewUprobe("print_net_seq_ops", "uprobe_seq_ops_trigger", binaryPath, "github.com/aquasecurity/tracee/pkg/ebpf.(*Tracee).triggerSeqOpsIntegrityCheckCall"),
Expand Down
2 changes: 1 addition & 1 deletion pkg/ebpf/probes/probes.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const (
Filldir64
SecurityFilePermission
TaskRename
SyscallTableCheck
PrintSyscallTable
PrintNetSeqOps
SecurityInodeRename
DoSigaction
Expand Down
1 change: 1 addition & 0 deletions pkg/ebpf/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func (t *Tracee) registerEventProcessors() {
t.RegisterEventProcessor(events.DoInitModule, t.processDoInitModule)
t.RegisterEventProcessor(events.HookedProcFops, t.processHookedProcFops)
t.RegisterEventProcessor(events.PrintNetSeqOps, t.processTriggeredEvent)
t.RegisterEventProcessor(events.PrintSyscallTable, t.processTriggeredEvent)
t.RegisterEventProcessor(events.PrintMemDump, t.processTriggeredEvent)
t.RegisterEventProcessor(events.PrintMemDump, t.processPrintMemDump)

Expand Down
10 changes: 7 additions & 3 deletions pkg/ebpf/processor_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ func (t *Tracee) processSchedProcessExec(event *trace.Event) error {

// processDoFinitModule handles a do_finit_module event and triggers other hooking detection logic.
func (t *Tracee) processDoInitModule(event *trace.Event) error {
_, okSyscalls := t.eventsState[events.HookedSyscall]
_, okSyscalls := t.eventsState[events.HookedSyscalls]
_, okSeqOps := t.eventsState[events.HookedSeqOps]
_, okProcFops := t.eventsState[events.HookedProcFops]
_, okMemDump := t.eventsState[events.PrintMemDump]
Expand All @@ -233,8 +233,12 @@ func (t *Tracee) processDoInitModule(event *trace.Event) error {
if err != nil {
return errfmt.WrapError(err)
}
if okSyscalls && expectedSyscallTableInit {
t.triggerSyscallTableIntegrityCheckCall()
if okSyscalls {
// Trigger syscalls hooking detection
err = t.triggerSyscallsIntegrityCheck(*event)
if err != nil {
logger.Warnw("hooked_syscalls returned an error", "error", err)
}
}
if okSeqOps {
// Trigger seq_ops hooking detection
Expand Down
Loading

0 comments on commit 1ecedcc

Please sign in to comment.