我想通过ptrace()
我开始的过程实现一个沙箱,它的所有孩子都会创建(包括孙子等)。 ptrace()
父进程,即主管。将是一个简单的C或Python程序,从概念上讲它将限制文件系统访问(基于路径名和访问方向(读或写)和套接字访问(例如禁止套接字创建)。
我应该注意什么才能使ptrace()
d进程及其子进程(递归)无法绕过沙箱?主管在fork()
时间应该做些什么特别的事情以避免竞争条件?是否可以读取例如文件名的参数。来自没有竞争条件的子进程的rename()
?
以下是我原本打算做的事情:
PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE
在fork()
*at()
系统调用变体(例如openat
)得到妥善保护我还应该注意什么?
答案 0 :(得分: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
他们(除非他们已经停止)。毋庸置疑,这可能会对性能产生重大影响。
(在堆栈上传递的syscall参数以及共享打开文件表也存在类似问题。)
答案 1 :(得分:3)
ptrace是否仅在事后获得通知?我认为你没有机会真正阻止系统调用的发生,只有在你看到“邪恶”的东西时才能尽快杀死它。
看起来你更喜欢像SELinux或AppArmor这样的东西,你可以保证甚至连一个非法的电话都没有通过。