Skip to content

Commit

Permalink
tracee-ebpf: add event to detect hooked syscalls (aquasecurity#1394)
Browse files Browse the repository at this point in the history
  • Loading branch information
itamarmaouda101 authored Apr 10, 2022
1 parent fc5c95f commit 44c3fb1
Show file tree
Hide file tree
Showing 11 changed files with 357 additions and 15 deletions.
29 changes: 15 additions & 14 deletions cmd/tracee-ebpf/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,25 @@ func main() {
// for the rest of execution, use this debug mode value
debug := flags.DebugModeEnabled()

// OS release information

OSInfo, err := helpers.GetOSInfo()
if err != nil {
if debug {
fmt.Fprintf(os.Stderr, "OSInfo: warning: os-release file could not be found\n(%v)\n", err) // only to be enforced when BTF needs to be downloaded, later on
fmt.Fprintf(os.Stdout, "OSInfo: %v: %v\n", helpers.OS_KERNEL_RELEASE, OSInfo.GetOSReleaseFieldValue(helpers.OS_KERNEL_RELEASE))
}
} else if debug {
for k, v := range OSInfo.GetOSReleaseAllFieldValues() {
fmt.Fprintf(os.Stdout, "OSInfo: %v: %v\n", k, v)
}
}

cfg := tracee.Config{
PerfBufferSize: c.Int("perf-buffer-size"),
BlobPerfBufferSize: c.Int("blob-perf-buffer-size"),
Debug: debug,
OSInfo: OSInfo,
}

cacheSlice := c.StringSlice("cache")
Expand Down Expand Up @@ -163,20 +178,6 @@ func main() {
}
}

// OS release information

OSInfo, err := helpers.GetOSInfo()
if err != nil {
if debug {
fmt.Fprintf(os.Stderr, "OSInfo: warning: os-release file could not be found\n(%v)\n", err) // only to be enforced when BTF needs to be downloaded, later on
fmt.Fprintf(os.Stdout, "OSInfo: %v: %v\n", helpers.OS_KERNEL_RELEASE, OSInfo.GetOSReleaseFieldValue(helpers.OS_KERNEL_RELEASE))
}
} else if debug {
for k, v := range OSInfo.GetOSReleaseAllFieldValues() {
fmt.Fprintf(os.Stdout, "OSInfo: %v: %v\n", k, v)
}
}

// decide BTF & BPF files to use based on kconfig, release & environment
err = prepareBpfObject(&cfg, kernelConfig, OSInfo)
if err != nil {
Expand Down
18 changes: 18 additions & 0 deletions pkg/bufferdecoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,24 @@ func (decoder *EbpfDecoder) DecodeIntArray(msg []int32, size uint32) error {
return nil
}

// DecodeUint64Array translate from the decoder buffer, starting from the decoder cursor, to msg, size * 8 bytes (in order to get int64).
func (decoder *EbpfDecoder) DecodeUint64Array(msg *[]uint64) error {
var arrLen uint8
err := decoder.DecodeUint8(&arrLen)
if err != nil {
return fmt.Errorf("error reading ulong array number of elements: %v", err)
}
for i := 0; i < int(arrLen); i++ {
var element uint64
err := decoder.DecodeUint64(&element)
if err != nil {
return fmt.Errorf("can't read element %d uint64 from buffer: %s", i, err)
}
*msg = append(*msg, element)
}
return nil
}

// DecodeSlimCred translates data from the decoder buffer, starting from the decoder cursor, to SlimCred struct.
func (decoder *EbpfDecoder) DecodeSlimCred(slimCred *SlimCred) error {
offset := decoder.cursor
Expand Down
11 changes: 11 additions & 0 deletions pkg/bufferdecoder/eventsreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const (
u16T
credT
intArr2T
uint64ArrT
)

// These types don't match the ones defined in the ebpf code since they are not being used by syscalls arguments.
Expand Down Expand Up @@ -154,6 +155,14 @@ func ReadArgFromBuff(ebpfMsgDecoder *EbpfDecoder, params []trace.ArgMeta) (trace
return argMeta, nil, fmt.Errorf("error reading int elements: %v", err)
}
res = intArray
case uint64ArrT:
ulongArray := make([]uint64, 0, 0)
err := ebpfMsgDecoder.DecodeUint64Array(&ulongArray)
if err != nil {
return argMeta, nil, fmt.Errorf("error reading ulong elements: %v", err)
}
res = ulongArray

default:
// if we don't recognize the arg type, we can't parse the rest of the buffer
return argMeta, nil, fmt.Errorf("error unknown arg type %v", argType)
Expand Down Expand Up @@ -202,6 +211,8 @@ func GetParamType(paramType string) ArgType {
return credT
case "umode_t":
return u16T
case "unsigned long[]":
return uint64ArrT
default:
// Default to pointer (printed as hex) for unsupported types
return pointerT
Expand Down
9 changes: 9 additions & 0 deletions pkg/bufferdecoder/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,12 @@ type SlimCred struct {
func (s SlimCred) GetSizeBytes() uint32 {
return 80
}

type HookedSyscallData struct {
SyscallName string
ModuleOwner string
}
type SymbolData struct {
SymbolName string
SymbolAddress string
}
118 changes: 117 additions & 1 deletion pkg/ebpf/c/tracee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ Copyright (C) Aqua Security inc.
#define U16_T 14UL
#define CRED_T 15UL
#define INT_ARR_2_T 16UL
#define UINT64_ARR_T 17UL
#define TYPE_MAX 255UL

#if defined(bpf_target_x86)
Expand Down Expand Up @@ -200,7 +201,8 @@ Copyright (C) Aqua Security inc.
#define CALL_USERMODE_HELPER 1037
#define DIRTY_PIPE_SPLICE 1038
#define DEBUGFS_CREATE_FILE 1039
#define MAX_EVENT_ID 1040
#define PRINT_SYSCALL_TABLE 1040
#define MAX_EVENT_ID 1041

#define NET_PACKET 4000

Expand Down Expand Up @@ -299,6 +301,10 @@ Copyright (C) Aqua Security inc.
#define MAX_BIN_CHUNKS 110
#endif

#define IOCTL_FETCH_SYSCALLS 65 // randomly picked number for ioctl cmd
#define NUMBER_OF_SYSCALLS_TO_CHECK_X86 18
#define NUMBER_OF_SYSCALLS_TO_CHECK_ARM 14

/*================================ eBPF KCONFIGs =============================*/

#ifdef CORE
Expand Down Expand Up @@ -627,6 +633,7 @@ BPF_HASH(process_tree_map, u32, u32); // filter events by the
BPF_HASH(process_context_map, u32, process_context_t); // holds the process_context data for every tid
BPF_HASH(network_config, u32, int); // holds the network config for each iface
BPF_HASH(ksymbols_map, ksym_name_t, u64) // holds the addresses of some kernel symbols
BPF_HASH(syscalls_to_check_map, int, u64); // syscalls to discover
BPF_LRU_HASH(sock_ctx_map, u64, net_ctx_ext_t); // socket address to process context
BPF_LRU_HASH(network_map, local_net_id_t, net_ctx_t); // network identifier to process context
BPF_ARRAY(config_map, u32, 4); // various configurations
Expand Down Expand Up @@ -1585,6 +1592,51 @@ static __always_inline int save_str_to_buf(event_data_t *data, void *ptr, u8 ind
return 0;
}

static __always_inline int save_u64_arr_to_buf(event_data_t *data, const u64 __user *ptr,int len , u8 index){
// Data saved to submit buf: [index][u64 count][u64 1][u64 2][u64 3]...
u8 elem_num = 0;
// Save argument index
data->submit_p->buf[(data->buf_off) & (MAX_PERCPU_BUFSIZE-1)] = index;

// Save space for number of elements (1 byte)
u32 orig_off = data->buf_off+1;
data->buf_off += 2;

#pragma unroll
for (int i = 0; i < len; i++) {
u64 element = 0;
int err = bpf_probe_read(&element, sizeof(u64), &ptr[i]);
if (err !=0)
goto out;
if (data->buf_off > MAX_PERCPU_BUFSIZE - sizeof(u64) )
// not enough space - return
goto out;

void *addr = &(data->submit_p->buf[data->buf_off ]);
int sz = bpf_probe_read(addr, sizeof(u64), (void *)&element);
if (sz == 0) {
elem_num++;
if (data->buf_off > MAX_PERCPU_BUFSIZE )
// Satisfy validator
goto out;

data->buf_off += sizeof(u64);
continue;
} else {
goto out;
}
}

goto out;

out:
// save number of elements in the array
data->submit_p->buf[orig_off & (MAX_PERCPU_BUFSIZE-1)] = elem_num;
data->context.argnum++;

return 1;
}

static __always_inline int save_str_arr_to_buf(event_data_t *data, const char __user *const __user *ptr, u8 index)
{
// Data saved to submit buf: [index][string count][str1 size][str1][str2 size][str2]...
Expand Down Expand Up @@ -2825,6 +2877,70 @@ int BPF_KPROBE(trace_do_exit)
return events_perf_submit(&data, DO_EXIT, code);
}

/* invoke_print_syscall_table_event submit to the buff the syscalls function handlers address from the syscall table.
* the syscalls are strode in map which is syscalls_to_check_map and the syscall-table address is stored in the kernel_symbols map.
*/
static __always_inline void invoke_print_syscall_table_event(event_data_t *data){
int key = 0;
u64 *table_ptr = bpf_map_lookup_elem(&syscalls_to_check_map, (void *)&key);
if (table_ptr == NULL){
return ;
}

char syscall_table[15] = "sys_call_table";
unsigned long *syscall_table_addr = (unsigned long*) get_symbol_addr(syscall_table);
u64 idx;
u64* syscall_num_p; // pointer to syscall_number
u64 syscall_num;
unsigned long syscall_addr = 0;
int monitored_syscalls_amount = 0;
#if defined(bpf_target_x86)
monitored_syscalls_amount = NUMBER_OF_SYSCALLS_TO_CHECK_X86;
u64 syscall_address[NUMBER_OF_SYSCALLS_TO_CHECK_X86];
#elif defined(bpf_target_arm64)
monitored_syscalls_amount = NUMBER_OF_SYSCALLS_TO_CHECK_ARM;
u64 syscall_address[NUMBER_OF_SYSCALLS_TO_CHECK_ARM];
#else

return
#endif

__builtin_memset(syscall_address, 0, sizeof(syscall_address));
// the map should look like [syscall number 1][syscall number 2][syscall number 3]...
#pragma unroll
for(int i =0; i < monitored_syscalls_amount; i++){
idx = i;
syscall_num_p = bpf_map_lookup_elem(&syscalls_to_check_map, (void *)&idx);
if (syscall_num_p == NULL){
continue;
}
syscall_num = (u64)*syscall_num_p;
syscall_addr = READ_KERN(syscall_table_addr[syscall_num]);
if (syscall_addr == 0){
return;
}
syscall_address[i] = syscall_addr;
}
save_u64_arr_to_buf(data, (const u64 *)syscall_address, monitored_syscalls_amount, 0);
events_perf_submit(data, PRINT_SYSCALL_TABLE, 0);
}

SEC("kprobe/security_file_ioctl")
int BPF_KPROBE(trace_tracee_trigger_event )
{
event_data_t data = {};

if (!init_event_data(&data, ctx))
return 0;

unsigned int cmd = PT_REGS_PARM2(ctx);

if (cmd == IOCTL_FETCH_SYSCALLS && get_config(CONFIG_TRACEE_PID) == data.context.host_pid){
invoke_print_syscall_table_event(&data);
}
return 0;
}

// include/trace/events/cgroup.h:
// TP_PROTO(struct cgroup *dst_cgrp, const char *path, struct task_struct *task, bool threadgroup)
SEC("raw_tracepoint/cgroup_attach_task")
Expand Down
21 changes: 21 additions & 0 deletions pkg/ebpf/events_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -905,3 +905,24 @@ const (
sys32process_mrelease int32 = 448
sys32undefined int32 = 10000
)

var syscallsToCheck = []int{
0, // read
1, // write
2, // open
3, // close
16, // ioctl
41, // socket
44, // sendto
45, // recvfrom
46, // sendmsg
47, // recvmsg
59, // execve
62, // kill
78, // getdents
101, // ptrace
217, // getdents64
257, // openat
321, // bpf
322, // execveat
}
19 changes: 19 additions & 0 deletions pkg/ebpf/events_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -965,3 +965,22 @@ const (
sys32fadvise64
sys32sync_file_range
)

var syscallsToCheck = []int{
29, // ioctl
56, // openat
57, // close
61, // getdents64
63, // read
64, // write
117, // ptrace
129, // kill
198, // socket
281, // execveat
206, // sendto
207, // recvfrom
211, // sendmsg
212, // recvmsg
221, // execve
280, // bpf
}
25 changes: 25 additions & 0 deletions pkg/ebpf/events_definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const (
CallUsermodeHelperEventID
DirtyPipeSpliceEventID
DebugfsCreateFile
PrintSyscallTableEventID
MaxCommonEventID
)

Expand All @@ -94,6 +95,7 @@ const (
ContainerCreateEventID
ContainerRemoveEventID
ExistingContainerEventID
DetectHookedSyscallsEventID
MaxUserSpaceEventID
)

Expand Down Expand Up @@ -6300,4 +6302,27 @@ var EventsDefinitions = map[int32]EventDefinition{
{Type: "u64", Name: "proc_ops_addr"},
},
},
PrintSyscallTableEventID: {
ID32Bit: sys32undefined,
Name: "print_syscall_table",
Probes: []probe{
{event: "security_file_ioctl", attach: kprobe, fn: "trace_tracee_trigger_event"},
},
Dependencies: dependencies{ksymbols: []string{"sys_call_table"}},
Sets: []string{},
Params: []trace.ArgMeta{
{Type: "unsigned long[]", Name: "syscalls_addresses"},
},
},
DetectHookedSyscallsEventID: {
ID32Bit: sys32undefined,
Name: "detect_hooked_syscalls",
Dependencies: dependencies{
events: []eventDependency{{eventID: FinitModuleEventID}, {eventID: PrintSyscallTableEventID}, {eventID: InitModuleEventID}},
},
Sets: []string{},
Params: []trace.ArgMeta{
{Type: "HookedSyscallData[]", Name: "hooked_syscalls"},
},
},
}
Loading

0 comments on commit 44c3fb1

Please sign in to comment.