Skip to content

Commit

Permalink
events: create fops hooking on /proc event (#1718)
Browse files Browse the repository at this point in the history
File operation hooking is done by replacing function pointers in the fops struct
This can be used to hide files and processes.
This commit adds a detection to detect such efforts.
EPIC: #1703

Co-authored-by: itamar maouda <[email protected]>
  • Loading branch information
AsafEitani and itamarmaouda101 authored Jun 1, 2022
1 parent bee0270 commit 3e1990b
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 1 deletion.
2 changes: 1 addition & 1 deletion pkg/bufferdecoder/eventsreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func GetParamType(paramType string) ArgType {
return credT
case "umode_t":
return u16T
case "unsigned long[]":
case "unsigned long[]", "[]trace.HookedSymbolData":
return uint64ArrT
default:
// Default to pointer (printed as hex) for unsupported types
Expand Down
2 changes: 2 additions & 0 deletions pkg/ebpf/c/missing_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@

#define TASK_COMM_LEN 16

#define PROC_SUPER_MAGIC 0x9fa0

// include/uapi/linux/const.h
#define __AC(X, Y) (X##Y)
#define _AC(X, Y) __AC(X, Y)
Expand Down
55 changes: 55 additions & 0 deletions pkg/ebpf/c/tracee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Copyright (C) Aqua Security inc.

#ifndef CORE
#include <uapi/linux/magic.h>
#include <uapi/linux/ptrace.h>
#include <uapi/linux/in.h>
#include <uapi/linux/in6.h>
Expand Down Expand Up @@ -230,6 +231,7 @@ enum event_id_e
DO_INIT_MODULE,
SOCKET_ACCEPT,
LOAD_ELF_PHDRS,
HOOKED_PROC_FOPS,
MAX_EVENT_ID,

// Net events IDs
Expand Down Expand Up @@ -1329,6 +1331,21 @@ static __always_inline struct sockaddr_un get_unix_sock_addr(struct unix_sock *s

// INTERNAL: CONFIG --------------------------------------------------------------------------------

static __always_inline struct inode *get_inode_from_file(struct file *file)
{
return READ_KERN(file->f_inode);
}

static __always_inline struct super_block *get_super_block_from_inode(struct inode *f_inode)
{
return READ_KERN(f_inode->i_sb);
}

static __always_inline unsigned long get_s_magic_from_super_block(struct super_block *i_sb)
{
return READ_KERN(i_sb->s_magic);
}

static __always_inline int get_config(u32 key)
{
u32 *config = bpf_map_lookup_elem(&config_map, &key);
Expand Down Expand Up @@ -5296,6 +5313,44 @@ int BPF_KPROBE(trace_load_elf_phdrs)
return 0;
}

SEC("kprobe/security_file_permission")
int BPF_KPROBE(trace_security_file_permission)
{
struct file *file = (struct file *) PT_REGS_PARM1(ctx);
if (file == NULL)
return 0;
struct inode *f_inode = get_inode_from_file(file);
struct super_block *i_sb = get_super_block_from_inode(f_inode);
unsigned long s_magic = get_s_magic_from_super_block(i_sb);

// Only check procfs entries
if (s_magic != PROC_SUPER_MAGIC) {
return 0;
}

event_data_t data = {};
if (!init_event_data(&data, ctx))
return 0;

if (!should_trace(&data.context))
return 0;

struct file_operations *fops = (struct file_operations *) READ_KERN(f_inode->i_fop);
if (fops == NULL)
return 0;

unsigned long iterate_shared_addr = (unsigned long) READ_KERN(fops->iterate_shared);
unsigned long iterate_addr = (unsigned long) READ_KERN(fops->iterate);
if (iterate_addr == 0 && iterate_shared_addr == 0)
return 0;

unsigned long fops_addresses[3] = {(unsigned long) fops, iterate_shared_addr, iterate_addr};

save_u64_arr_to_buf(&data, (const u64 *) fops_addresses, 3, 0);
events_perf_submit(&data, HOOKED_PROC_FOPS, 0);
return 0;
}

static __always_inline bool
skb_revalidate_data(struct __sk_buff *skb, uint8_t **head, uint8_t **tail, const u32 offset)
{
Expand Down
9 changes: 9 additions & 0 deletions pkg/ebpf/c/vmlinux.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,13 @@ struct path {

typedef unsigned int fmode_t;

struct dir_context {
};
struct file_operations {
int (*iterate_shared)(struct file *, struct dir_context *);
int (*iterate)(struct file *, struct dir_context *);
};

struct file {
struct path f_path;
struct inode *f_inode;
Expand Down Expand Up @@ -609,10 +616,12 @@ struct inode {
long unsigned int i_ino;
struct timespec64 i_ctime;
loff_t i_size;
struct file_operations *i_fop;
};

struct super_block {
dev_t s_dev;
unsigned long s_magic;
};

struct mm_struct {
Expand Down
19 changes: 19 additions & 0 deletions pkg/ebpf/events_definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const (
DoInitModuleEventID
SocketAcceptEventID
LoadElfPhdrsEventID
HookedProcFopsEventID
MaxCommonEventID
)

Expand Down Expand Up @@ -5720,4 +5721,22 @@ var EventsDefinitions = map[int32]EventDefinition{
{Type: "unsigned long", Name: "inode"},
},
},
HookedProcFopsEventID: {
ID32Bit: sys32undefined,
Name: "hooked_proc_fops",
Probes: []probe{
{event: "security_file_permission", attach: kprobe, fn: "trace_security_file_permission"},
},
Dependencies: dependencies{
ksymbols: []string{"_stext", "_etext"},
events: []eventDependency{
{eventID: FinitModuleEventID},
{eventID: InitModuleEventID},
},
},
Sets: []string{},
Params: []trace.ArgMeta{
{Type: "[]trace.HookedSymbolData", Name: "hooked_fops_pointers"},
},
},
}
36 changes: 36 additions & 0 deletions pkg/ebpf/events_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ func (t *Tracee) deleteProcInfoDelayed(hostTid int) {
t.procInfo.DeleteElement(hostTid)
}

const (
StructFopsPointer int = iota
IterateShared
Iterate
)

func (t *Tracee) processEvent(event *trace.Event) error {
switch int32(event.EventID) {

Expand Down Expand Up @@ -343,6 +349,36 @@ func (t *Tracee) processEvent(event *trace.Event) error {
case InitModuleEventID:
t.invokeIoctlTriggeredEvents()
t.updateKallsyms()

case HookedProcFopsEventID:
fopsAddresses, err := getEventArgUlongArrVal(event, "hooked_fops_pointers")
if err != nil || fopsAddresses == nil {
return fmt.Errorf("error parsing hooked_proc_fops args: %w", err)
}
hookedFops := make([]trace.HookedSymbolData, 0)
for idx, addr := range fopsAddresses {
inTextSeg, err := t.kernelSymbols.TextSegmentContains(addr)
if err != nil {
return fmt.Errorf("error checking kernel address: %v", err)
}
if !inTextSeg {
hookingFunction := parseSymbol(addr, t.kernelSymbols)
if hookingFunction.Owner == "system" {
continue
}
functionName := "unknown"
switch idx {
case StructFopsPointer:
functionName = "struct file_operations pointer"
case IterateShared:
functionName = "iterate_shared"
case Iterate:
functionName = "iterate"
}
hookedFops = append(hookedFops, trace.HookedSymbolData{SymbolName: functionName, ModuleOwner: hookingFunction.Owner})
}
}
event.Args[0].Value = hookedFops
}

return nil
Expand Down

0 comments on commit 3e1990b

Please sign in to comment.