Skip to content

Commit

Permalink
Merge pull request #1743 from daemon1024/special-presets
Browse files Browse the repository at this point in the history
feat: special presets (handle fileless exec)
  • Loading branch information
daemon1024 authored Jan 10, 2025
2 parents ca59e4a + 4e529fe commit b88c8f5
Show file tree
Hide file tree
Showing 63 changed files with 3,027 additions and 298 deletions.
126 changes: 126 additions & 0 deletions KubeArmor/BPF/anonmapexec.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// +build ignore
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright 2023 Authors of KubeArmor */

#include "shared.h"

#define PROT_EXEC 0x4 /* page can be executed */
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS

#define S_IFIFO 0010000

struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
} events SEC(".maps");

typedef struct {
u64 ts;

u32 pid_id;
u32 mnt_id;

u32 host_ppid;
u32 host_pid;

u32 ppid;
u32 pid;
u32 uid;

u32 event_id;
s64 retval;

u8 comm[TASK_COMM_LEN];

unsigned long args[6];
} mmap_event;

static __always_inline u32 init_mmap_context(mmap_event *event_data) {
struct task_struct *task = (struct task_struct *)bpf_get_current_task();

event_data->ts = bpf_ktime_get_ns();

event_data->host_ppid = get_task_ppid(task);
event_data->host_pid = bpf_get_current_pid_tgid() >> 32;

u32 pid = get_task_ns_tgid(task);
if (event_data->host_pid == pid) { // host
event_data->pid_id = 0;
event_data->mnt_id = 0;

event_data->ppid = get_task_ppid(task);
event_data->pid = bpf_get_current_pid_tgid() >> 32;
} else { // container
event_data->pid_id = get_task_pid_ns_id(task);
event_data->mnt_id = get_task_mnt_ns_id(task);

event_data->ppid = get_task_ns_ppid(task);
event_data->pid = pid;
}

event_data->uid = bpf_get_current_uid_gid();

// Clearing array to avoid garbage values
__builtin_memset(event_data->comm, 0, sizeof(event_data->comm));
bpf_get_current_comm(&event_data->comm, sizeof(event_data->comm));

return 0;
}


// Force emitting struct mmap_event into the ELF.
const mmap_event *unused __attribute__((unused));

struct preset_map anon_map_exec_preset_containers SEC(".maps");


SEC("lsm/mmap_file")
int BPF_PROG(enforce_mmap_file, struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags){

struct task_struct *t = (struct task_struct *)bpf_get_current_task();

struct outer_key okey;
get_outer_key(&okey, t);

u32 *present = bpf_map_lookup_elem(&anon_map_exec_preset_containers, &okey);

if (!present) {
return 0;
}

// only if PROT_EXEC is assigned
if (prot & PROT_EXEC) {
if (flags & MAP_ANONYMOUS) {
mmap_event *event_data;
event_data = bpf_ringbuf_reserve(&events, sizeof(mmap_event), 0);

if (!event_data) {
return 0;
}

init_mmap_context(event_data);

__builtin_memset(event_data->args, 0, sizeof(event_data->args));

event_data->args[0] = reqprot;
event_data->args[1] = prot;
event_data->args[2] = flags;
event_data->event_id = ANON_MAP_EXEC;
if (*present == BLOCK) {
event_data->retval = -EPERM;
} else {
event_data->retval = 0;
}
bpf_ringbuf_submit(event_data, 0);
// mapping not backed by any file with executable permission, denying mapping
if (*present == BLOCK) {
return -EPERM;
} else {
return 0;
}
}
}
return 0;
}
111 changes: 111 additions & 0 deletions KubeArmor/BPF/filelessexec.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// +build ignore
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright 2023 Authors of KubeArmor */

#include "shared.h"


struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
} events SEC(".maps");


// Force emitting struct mmap_event into the ELF.
const event *unused __attribute__((unused));

struct preset_map fileless_exec_preset_containers SEC(".maps");

#define MEMFD "memfd:"
#define RUN_SHM "/run/shm/"
#define DEV_SHM "/dev/shm/"

static __always_inline int is_memfd(char *name) {
return string_prefix_match(name, MEMFD, sizeof(MEMFD));
}

static __always_inline int is_run_shm(char *name) {
return string_prefix_match(name, RUN_SHM, sizeof(RUN_SHM));
}

static __always_inline int is_dev_shm(char *name) {
return string_prefix_match(name, DEV_SHM, sizeof(DEV_SHM));
}

struct pathname {
char path[256];
};

SEC("lsm/bprm_check_security")
int BPF_PROG(enforce_bprm_check_security, struct linux_binprm *bprm){

struct task_struct *t = (struct task_struct *)bpf_get_current_task();

struct outer_key okey;
get_outer_key(&okey, t);

u32 *present = bpf_map_lookup_elem(&fileless_exec_preset_containers, &okey);

if (!present) {
return 0;
}

struct pathname path_data = {};

struct file *file = BPF_CORE_READ(bprm, file);
if (file == NULL)
return 0;

bufs_t *path_buf = get_buf(PATH_BUFFER);
if (path_buf == NULL)
return 0;

// prepend path is needed to capture /dev/shm and /run/shm paths
if (!prepend_path(&(file->f_path), path_buf)){
// memfd files have no path in the filesystem -> extract their name
struct dentry *dentry = BPF_CORE_READ(&file->f_path, dentry);
struct qstr d_name = BPF_CORE_READ(dentry, d_name);
bpf_probe_read_kernel_str(path_data.path, MAX_STRING_SIZE, (void *) d_name.name);
} else {
u32 *path_offset = get_buf_off(PATH_BUFFER);
if (path_offset == NULL)
return 0;

void *path_ptr = &path_buf->buf[*path_offset];
bpf_probe_read_str(path_data.path, MAX_STRING_SIZE, path_ptr);
}

const char *filename = BPF_CORE_READ(bprm, filename);

if (is_memfd(path_data.path) || is_run_shm(path_data.path) || is_dev_shm(path_data.path)) {
event *event_data;
event_data = bpf_ringbuf_reserve(&events, sizeof(event), 0);

if (!event_data) {
return 0;
}

__builtin_memset(event_data->data.path, 0, sizeof(event_data->data.path));
__builtin_memset(event_data->data.source, 0, sizeof(event_data->data.source));

bpf_probe_read_str(event_data->data.path, MAX_STRING_SIZE, path_data.path);
bpf_probe_read_str(event_data->data.source, MAX_STRING_SIZE, filename);

init_context(event_data);
event_data->event_id = FILELESS_EXEC;

// mapping not backed by any file with executable permission, denying mapping
if (*present == BLOCK) {
event_data->retval = -13;
bpf_ringbuf_submit(event_data, 0);
// bpf_printk("[bprm] fileless execution detected with pid %d, denying execution", event_data->pid);
return -13;
} else {
event_data->retval = 0;
bpf_ringbuf_submit(event_data, 0);
return 0;
}
}

return 0;
}
86 changes: 86 additions & 0 deletions KubeArmor/BPF/protectenv.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// +build ignore
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright 2023 Authors of KubeArmor */

#include "shared.h"

typedef struct {
u32 pid;
u32 pid_ns;
u32 mnt_ns;
char comm[80];
} pevent;

const pevent *unused __attribute__((unused));

struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
} events SEC(".maps");

struct preset_map protectenv_preset_containers SEC(".maps");

#define DIR_PROC "/proc/"
#define FILE_ENVIRON "/environ"

static __always_inline int isProcDir(char *path) {
return string_prefix_match(path, DIR_PROC, sizeof(DIR_PROC));
}

static __always_inline int isEnviron(char *path) {
return string_prefix_match(path, FILE_ENVIRON, sizeof(FILE_ENVIRON));
}

SEC("lsm/file_open")
int BPF_PROG(enforce_file, struct file *file) {

struct task_struct *t = (struct task_struct *)bpf_get_current_task();

struct outer_key okey;
get_outer_key(&okey, t);

u32 *present = bpf_map_lookup_elem(&protectenv_preset_containers, &okey);

if (!present) {
return 0;
}

u64 id = bpf_get_current_pid_tgid();
u32 tgid = id >> 32;

char path[80];
bpf_d_path(&file->f_path, path, 80);

if (!isProcDir(path)) {
return 0;
}

long envpid;
int count = bpf_strtol(path + sizeof(DIR_PROC) - 1, 10, 0, &envpid);
if (count < 0) {
return 0;
}
u8 envstart = sizeof(DIR_PROC) + count - 1;
if (envstart < 80 && !isEnviron(path + envstart)) {
return 0;
}

long pid = get_task_ns_tgid(t);

if (envpid != pid) {

pevent *task_info;

task_info = bpf_ringbuf_reserve(&events, sizeof(pevent), 0);
if (!task_info) {
return 0;
}
task_info->pid = pid;
task_info->pid_ns = okey.pid_ns;
task_info->mnt_ns = okey.mnt_ns;
bpf_ringbuf_submit(task_info, 0);
return -EPERM;
}

return 0;
}
40 changes: 37 additions & 3 deletions KubeArmor/BPF/shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,40 @@ struct {
__uint(max_entries, 3);
} bufk SEC(".maps");

// ============
// match prefix
// ============

static __always_inline int string_prefix_match(const char *name, const char *prefix, size_t prefix_len) {
int i = 0;
while (i < prefix_len - 1 && name[i] != '\0' && name[i] == prefix[i]) {
i++;
}
return (i == prefix_len - 1) ? 1 : 0;
}

// ============
// == preset ==
// ============

enum preset_action {
AUDIT = 1,
BLOCK
};

enum preset_type {
FILELESS_EXEC = 1001,
ANON_MAP_EXEC
};

struct preset_map {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 256);
__uint(key_size, sizeof(struct outer_key));
__uint(value_size, sizeof(u32));
__uint(pinning, LIBBPF_PIN_BY_NAME);
};

typedef struct {
u64 ts;

Expand Down Expand Up @@ -172,8 +206,8 @@ static __always_inline bool prepend_path(struct path *path, bufs_t *string_p) {
return false;
}

struct dentry *dentry = path->dentry;
struct vfsmount *vfsmnt = path->mnt;
struct dentry *dentry = BPF_CORE_READ(path, dentry);
struct vfsmount *vfsmnt = BPF_CORE_READ(path, mnt);

struct mount *mnt = real_mount(vfsmnt);

Expand All @@ -183,7 +217,7 @@ static __always_inline bool prepend_path(struct path *path, bufs_t *string_p) {
struct qstr d_name;

#pragma unroll
for (int i = 0; i < 30; i++) {
for (int i = 0; i < 20; i++) {
parent = BPF_CORE_READ(dentry, d_parent);
mnt_root = BPF_CORE_READ(vfsmnt, mnt_root);

Expand Down
Loading

0 comments on commit b88c8f5

Please sign in to comment.