简介
沙箱是个好点子。无论是提高儿童的免疫系统还是将你的app和系统其他部分隔离,这就是沙箱的意义。虽然沙箱具有显而易见的好处,但是它并不常见。我们认为那是因为对于大部分开发者来说沙箱是挺模糊的概念,希望这篇文章可以解决这个问题。
什么是沙箱?
软件沙箱是将一个进程与其他的进程隔离开来,从而限制进程对其所需系统的访问,并拒绝访问其他所有内容。举个简单的例子,如果在Adobe Reader中打开一个pdf文档。在Adobe Reader使用沙箱之后,文档被运行在限制环境下的进程打开,使之与系统的其他部分隔离。这就限制了一个恶意文档可能造成的破坏,这也是为什么可以在野外看到更多的使用第一种攻击方式的恶意PDF文档被丢弃,更多的用户选择更新支持沙箱的Adobe Reader版本。
但是沙箱并不是魔法,它只是简单的限制攻击者可用的工具和减少利用的影响范围。沙箱中进程的bug依然可以拿到对系统关键部分的全部权限,使得沙箱几乎没有用处。
Canary沙箱
长时间以来读者都知道Canary是我们熟知的蜜罐解决方案(如果你对快速部署和运行漏洞检测感兴趣,请通过https://canary.tools联系我们)。
Canary是一个高质量的混合交互蜜罐。它是一个可以嵌入到你的网络中的小设备,能够模拟一大批机器(打印机/便携电脑/文件服务器等等)。一但配置,它将运行零个或更多的服务,比如SSH,telnet,数据库或者Windows文件共享。当有人和这些模拟的主机和服务交互的时候,你会收到一个警告(和一个你能取消你的周末计划的高质量的信号)。
几乎我们所有的服务都使用内存安全语言实现,但是如果用户想要一个Windows文件共享,我们依赖一个流行的Samba项目(在设置Samba之前,我们需要检测其他SMB能力,像优秀的impacket库,但是因为我们的Canary(和他们的文件共享)可以注册到活动目录中所以Samba更好),在Samba作为服务运行之后,我们不能完全控制它内部的工作,它成了沙箱的重要部分:我们希望可以限制它访问系统的其他部分,避免系统受到破坏。
Sandboxing 101
我们将用简单的介绍解释一下Linux提供的沙箱的一些关键部分。
Linux提供多种方式来限制进程,我们将考虑哪种方式适合我们的解决方案。我们实现了一个沙箱解决方案,你可以根据你的环境来选择哪些方式作为组合,让它们更有意义。
控制组
控制组是用来限制和控制对CPU、内存、磁盘、网络等资源的访问。
Chroot
这进程能看到包含更改的文件系统中明显的根目录。它确保这个进程不能接触到整个文件系统,但是还是能访问到它应该能够看到的部分。Chroot是Unix 系统第一次在沙箱中做尝试,但是很快就被确定了它不足以限制攻击者。
Seccomp
作为代表的“安全计算模式”,可以让你限制进程进行系统调用。限制系统调用意味着这个进程只能执行你希望它能够执行的系统操作,因此如果攻击者危及到你的应用程序,他们将无法运行。
能力
这是指能够在Linux系统上执行的特权操作的集合。这些功能包括setuid,chroot和chown。有关的完整列表你可以在此处查看源代码(https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h)。然而他们也不是灵丹妙药
命名空间
没有命名空间,任何进程都能够查看所有所有进程的系统资源信息。命名空间虚拟化如主机名,用户ID,网络资源等资源,让进程无法查看其他进程的这些信息。
过去在你的应用程序中增加沙箱是为了使用其中一些原语(对于大多数开发人员而言这些看起来很杂乱)。幸运的是,现在很多项目将沙箱包装在软件包中方便使用。
我们的解决方案
我们现在需要找到一个适合我们的解决方案,但是也可以让我们在需要时轻松扩展而不用重头开始重建。
我们想要的解决方案至少需要解决Seccomp过滤和chroot/pivot_root的问题。如果你能获取到完整的配置文件,那么过滤系统调用很容易控制,一旦过滤,你就会放心不少因为你知道服务不能执行它不应该执行的系统调用。对于我们还有另一种简单的选择那就是限制文件系统的视图。Samba只需要访问特定的目录和文件,同时许多文件也可以设置为只读。
我们评估了许多选项,最终决定的解决方案如下:
- 隔离进程(Samba)
- 保留真正的主机名
- 能够与非独立的过程进行交互
另一个进程必须能够拦截Samba网络流量,这就意味着我们无法在没有引入额外进程的情况下将其放入网络命名空间。
这排除了像Docker这样的东西,虽然它提供了开箱即用的高级别隔离(这在很多情况下都是完美的选择),但是我们不得不关闭很多功能才能让app更好的发挥作用。
Systemd 和 nsroot (看起来已经被放弃了)都更专注于特定的隔离技术(针对Systemd的seccomp过滤和nsroot的命名空间隔离),但对于我们来说还不够。
然后我们查看了NSJail 和 Firejail(Google 与 Mozilla 比较,虽然这与我们的决定没有任何关系)。两者十分相似,并且在我们可以限制的范围内为我们提供了灵活性,使它们由于其他选择。
最后我们决定使用NsJail,但由于他们非常相似,很容易走到另一条路,比如YMMV
NsJail
Nsjail 就像在其概述中说的那样“是一个用于linux进程隔离的工具”,由谷歌团队开发(但是它没有被谷歌官方认可为谷歌的产品)。它提供独立的命名空间,文件约束系统,资源限制,seccomp过滤器,克隆/隔离的以太网接口和控制组。
此外,它使用kafel(另一种非官方google产品),它允许你在配置文件中定义系统调用过滤策略,从而可以轻松管理/维护/重用/扩展你的配置。
使用Nsjail隔离进程的一个简单示例:
./nsjail -Mo --chroot /var/safe_directory --user 99999 --group 99999 -- /bin/sh -i
参数的意义是:
-Mo: launch a single process using clone/execve
–chroot: set /var/safe_directory as the new root directory for the process
–user/–group: set the uid and gid to 99999 inside the jail
— /bin/sh -i: our sandboxed process (in this case, launch an interactive shell)
我们将chroot设置为/var/safe_directory。这是我们事先创建的有效chroot。你可以使用–chroot /用于测试目的。
如果你启动并运行ps aux 和 id,你会看到类似下面的内容:
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
99999 1 0.0 0.1 1824 1080 ? SNs 12:26 0:00 /bin/sh -i
99999 11 0.0 0.1 3392 1852 ? RN 12:32 0:00 ps ux
$ id
uid=99999 gid=99999 groups=99999
你只能看到你在jail中启动的进程
现在我们尝试增加一个过滤器:
./nsjail --chroot /var/safe_directory --user 99999 --group 99999 --seccomp_string 'POLICY a { ALLOW { write, execve, brk, access, mmap, open, newfstat, close, read, mprotect, arch_prctl, munmap, getuid, getgid, getpid, rt_sigaction, geteuid, getppid, getcwd, getegid, ioctl, fcntl, newstat, clone, wait4, rt_sigreturn, exit_group } } USE a DEFAULT KILL' -- /bin/sh -i
参数表示:
-Mo: launch a single process using clone/execve
–chroot: set /var/safe_directory as the new root directory for the process
–user/–group: set the uid and gid to 99999 inside the jail
–seccomp_string: use the provided seccomp policy
— /bin/sh -i: our sandboxed process (in this case, launch an interactive shell)
如果你现在尝试运行id,会发现失败了。因为我们需要给他权限来使用需要的系统调用。
$ id
Bad system call
我们的想法是使用NsJail来执行smbd 和 nmbd(samba设置需要用到它)并且只能做事先预期的系统调用
构建我们的解决方案
从配置空白文件开始,专注于smbd,我们开始添加限制来锁定服务。
首先,我们构建seccomp过滤器列表,以确保该进程只能访问所需的系统调用。这点使用perf就很容易获得:
perf record -e 'raw_syscalls:sys_enter' -- /usr/sbin/smbd -F
这记录了smbd用于perf格式的所有系统调用。要以可读的列表格式输出系统调用:
perf script | grep -oP "(?<= NR )[0-9]+" | sort -nu
在这里要提到的是系统调用号可以根据你自己来命名成不同的方式。即使只是在‘strace’和‘nsjail’之间,一些系统调用名称与linux源代码中的名称略有不同。这意味着如果你使用系统调用名称,在不同的工具之间你不能直接使用完全相同的列表,因此需要重新命名一下他们。如果你对此感到担心,你可以选择使用系统调用号。这是一种强大的与工具无关的识别系统调用的方法。
在我们有了清单后,我们开始限制FS访问以及最终设置,以确保它尽可能的被锁定。
通过启动shell并手动测试保护这种有趣的方法可以测试配置文件是否按照我们的预期工作,
./nsjail –config smb.cfg — /bin/sh -i
一旦这个策略通过测试,smbd就可以按照预期运行,那么我们就可以开始测试nmbd.
两个服务都是沙箱,我们进行了几次长时间运行的测试,以确保我们没有错过任何东西。主要包括在周末运行服务并通过不同系统连接它们来测试。经过所有测试,我们很高兴的确认没有发现任何错误。
这对我们意味着什么?
针对Samba的大多数利用希望有一个开放系统来访问系统资源。在未来的某天,当下一个Samba 0day到来时,会证明针对samba的通用利用将会在尝试调用我们没有明确允许的系统调用时失败。但是即使攻击者要破坏Samba并将自己包装一个shell,这个shell也会在文件系统和系统的限制视图中受到功能限制。
这对你意味着什么?
我们引导你完成为Samba服务配置沙箱的过程,目的是让你思考自己的环境以及沙盒如何在保护你的应用程序中发挥作用。我们想给你展示的是:这不是一个昂贵或复杂的任务。你应该尝试一下,如果你想这样做,请给我们留言!