Linux ptrace如何不安全或包含竞态条件?

6
我希望通过对启动的进程及其所有子进程(包括孙子等)进行ptrace()处理来实现沙箱。 ptrace()父进程即监督者将是一个简单的C或Python程序,概念上它将限制文件系统访问(基于路径名和访问方向(读取或写入))和套接字访问(例如禁止套接字创建)。
为了使ptrace()处理的进程及其子进程(递归地)无法绕过沙箱,我应该注意什么?监督者在fork()时是否需要特殊操作以避免竞争条件?是否可能在没有竞争条件的情况下从子进程中读取例如rename()的文件名参数?
以下是我已经计划执行的内容:
  • PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE以避免在fork()时出现一些竞争条件
  • 默认情况下禁止所有系统调用,并组成允许的系统调用白名单
  • 确保正确保护*at()系统调用变体(例如openat
还有什么其他需要注意的吗?

1
所以基本上你正在尝试复制Systrace(http://www.systrace.org/)的基于ptrace的后端? - thkala
是的,我的主管的工作方式与 systrace 类似。不幸的是,systrace 似乎已经停止维护,它不能干净地编译,而且还存在 bug(当我对运行GNU as 的GCC进行沙盒处理时,它会无限期地挂起)。 - pts
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_VM标志来调用clone(),另外还要防止创建可写的MAP_SHARED内存映射(或者至少跟踪它们以便拒绝任何试图直接引用此类映射数据的系统调用)。您也可以在允许系统调用继续之前将任何此类参数复制到非共享的反弹缓冲区中。这将有效地禁止任何线程应用程序在沙箱中运行。

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

(与通过堆栈传递的系统调用参数和共享打开文件表存在类似问题。)


为什么复制解决方案会阻止线程化应用程序?无论如何,应用程序是否期望系统调用是“有点”原子的? - keppla
1
@keppla:因为在线程应用程序中无法创建非共享内存区域,因为所有线程共享同一个VM。将数据复制到非共享内存区域的解决方法适用于在与其他进程共享的内存区域中的系统调用参数的情况。 - caf

3

ptrace仅能在事后获得通知吗?我认为你没有机会实际停止系统调用的发生,只能在看到“恶意”行为后尽快终止它。

看起来你更需要像SELinux或AppArmor这样的东西,可以保证不会有任何非法调用通过。


1
在Linux上,有PTRACE_SYSEMU,可以停止系统调用。同时感谢您提到了替代方案。然而,我无法理解为什么SELinux或AppArmor会更安全——前提是没有漏洞和竞争条件。这是一个合理的假设吗? - pts
2
禁用系统调用的常见方法是将其更改为getpid(2)。 - maat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接