Seccomp syscall filtering¶
Linux seccomp (
SECCOMP_MODE_STRICT/SECCOMP_MODE_FILTER) installs a classic-BPF program — viaprctl(PR_SET_SECCOMP, ...)orseccomp(2)— that inspects each syscall's number and arguments and returns an action (kill, errno, trap, allow), shrinking the kernel attack surface reachable from a compromised process.
Mechanism¶
Neden çalışır
Her privilege-escalation ya da sandbox-escape exploit'i nihayetinde kernel'e syscall interface üzerinden ulaşır. Kernel devasa bir saldırı yüzeyidir (yüzlerce syscall, her biri sub-command'lar ve driver yollarıyla); çoğu program yalnızca minik bir alt kümeye ihtiyaç duyar. Seccomp least syscall ilkesini enforce eder: syscall entry sınırında bir filtre araya koyar, böylece bir process ihtiyaç duymadığı çağrılardan gönüllü ve geri dönülmez şekilde vazgeçer.
İki mod vardır:
SECCOMP_MODE_STRICT— thread yalnızcaread(2),write(2),_exit(2)(exit_group(2)değil) vesigreturn(2)'yi çağırabilir; başka her şeySIGKILLalır.SECCOMP_MODE_FILTER("seccomp-bpf") — process, her syscall için read-only birstruct seccomp_dataüzerinde çalışan ve bir action döndüren klasik bir BPF programı sağlar.
Güvenlik invariant'ı monotonik, ayrıcalıksız öz-kısıtlama'dır: bir kez kurulduktan
sonra bir filtre "kaldırılamaz" ve çağıran thread'e ve onun gelecekteki tüm
child'larına uygulanır. Ayrıcalıksız bir process yalnızca kendi privilege'larını
azaltabileceği için, filtre önce hiç yeni privilege vermediğini tesis etmelidir: task
prctl(PR_SET_NO_NEW_PRIVS, 1)'i çağırmalı (ya da CAP_SYS_ADMIN tutmalı), aksi halde
kurulum -EACCES döndürür. İşte bu, seccomp'un sıradan, güvenilmeyen kod tarafından
güvenle uygulanmasını sağlar — kernel, asla erişim vermeye kandırılamayacağını bilir.
Walkthrough¶
1. Strict mode (minimal sandbox):
#include <sys/prctl.h>
#include <linux/seccomp.h>
prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT); /* only read/write/_exit/sigreturn now */
2. Filtrenin gördüğü veri. BPF programı şunun üzerinde değerlendirilir:
struct seccomp_data {
int nr; /* system call number */
__u32 arch; /* AUDIT_ARCH_* (calling convention)*/
__u64 instruction_pointer; /* CPU IP at the syscall */
__u64 args[6]; /* up to 6 syscall arguments */
};
3. Ham classic-BPF filtresi — yalnızca listelenen syscall'lara izin ver, aksi halde kill et (number/ABI karışıklığından kaçınmak için önce arch kontrolü):
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/audit.h>
#include <sys/prctl.h>
struct sock_filter prog[] = {
/* load arch and reject anything but x86-64 */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
/* load syscall number */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_read, 3, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 2, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_exit, 1, 0),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (EPERM & SECCOMP_RET_DATA)),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog bpf = { .len = sizeof(prog)/sizeof(prog[0]), .filter = prog };
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bpf); /* or seccomp(SECCOMP_SET_MODE_FILTER, 0, &bpf) */
4. Filtre return action'ları (en yüksek öncelik önce):
SECCOMP_RET_KILL_PROCESS kill whole process (core dump)
SECCOMP_RET_KILL_THREAD kill calling thread (alias SECCOMP_RET_KILL)
SECCOMP_RET_TRAP deliver SIGSYS
SECCOMP_RET_ERRNO skip syscall, return chosen errno
SECCOMP_RET_USER_NOTIF hand off to a userspace supervisor (seccomp notify)
SECCOMP_RET_TRACE notify an attached ptracer
SECCOMP_RET_LOG run syscall, but log it
SECCOMP_RET_ALLOW run syscall normally
libseccomp eşdeğeri
libseccomp aynı cBPF'yi sizin için inşa eder: scmp_filter_ctx ctx =
seccomp_init(SCMP_ACT_ERRNO(EPERM)); sonra seccomp_rule_add(ctx,
SCMP_ACT_ALLOW, SCMP_SYS(read), 0); (syscall başına tekrarla) ve
seccomp_load(ctx);. Ayrıca multi-arch ve argüman karşılaştırmalarını da yönetir.
Detection¶
Denetim: bir SECCOMP_RET_LOG action'ı (ve SECCOMP_RET_KILL* denetim kaydı), ihlal eden
syscall'ı adlandıran kernel audit/dmesg entry'leri üretir; reddedilen syscall'lar böyle
gözlemlenir. /proc/<pid>/status, Seccomp: field'ını (0 disabled, 1 strict, 2 filter)
ve SeccompFilters: sayısını sunar.
Mitigation¶
(Artık risk / bypass.) Seccomp kernel'i daraltır ama mühürlemez:
- Filtrenin hâlâ izin verdiği syscall'lar her ne ise sömürülebilir kalır. İzin verilen
bir çağrı üzerinden ulaşılabilen bir bug (örn. izin verilen bir
ioctlya dasocketyolu) etkilenmez — seccomp saldırı-yüzeyi azaltmasıdır, bug eliminasyonu değil. - Argüman incelemesi sığdır. cBPF pointer argümanlarını dereference edemez (bir path
ya da struct'ı incelemek için bir user pointer'ı takip etmek yok), bu yüzden işaret
edilen veri üzerinde filtreleme imkânsızdır; bu ayrıca argüman belleği üzerinde TOCTOU
oyunlarına da olanak tanır. Number/ABI karışıklığından yalnızca filtre
archkontrol ederse kaçınılır. bpf(2),ptrace(2)ya da writable/procyollarına izin veren fazla esnek bir politika, bir saldırganın yine de BPF JIT spray'e ya da diğer kernel primitive'lerine ulaşmasına izin verebilir. Seccomp namespace'ler ve LSM'lerle birlikte çalışır — onların yerini almaz.
References¶
- seccomp(2) man page. — https://man7.org/linux/man-pages/man2/seccomp.2.html
- Kernel documentation. Seccomp BPF (SECure COMPuting with filters). — https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html