最近在学习linux内置的沙盒:Seccomp-bpf
,资料略少,最后找到了这个网站提供的例子。
这个例子我已实验成功,能够正常阻止禁止的系统调用并结束程序,去掉禁止的系统调用后也能正常运行。
但是当我尝试用execv运行另外一个程序时却失败了,显示Bad System Call
。
我已经使用syscall-reporter
检查了需要允许的System Call
,但是仍旧无法运行。
源代码如下:
// main.c/* * seccomp example with syscall reporting * * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org> * Authors: * Kees Cook <keescook@chromium.org> * Will Drewry <wad@chromium.org> * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */#include <stdio.h>#include <stddef.h>#include <stdlib.h>#include <unistd.h>#include "seccomp-bpf.h"#include "syscall-reporter.h"static int install_syscall_filter(void){ struct sock_filter filter[] = { /* Validate architecture. */ VALIDATE_ARCHITECTURE, /* Grab the system call number. */ EXAMINE_SYSCALL, /* List allowed syscalls. */ ALLOW_SYSCALL(rt_sigreturn),#ifdef __NR_sigreturn ALLOW_SYSCALL(sigreturn),#endif ALLOW_SYSCALL(exit_group), ALLOW_SYSCALL(exit), ALLOW_SYSCALL(read), ALLOW_SYSCALL(write), ALLOW_SYSCALL(close), ALLOW_SYSCALL(fstat), ALLOW_SYSCALL(brk), ALLOW_SYSCALL(execve), KILL_PROCESS, }; struct sock_fprog prog = { .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), .filter = filter, }; if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("prctl(NO_NEW_PRIVS)"); goto failed; } if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { perror("prctl(SECCOMP)"); goto failed; } return 0;failed: if (errno == EINVAL) fprintf(stderr, "SECCOMP_FILTER is not available. :(\n"); return 1;}int main(){ prctl(PR_SET_NO_NEW_PRIVS, 1); if (install_syscall_reporter()) return 1; if (install_syscall_filter()) return 1; printf("Hello World!\n"); execv("./test",NULL); printf("Hello World!\n"); return 0;}
//test.c#include <stdio.h>int main() { printf("Hello World!!!\n");}
以下文件我没有修改,就不放源代码了,直接放下载链接。
seccomp-bpf.h
syscall-reporter.c
syscall-reporter.h
编译指令:
echo "static const char *syscall_names[] = {" > syscall-names.h ;\ echo "#include <syscall.h>" | cpp -dM | grep '^#define __NR_' | \ LC_ALL=C sed -r -n -e 's/^\#define[ \t]+__NR_([a-z0-9_]+)[ \t]+([0-9]+)(.*)/ [\2] = "\1",/p' >> syscall-names.h;\ echo "};" >> syscall-names.hgcc test.c -o testgcc main.c syscall-reporter.c -o main
修改过程
# 从最初下载的源代码加上execv函数ubuntu@ubuntu:~/seccomp$ ./main Looks like you also need syscall: brk(12)# 此处添加系统调用白名单:brkubuntu@ubuntu:~/seccomp$ ./main Looks like you also need syscall: execv(59)# 此处添加系统调用白名单:execvubuntu@ubuntu:~/seccomp$ ./main Hello World!Bad system callubuntu@ubuntu:~/seccomp$ sudo ./main Hello World!Bad system call (core dumped)
上面提供的源代码已经是最后一次修改的版本,运行环境为Ubuntu 16.04
ubuntu@ubuntu:~/seccomp$ uname -aLinux ubuntu 4.4.0-53-generic #74-Ubuntu SMP Fri Dec 2 15:59:10 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
小弟初学,希望各位神犇不吝赐教,谢谢!
17.08.20补充内容:
自main.c
调用prctl PR_SET_SECCOMP
系统调用大致有以下几种:
execvebrkaccessmmapopenfstatclosereadmprotectarch_prctlmunmapwriteexit_group
于是main.c
新允许以下调用,仍然无法运行:
ALLOW_SYSCALL(open),ALLOW_SYSCALL(close),ALLOW_SYSCALL(fstat),ALLOW_SYSCALL(execve),ALLOW_SYSCALL(mmap),ALLOW_SYSCALL(mprotect),ALLOW_SYSCALL(munmap),ALLOW_SYSCALL(uname),ALLOW_SYSCALL(arch_prctl),ALLOW_SYSCALL(brk),ALLOW_SYSCALL(access),ALLOW_SYSCALL(readlink),ALLOW_SYSCALL(sysinfo),ALLOW_SYSCALL(writev),ALLOW_SYSCALL(lseek),
使用strace ./main
得到以下信息
execve("./main", ["./main"], [/* 23 vars */]) = 0brk(NULL) = 0x706000access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb318b15000access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3fstat(3, {st_mode=S_IFREG|0644, st_size=23771, ...}) = 0mmap(NULL, 23771, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb318b0f000close(3) = 0access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832fstat(3, {st_mode=S_IFREG|0755, st_size=1864888, ...}) = 0mmap(NULL, 3967488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb318529000mprotect(0x7fb3186e9000, 2093056, PROT_NONE) = 0mmap(0x7fb3188e8000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bf000) = 0x7fb3188e8000mmap(0x7fb3188ee000, 14848, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb3188ee000close(3) = 0mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb318b0e000mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb318b0d000mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb318b0c000arch_prctl(ARCH_SET_FS, 0x7fb318b0d700) = 0mprotect(0x7fb3188e8000, 16384, PROT_READ) = 0mprotect(0x601000, 4096, PROT_READ) = 0mprotect(0x7fb318b17000, 4096, PROT_READ) = 0munmap(0x7fb318b0f000, 23771) = 0prctl(PR_SET_NO_NEW_PRIVS, 1, 0x7ffea2134838, 0, 0x400e80) = -1 EINVAL (Invalid argument)rt_sigaction(SIGSYS, {0x400bf2, [], SA_RESTORER|SA_SIGINFO, 0x7fb31855e4a0}, NULL, 8) = 0rt_sigprocmask(SIG_UNBLOCK, [SYS], NULL, 8) = 0prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) = 0prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, {len = 23, filter = 0x7ffea2134670}) = 0fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0brk(NULL) = 0x706000brk(0x727000) = 0x727000write(1, "Hello World!\n", 13Hello World!) = 13execve("./test", NULL, [/* 23 vars */]) = 0brk(NULL) = 0x21c7000syscall_18446744073709551615(0x7f42edeb7b8a, 0, 0x7f42ee0bde28, 0x10, 0x7fff191037f8, 0x7fff19103040) = 0x15--- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x7f42edeb31b7, si_syscall=__NR_access, si_arch=AUDIT_ARCH_X86_64} ---+++ killed by SIGSYS (core dumped) +++Bad system call (core dumped)
使用strace ./test
得到以下信息
execve("./test", ["./test"], [/* 23 vars */]) = 0brk(NULL) = 0x115f000access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4b1a556000access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3fstat(3, {st_mode=S_IFREG|0644, st_size=23771, ...}) = 0mmap(NULL, 23771, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4b1a550000close(3) = 0access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832fstat(3, {st_mode=S_IFREG|0755, st_size=1864888, ...}) = 0mmap(NULL, 3967488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4b19f6a000mprotect(0x7f4b1a12a000, 2093056, PROT_NONE) = 0mmap(0x7f4b1a329000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bf000) = 0x7f4b1a329000mmap(0x7f4b1a32f000, 14848, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4b1a32f000close(3) = 0mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4b1a54f000mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4b1a54e000mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4b1a54d000arch_prctl(ARCH_SET_FS, 0x7f4b1a54e700) = 0mprotect(0x7f4b1a329000, 16384, PROT_READ) = 0mprotect(0x600000, 4096, PROT_READ) = 0mprotect(0x7f4b1a558000, 4096, PROT_READ) = 0munmap(0x7f4b1a550000, 23771) = 0fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0brk(NULL) = 0x115f000brk(0x1180000) = 0x1180000write(1, "Hello World!!!\n", 15Hello World!!!) = 15exit_group(0) = ?
解决方案
我知道了。你允许的系统调用太少,根本不够执行你的 test 程序。(seccomp filter 是会被 execve 继承的。)
你可以 strace ./test 看看执行 test 的过程中都用了哪些系统调用。