沙箱技术杂谈
为什么沙箱技术被称为沙箱技术?
在现实生活中,沙箱是一个装满沙子的小箱子,小孩儿们可以在沙箱里面发挥自己的想象力——建沙堡、画画等,而不会将沙子弄得满地都是。
在计算机安全领域,沙箱技术的主要目标是隔离和保护程序,以防止它们对系统或其他应用程序造成不必要的损害或干扰。
沙箱技术的历史
早在上世纪60年代,就已经通过硬件实现了系统和进程代码的隔离。
80年代,通过硬件方法隔离不同进程的内存空间。VAX/VMS操作系统引入了”访问控制列表”的概念,允许管理员对文件和资源进行更细粒度的权限控制。这是隔离和控制访问的重要步骤。
90年代,互联网逐渐开始普及。产生了解释器和被解释的代码之间的隔离,以及Java虚拟机(JVM)等早期沙箱技术。
2000年左右,是针对浏览器的网络攻击最盛之时。
而2010年前后,现代沙箱技术崛起了,主要用于浏览器,让不被信任的代码、数据被放置于一个被隔离的进程中。当被隔离的进程(子进程)想要执行一些需要授权的操作时,需要向父进程(如Firefox)请求,得到允许后方可执行。
沙箱逃逸
chroot()
chroot()
系统调用最早于1979年出现在Unix系统,然后出现在BSD系统。这是一种传统沙盒。
chroot('/sandbox')
的作用是让调用该函数的进程以及其子进程认为根目录是/sandbox
,在/sandbox
中时,不能cd ../
到真正的根目录,因此一定程度上限制了进程对/sandbox
外资源的访问。
路径穿越
考虑在根目录运行如下C代码(不完整):
1 | chroot("/sandbox"); |
初看:busybox
运行,它认为自己的根目录是/sandbox
。但问题是,调用chroot()
不会自动更改工作目录,因此busybox
的工作目录还在根目录,可能可以借由这个工作目录访问沙箱外的资源。
资源未关闭
仅仅执行chroot()
,并不会将先前打开的资源(如文件)关闭(留下文件句柄)。
Linux中有许多后缀为at
的系统调用,可以根据目录(的句柄)和相对路径找到文件。以下是一些例子:
1 | int openat(int dirfd, char *pathname, int flags); |
如果先前有打开的目录未释放句柄,那么很容易利用句柄访问任意文件。
重复调用chroot()
调用了一次chroot()
后,如果没有明确限制,可以再次调用chroot()
。考虑以下情况:
位于根目录为/sandbox
的沙箱中(已经执行chroot("/sandbox");
)。
1 | mkdir springboard # 在沙箱的根目录中创建目录 |
seccomp
seccomp
被称作系统调用的防火墙。可以禁用某些系统调用,或者基于参数来禁用。
docker、chrome、firefox等,都依赖于seccomp
。
seccomp
的规则将会被children
继承
1 | scmp_filter_ctx ctx; //seccomp过滤规则的数据结构 |
更多使用方法:
1 | man seccomp_rule_add |
宽松的过滤规则
Linux系统现在有300多种系统调用,且仍然在不断更新,十分复杂。沙箱应用开发者为了功能、性能或方便,可能会制定相对宽松的过滤规则。有些未被允许的系统调用就有可能被用于逃逸。
系统调用混淆
许多64位架构向后兼容了32位。在amd64等架构中,你可以在同一个进程中切换32/64位模式。
不过有趣的是,不同架构(甚至是同一个架构的32/64位)的系统调用号不一样。例如:
exit()
在amd64中号码是60,在x86中是1。
在amd64操作系统中,seccomp
默认配置的是amd64下允许/禁用的系统调用。如果允许两种模式下的系统调用,那么沙盒很可能顾此失彼。
考虑下面这种情况:
系统调用号 | amd64 | x86 |
---|---|---|
3 | close | read() |
4 | stat() | write() |
5 | fstat() | open() |
如果开发者在配置规则时,本意是允许close()
, stat()
, fstat()
系统调用,但由于seccomp
是根据系统调用号进行过滤,当我们使用32位的指令进行系统调用时,我们竟然可以调用open
, read
, write
——这意味着我们可以读取文件的内容并输出到其他文件或标准输出等。
内核漏洞
如果沙盒的seccomp
被正确配置,攻击者很难发起有用的攻击。但是,用户仍然可以调用在白名单中的系统调用。如果内核中含有漏洞,通过系统调用,攻击者就可能利用这些内核漏洞。
听起来很玄乎,但其实内核与普通软件一样,都是代码,都或多或少存在漏洞,单是2019年一年,就有超过30个Chrome沙盒逃逸,其中大部分利用了内核漏洞。
namespace——现代解决方案
命名空间是 Linux 中可用的功能,用于隔离不同系统资源方面的进程。在Linux中有很多namespace,包括:
- mnt(挂载点,文件系统)
- pid(进程)
- net(网络堆栈)
- IPC(系统 V IPC)
- uts(主机名)
- 用户(UID)
等。使用挂载命名空间、pivot_root可以让用户只能访问原本文件树的一部分。
原有root挂载点未删除
一般会将旧的根目录挂载到沙箱内某个挂载点,然后使用pivot_root
将根目录换成新的“根目录”,然后再将该挂载点和目录删除。如果忘记删除原有挂载点,则沙箱形同虚设。
共享文件系统
如果沙箱和宿主共用文件系统(沙箱对文件系统有写权限),那么沙箱用户就可以利用自己的root权限来在沙箱外提取。
例如,在沙箱内:
1 | chmod 4755 /bin/cat |
那么在沙箱外的任何用户就都可以以root身份运行cat,这意味着可以读取任何文件。
先前打开的资源未关闭
与chroot()
中讨论的一致。
setns()和/proc
setns()
:
1 | int setns(int fd, int nstype); |
fd
参数是下列两者其中之一:
• 指向/proc/pid/ns/
目录中的一个链接(或绑定挂载到此类链接)的文件描述符;
• 一个进程文件的句柄
nstype
视情况而定。
setns()
系统调用可以将当前进程的命名空间切换成其他进程的命名空间。
在bash中,可以:
1 | PID=149 |
这样当前bash就会拥有和PID为149的进程一样的挂载命名空间。假如这个进程是沙箱外的,那么当前bash就有了沙箱外的挂载命名空间,意味着沙箱内用户可以访问完整的文件树。
也就是说,将原有的/proc
挂载到沙箱中,且沙箱外有其他进程未关闭的情况下,是非常危险的!
Docker
Docker容器 vs 沙箱
Docker容器和沙箱有许多相似之处,例如:
环境隔离
两者都与宿主系统产生了一定的隔离,起到了限制和保护作用
轻量
相比于虚拟机,两者都称得上轻量
但两者是截然不同的技术:
隔离级别:
Docker容器提供了一种相对较高级别的隔离,包括文件系统隔离、进程隔离、网络隔离等。容器之间通常是相互隔离的,但它们仍然在同一个操作系统内核上运行。沙箱通常提供更加细粒度的隔离,通常是为了限制单个应用程序或代码的权限。沙箱环境可以是单进程的,不涉及多个容器或应用程序的协同工作。
用途:
Docker容器是用于构建、打包和运行应用程序的独立、可移植的环境。它们旨在在不同的环境中一致地运行应用程序,包括开发、测试和生产环境。Docker容器通常包括应用程序及其依赖项,并提供了隔离、版本控制和自动化部署的能力。沙箱是一种安全机制,用于隔离和限制运行在其中的代码或程序的能力。沙箱旨在提供一种受限制的执行环境,以防止应用程序或代码对系统或其他应用程序造成损害。它通常用于执行不受信任的代码或对应用程序进行测试,以减少潜在的风险。
隔离技术:
Docker容器使用容器化技术,如Docker引擎,通过Linux命名空间、控制组等技术提供隔离和资源管理。沙箱可以使用各种技术,包括操作系统级别的虚拟化、chroot、Seccomp、AppArmor等,具体取决于实现。
Docker安全
除了在沙箱技术中提到的namespace, seccomp等,Docker还使用capabilities, control groups等来Linux提供的功能来提升安全性。
- cgroups:
- 资源隔离:Docker使用 cgroups 来隔离容器的资源使用,包括CPU、内存、磁盘I/O等。每个容器都可以分配一定的资源配额,以确保它们不会互相干扰或抢占主机上的资源。
- 资源限制:cgroups 允许设置容器的资源限制,例如限制 CPU 使用率、内存使用量等。这有助于防止容器滥用主机上的资源,提高整个系统的稳定性。
- 资源监控:通过 cgroups,你可以监视容器的资源使用情况,以便进行性能调整和资源规划。
- capabilities:
- 最小权限原则:Docker 使用 Linux 的 capabilities 功能来确保容器中的进程以最小权限原则运行。capabilities 允许我们将权限分配给进程,而不需要完全的 root 权限。这样可以减小潜在的攻击面。
- 降低特权:Docker 默认情况下会剥夺容器中的进程一些敏感的权限,如修改主机的网络配置或访问主机的设备。这有助于降低容器中运行的进程对主机的潜在威胁。
值得一提的是,如果主机 /proc
目录被挂载在 docker 容器中,而且容器的capabilities
配置了 CAP_SYS_ADMIN
(很高的权限,例如可以挂载文件系统),那么我们能够很轻松的从容器中逃逸。
条评论