Linux沙箱(1): setuid sandbox

<

div id=”content” contentScore=”4628″>Setuid Sandbox主要是基于Linux Kernel所提供的安全机制(eg,DAC)来实现。简单地说就是利用 random uid/gid + chroot() + capability的组合出击来达到目标。其实现非常简单,无需修改Kernel。下面分别悉数之:

  1. Linux中每个进程都会有一个uid,uid=0则为root用户进程(privileged),uid>0则为普通用户进程(unprivileged)。不同uid进程之间(不包括root进程)是相互隔离的,各自都有自己独立的权限,互不干扰。而root进程具有特权,它能干任何事情。Linux uid/gid机制主要是用于进程的权限隔离。如果你打算执行不可信的程序,那么你可以在启动该程序时为其分配一个random uid。一个可能的执行流程如下:fork() -> setuid() -> {设置相关的进程资源限制, eg, RLIMIT_NPROC (0,0)} -> execv()。注意,setuid()只能由root权限(或拥有 CAP_SETUID capability)才能成功调用,所以这个执行流程需要借助某个拥有root权限的helper。比如,将helper程序设置为setuid root。

  2. Chroot是Linux kernel提供的另一个安全功能,它用于修改进程的根目录。比如执行chroot(“/tmp/sandbox/1/”)则可以设置当前进程的根目录为”/tmp/sandbox/1/”,那么该进程的文件操作将被限制在”/tmp/sandbox/1/”中。注意,chroot()只能由root权限(或拥有CAP_SYS_CHROOT capability)才能成功调用。也许你马上会想到:在前面的执行流程中,先让具有root权限的helper去执行”chroot()”后再调用setuid() -> {…} -> execve(),但这样做是行不通的,因为execve()本要执行的binary文件已经不可用了(进程的根目录已经被重定位了)。Google的一篇文章里给出了一个解决此问题的简单方法:

(1) Helper创建一个子进程H,注意要用clone()和CLONE_FS,使得Helper和H可以共享根目录、当前目录、等等;

(2) Helper降权后执行execve(“Worker”);

(3) Worker(原Helper进程)请求H去执行chroot();

(4) H执行chroot(),新的根目录会对H和Worker同时生效。

(5) H退出。

这个方法听起来不错,前提是Helper需要设置RLIMIT_NOFILE为(0,0),并且对于不可信的Worker进程来说,在执行第4步之前应是可控的。

另外,对于Helper程序来说,由于它是以root身份运行,那么就可能会成为攻击点,比如confused deputy问题。下面我们介绍如何用capability机制来解这个问题。

  1. Linux Capability 主要是解决 confused deputy problem. 这类问题的典型代表之一是 CSRF(cross-site request forgery).给一个简单的例子来描述: 假如你刚刚用浏览器访问过你的网上银行,而同时又在逛水木BBS。这时BBS上的某个坏蛋可能正好猜到你在访问网上银行,于是那个坏蛋就编写一个在你的网上银行站点进行转帐的form提交的链接,并将该链接作为他在BBS上传的图片的tag。如果此时你点击了他的图片,并且你的网上银行在cookie中保存的授权信息还没有过期,那么你就倒霉了。此时的你就被称为”confused deputy”,因为你糊里糊涂地就授权了那个坏蛋所诱导的这次交易事务。

Linux支持Capability的主要目的是细化root的特权,以避免confused deputy problem. 比如拿ping程序来说,它需要使用raw_sockets所以需要root特权才能运行;如果有了Capability机制,由于该程序只需要一个CAP_NET_RAW的Capability即可运行,那么根据最小权限原则,该程序运行时可以丢弃所有多余的Capability,以防止被误用或被攻击。所以,Capability机制可以将root特权进行很好的细分,当前kernel(2.6.18)已支持30多种不同的Capability。注意在之前的kernel实现中,Capability只能由root进程持有,非root进程是不能保持任何Capability的。但是在2.6.24及以上的kernel版本中一个普通用户进程也将可以持有capability。

小结:可以看出,setuid sandbox实现是简单易行。在一定程度上,它可以用于隔离不可信的程序。由于它完全依赖于kernel所提供的安全机制,除非攻击者能找到kernel的0-day漏洞并通过攻击获得root权限,否则setuid sandbox所提供的安全隔离是可以保证的。不可信代码的隔离一直都是操作系统安全领域的挑战之一,面对这种挑战,我们应当采用防御纵深(in depth)的方法来解决。而最近,我们发现setuid sandbox已被Google用作Chromium系统的第一道隔离屼/div>