Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat<observability>: Added kprobe for tracing capabilities in ebpfMonitor #1596

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 62 additions & 14 deletions KubeArmor/BPF/system_monitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@
#define PTRACE_REQ_T 23UL
#define MOUNT_FLAG_T 24UL
#define UMOUNT_FLAG_T 25UL
#define CAPABLE_T 26UL
#define CAPABLE_EFF 27UL

#define MAX_ARGS 6
#define ENC_ARG_TYPE(n, type) type << (8 * n)
Expand Down Expand Up @@ -174,6 +176,7 @@ enum
_SYS_PTRACE = 101,
// lsm
_SECURITY_BPRM_CHECK = 352,
_CAPABLE = 464,

// accept/connect
_TCP_CONNECT = 400,
Expand All @@ -191,18 +194,6 @@ struct mnt_namespace
struct ns_common ns;
};

struct fs_struct {
struct path pwd;
};

struct tty_struct {
char name[64];
};

struct signal_struct {
struct tty_struct *tty;
};

struct mount
{
struct hlist_node mnt_hash;
Expand Down Expand Up @@ -242,7 +233,7 @@ typedef struct __attribute__((__packed__)) sys_context
__type(key, _key_type); \
__type(value, _value_type); \
__uint(max_entries, _max_entries); \
} _name SEC(".maps");
} _name SEC(".maps");

#define BPF_HASH(_name, _key_type, _value_type) \
BPF_MAP(_name, BPF_MAP_TYPE_HASH, _key_type, _value_type, 10240)
Expand Down Expand Up @@ -912,12 +903,21 @@ static __always_inline int save_args_to_buffer(u64 types, args_t *args)
case UNLINKAT_FLAG_T:
save_to_buffer(bufs_p, (void *)&(args->args[i]), sizeof(int), UNLINKAT_FLAG_T);
break;
case CAPABLE_T:
save_to_buffer(bufs_p, (void *)&(args->args[i]), sizeof(int), CAPABLE_T);
break;
case CAPABLE_EFF:
save_to_buffer(bufs_p, (void *)&(args->args[i]), sizeof(long), CAPABLE_EFF);
break;
}
}

return 0;
}

static __always_inline kernel_cap_t get_task_cap_effective (struct task_struct *task)
{
return BPF_CORE_READ(task, cred, cap_effective);
}
static __always_inline int events_perf_submit(struct pt_regs *ctx)
{
bufs_t *bufs_p = get_buffer(DATA_BUF_TYPE);
Expand Down Expand Up @@ -982,6 +982,7 @@ static __always_inline u32 init_context(sys_context_t *context)
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();


context->ts = bpf_ktime_get_ns();

context->host_ppid = get_task_ppid(task);
Expand Down Expand Up @@ -2057,4 +2058,51 @@ int kretprobe__inet_csk_accept(struct pt_regs *ctx)

return 0;
}
SEC("kprobe/cap_capable")
int kprobe__cap_capable(struct pt_regs *ctx){
if (skip_syscall())
return 0;

if (get_kubearmor_config(_ENFORCER_BPFLSM) && drop_syscall(_CAPS_PROBE))
{
return 0;
}
sys_context_t context = {};
args_t args = {};

int cap = (int)PT_REGS_PARM3(ctx);

struct cred *cred ;
bpf_probe_read(&cred, sizeof(cred), (void *)&PT_REGS_PARM1(ctx));

u32 *off = get_buffer_offset(EXEC_BUF_TYPE);
if (off == NULL)
return -1;
//

// u64 types = ARG_TYPE0(CAPABLE_T);
init_context(&context);

context.event_id = _CAPABLE;
context.argnum = 1;
context.retval = 0;
kernel_cap_t current_cap_effective = (kernel_cap_t)BPF_CORE_READ(cred, cap_effective);
u64 current_effective_mask = *(u64 *)&current_cap_effective;

args.args[0] = (unsigned long)cap;
// args.args[1] = (unsigned long)current_effective_mask;
bpf_printk("size %d ", sizeof(args.args[1]));
set_buffer_offset(DATA_BUF_TYPE, sizeof(sys_context_t));

bufs_t *bufs_p = get_buffer(DATA_BUF_TYPE);
if (bufs_p == NULL)
return 0;
save_context_to_buffer(bufs_p, (void *)&context);
save_to_buffer(bufs_p, (void *)&cap, sizeof(int), INT_T);
// save_to_buffer(bufs_p, (void *)&current_effective_mask, sizeof(int64_t), CAPABLE_EFF);

events_perf_submit(ctx);

return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
16 changes: 16 additions & 0 deletions KubeArmor/monitor/logUpdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,22 @@ func (mon *SystemMonitor) UpdateLogs() {
log.Resource = ""
log.Data = "syscall=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " fd=" + fd

case Capable:

var cap int32
capeff := ""
// add arguments
if val, ok := msg.ContextArgs[0].(int32); ok {
cap = int32(val)
}
// if val, ok := msg.ContextArgs[1].(string); ok {
// capeff = val
// }
// fmt.Println(cap)
log.Resource = "cap=" + getCapabilityName(cap)
log.Operation = "Capabilities"
log.Data = "kprobe=" + GetSyscallName(int32(msg.ContextSys.EventID)) + " cap=" + getCapabilityName(cap) + "capeff=" + capeff

default:
continue
}
Expand Down
31 changes: 31 additions & 0 deletions KubeArmor/monitor/syscallParser.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const (
ptraceReqT uint8 = 23
mountFlagT uint8 = 24
umountFlagT uint8 = 25
capableT uint8 = 26
capableEff uint8 = 27
)

// ======================= //
Expand Down Expand Up @@ -95,6 +97,20 @@ func readUInt32BigendFromBuff(buff io.Reader) (uint32, error) {
return res, err
}

// readUInt32FromBuff Function
func readUInt64FromBuff(buff io.Reader) (uint64, error) {
var res uint64
err := binary.Read(buff, binary.LittleEndian, &res)
return res, err
}

// readUInt32BigendFromBuff Function
func readUInt64BigendFromBuff(buff io.Reader) (uint64, error) {
var res uint64
err := binary.Read(buff, binary.BigEndian, &res)
return res, err
}

// Min Function
func Min(a, b int) int {
if a < b {
Expand Down Expand Up @@ -944,6 +960,21 @@ func readArgFromBuff(dataBuff io.Reader) (interface{}, error) {
return nil, err
}
res = GetSocketType(t)
case capableT:
cap, err := readUInt32FromBuff(dataBuff)
if err != nil {
return nil, err
}
// fmt.Println(cap)
res = cap
case capableEff:
capeff, err := readUInt64FromBuff(dataBuff)
if err != nil {
return nil, err
}
hexCode := fmt.Sprintf("%X", capeff)
// fmt.Println(hexCode)
res = string(hexCode)
default:
return nil, fmt.Errorf("error unknown argument type %v", at)
}
Expand Down
9 changes: 7 additions & 2 deletions KubeArmor/monitor/systemMonitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ func (mon *SystemMonitor) InitBPF() error {
systemCalls := []string{"open", "openat", "execve", "execveat", "socket", "connect", "accept", "bind", "listen", "unlink", "unlinkat", "rmdir", "ptrace", "chown", "setuid", "setgid", "fchownat", "mount", "umount"}
// {category, event}
sysTracepoints := [][2]string{{"syscalls", "sys_exit_openat"}}
sysKprobes := []string{"do_exit", "security_bprm_check", "security_file_open", "security_path_mknod", "security_path_unlink", "security_path_rmdir", "security_ptrace_access_check"}
sysKprobes := []string{"do_exit", "security_bprm_check", "security_file_open", "security_path_mknod", "security_path_unlink", "security_path_rmdir", "security_ptrace_access_check", "cap_capable"}
netSyscalls := []string{"tcp_connect"}
netRetSyscalls := []string{"inet_csk_accept"}

Expand Down Expand Up @@ -579,7 +579,7 @@ func (mon *SystemMonitor) TraceSyscall() {
if errors.Is(err, perf.ErrClosed) {
// This should only happen when we call DestroyMonitor while terminating the process.
// Adding a Warn just in case it happens at runtime, to help debug
mon.Logger.Warnf("Perf Buffer closed, exiting TraceSyscall %s", err.Error())
mon.Logger.Warnf("Perf Buffer closed, exiting call %s", err.Error())
return
}
mon.Logger.Warnf("Perf Event Error : %s", err.Error())
Expand Down Expand Up @@ -903,7 +903,12 @@ func (mon *SystemMonitor) TraceSyscall() {
if len(args) != 2 {
continue
}
} else if ctx.EventID == Capable {
if len(args) != 1 {
continue
}
}

MonitorLock.Lock()
// push the context to the channel for logging
mon.ContextChan <- ContextCombined{ContainerID: containerID, ContextSys: ctx, ContextArgs: args}
Expand Down
Loading