AppArmor Logging Hack
Credits: unknown
Anleitung für AppArmor Umfassende AppArmor Policy
Beim Versuch, diese Policy einzuspielen fing AppArmor an, wie wahnsinnig zu loggen.
Eine generelle Methode, nur Denials von AppArmor zu erhalten scheint nicht zu existieren, vgl. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/security/apparmor/audit.c#n164
Den Kernel jedes Mal neu zu kompilieren mit meinem Patch war mir zu blöd, und Upstream scheint das Problem zwar bekannt, aber keiner dazu bereit sich zu rühren. Also dem Kernel mit eBPF, Livepatch oder KProbes zuleibe rücken. Für die Zieldistro blieb nur mehr KProbes.
Makefile
SHELL := bash .ONESHELL: .SHELLFLAGS := -eu -o pipefail -c .DELETE_ON_ERROR: #MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --no-builtin-rules .DEFAULT: all obj-m += logfilter.o PWD := $(CURDIR) .PHONY: all all: ## Builds kernel module make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules .PHONY: help help: # see https://diamantidis.github.io/tips/2020/07/01/list-makefile-targets @grep -E '^[\a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ | sed -n 's/^\(.*\): \(.*\)##\(.*\)/\1|\3/p' \ | column -t -s '|' .PHONY: clean clean: ## cleans up build dir make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean # https://www.xmodulo.com/build-kernel-module-dkms-linux.html .PHONY: dkms_install dkms_install: ## Install as DKMS module, build, and install sudo -s << EOF cp -R . /usr/src/logfilter-1.0.0 cd /usr/src/logfilter-1.0.0 dkms add logfilter/1.0.0 dkms build logfilter/1.0.0 dkms install logfilter/1.0.0 dkms status | grep logfilter EOF
logfilter.c
/* vim:set ts=8 sts=8 sw=8 tw=80 cc=80 noet: */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/audit.h> #include <linux/lsm_audit.h> #include <linux/kprobes.h> /* TODO: extract this from security/apparmor/include/audit.h */ enum audit_type { AUDIT_APPARMOR_AUDIT, AUDIT_APPARMOR_ALLOWED, AUDIT_APPARMOR_DENIED, AUDIT_APPARMOR_HINT, AUDIT_APPARMOR_STATUS, AUDIT_APPARMOR_ERROR, AUDIT_APPARMOR_KILL, AUDIT_APPARMOR_AUTO }; struct apparmor_audit_data { int error; int type; u16 class; const char *op; struct aa_label *label; const char *name; const char *info; u32 request; u32 denied; union { /* these entries require a custom callback fn */ struct { struct aa_label *peer; union { struct { const char *target; kuid_t ouid; } fs; struct { int rlim; unsigned long max; } rlim; struct { int signal; int unmappedsig; }; struct { int type, protocol; struct sock *peer_sk; void *addr; int addrlen; } net; }; }; struct { struct aa_profile *profile; const char *ns; long pos; } iface; struct { const char *src_name; const char *type; const char *trans; const char *data; unsigned long flags; } mnt; }; }; #define aad(SA) ((SA)->apparmor_audit_data) /* END TODO */ #define KPROBE_PRE_HANDLER(fname) static int __kprobes fname(struct kprobe *p, struct pt_regs *regs) KPROBE_PRE_HANDLER(patch) { /* * x86, 64bit * Most of the kernel functions (including those with variable argument * list) get the first 6 arguments in rdi, rsi, rdx, rcx, r8, r9, in * that order, the remaining ones go on stack. *(esp+8) should be the * 7th argument, *(esp+16) - the 8th and so on. * * https://stackoverflow.com/questions/10563635/getting-function-arguments-using-kprobes */ unsigned long rdi = regs->di; struct common_audit_data *a = (struct common_audit_data*) rdi; /* is this an AppArmor event? */ if (aad(a)) { /* get event type */ int type = aad(a)->type; /* filter if we are not interested */ if (type != AUDIT_APPARMOR_KILL && type != AUDIT_APPARMOR_DENIED && type != AUDIT_APPARMOR_STATUS) { /* set common_audit_data = NULL, then common_lsm_audit * returns immediately */ regs->di = 0; } } return 0; } static struct kprobe kp_patch = { .symbol_name = "common_lsm_audit", .pre_handler = patch }; static __init int logfilter_init(void) { int result = register_kprobe(&kp_patch); if(result) { printk(KERN_INFO "Failed to register kprobe\n"); return result; } else { printk(KERN_INFO "Registered common_lsm_audit patch\n"); } return 0; } static __exit void logfilter_exit(void) { unregister_kprobe(&kp_patch); printk(KERN_INFO "Unregistered common_lsm_audit patch\n"); } module_init(logfilter_init); module_exit(logfilter_exit); MODULE_DESCRIPTION("auditlog filtering"); MODULE_AUTHOR("unknown, fuero"); MODULE_LICENSE("GPL");
dkms.conf
PACKAGE_NAME="logfilter" PACKAGE_VERSION="1.0.0" BUILT_MODULE_NAME[0]="logfilter" DEST_MODULE_LOCATION[0]="/kernel/extra/" AUTOINSTALL="yes"