6

我想通过ptrace()创建一个我启动的进程以及它的所有子进程(包括孙子进程等)来实现一个沙盒。父进程,ptrace()即主管。将是一个简单的 C 或 Python 程序,从概念上讲,它将限制文件系统访问(基于路径名和访问方向(读或写)和套接字访问(例如,不允许创建套接字)。

我应该注意什么,以使ptrace()d 进程及其子进程(递归)无法绕过沙箱?主管有什么特别的事情应该做fork()以避免竞争条件吗?rename()是否可以在没有竞争条件的情况下从子进程中读取 eg 的文件名参数?

这是我已经计划做的事情:

  • PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE避免(某些)比赛fork()规则
  • 默认禁止所有系统调用,并组成允许系统调用的白名单
  • 确保*at()系统调用变体(例如openat)得到适当保护

我还应该注意什么?

4

2 回答 2

12

主要问题是许多系统调用参数,如文件名,作为用户空间指针传递给内核。任何允许同时运行并且对指针指向的内存具有写访问权限的任务都可以在您的主管检查这些参数之后以及内核对其进行操作之前有效地修改这些参数。当内核跟随指针时,指向的内容可能已被另一个可访问该内存的可调度任务(进程或线程)故意更改。例如:

Thread 1                           Supervisor             Thread 2
-----------------------------------------------------------------------------------------------------
strcpy(filename, "/dev/null");
open(filename, O_RDONLY);
                                   Check filename - OK
                                                          strcpy(filename, "/home/user/.ssh/id_rsa");
(in kernel) opens "/home/user/.ssh/id_rsa"

阻止这种情况的一种方法是禁止clone()使用该CLONE_VM标志进行调用,此外还防止创建任何可写MAP_SHARED内存映射(或至少跟踪它们,以便您拒绝任何尝试直接从此类映射中引用数据的系统调用)。在允许系统调用继续之前,您还可以将任何此类参数复制到非共享反弹缓冲区中。这将有效地防止任何线程应用程序在沙箱中运行。

另一种方法是SIGSTOP在每个潜在危险的系统调用周围跟踪组中的每个其他进程,等待它们实际停止,然后允许系统调用继续。在它返回之后,你然后SIGCONT他们(除非他们已经被停止)。不用说,这可能会对性能产生重大影响。

(There are also analogous problems with syscall arguments that are passed on the stack, and with shared open file tables).

于 2010-12-12T12:57:35.020 回答
3

ptrace 不是在事后才收到通知吗?我认为您没有机会真正阻止系统调用的发生,而只是在看到“邪恶”的东西后尽快杀死它。

看起来您更多的是在寻找 SELinux 或 AppArmor 之类的东西,在那里您可以保证连一个非法呼叫都不会通过。

于 2010-12-11T01:22:31.273 回答