LWN:对Debian的file命令增加seccomp()安全防护

Debian尝试为file命令实施seccomp过滤以增强安全性,但遭遇与fakeroot及LD_PRELOAD机制的冲突,导致功能缺失与错误。讨论涉及如何平衡安全与兼容性,包括调整过滤器白名单与探测LD_PRELOAD的存在。

640

点击上方蓝色“Linux News搬运工”关注我们~

Hardening the "file" utility for Debian

By Jake Edge

译者注:本文有删节

Linux的"file"命令是一个非常应该做sandboxing(沙盒保护)的工具。它经常可能会处理不可信的输入源。不过近期人们试图对Debian里的"file"命令增加seccomp()过滤的工作触礁了,因为跟Debian环境里其他机制(主要是package的编译机制)有冲突。这个例子又一次证明seccomp()过滤机制很脆弱、难用。

这个讨论起自debian-devel mailing list上的一篇帖子,Christoph Biedl宣布他打开了file命令的sandbox功能。他希望其他Debian开发者留意一下是否碰到什么问题,也解释了这个改动的一些缺陷:“这个改动有一些缺点,导致一些功能缺失。例如查看压缩文件的内容(缺省是disabled,命令行参数-z和-Z)这个功能现在只能支持少数几种压缩方式:gzip(利用libz的这一批都一样),bzip2, lzma,xz。对其他格式进行解压缩的话就需要调用其他外界工具,从而会导致程序异常退出(SIGSYS)”

除此之外,他还碰到一个问题。如果运行环境里面使用LD_PRELOAD环境变量加载了其他一些非标准库的话,这时运行file命令会出错,因为这些非标准库可能会调用一些"file"命令本身不会调用的系统调用,从而被seccomp()过滤拦截。

而编译Debian安装包的过程通常需要FakeRoot,这样才能执行一些命令的时候让它们以为自己有文件系统操作特权,这个方法就可以用来避免真的授予特权。这样做了之后好处很多,例如tar包之类的文件创建时内含的文件属主就不用非要是执行Debian打包工具的用户ID了。Fakeroot内部会维护一个映射表,来记录它更改的owner, group, 文件权限等。这样就能把这些信息报告给访问者。具体实现机制就是在GNU C library (glibc)之前加载一个library来拦截文件操作。

所以fakeroot会先孵化出一个守护进程(名为faked),用来维护进程在fakeroot之类所做改动的状态。利用LD_PRELOAD加载libfakeroot库之后就可以利用System V (sysv) IPC通讯机制或者TCP/IP方式跟守护进程通讯。Biedl在他邮件里面也提到了一个bug report,是Helmut Grohne报告的在fakeroot内部运行“file"命令碰到的问题,当时是msgget()系统调用导致的问题。Biedl修改了Debian "file"命令的白名单来允许执行这个系统调用来解决问题,否则的话只能在file命令调用时加上--no-sandbox参数来关闭seccomp了。

不过,他的fix方案只涵盖了sysv IPC机制,如果要想让上面提到的TCP/IP机制也能正常工作,就需要按Grohne说的加更多白名单函数了。

而且,本来使用seccomp过滤的目的就是能禁止危险的IPC和网络操作,因此假如file命令被攻破了,这里打开IPC和网络操作白名单的话就显得很不合理了。Grohne建议不要放开system call,而是检查是否有LD_PRELOAD进行预先加载某些library,并且针对这种情况关闭sandbox。

Biedl不是完全同意,因为他担心像这样在生产系统上悄无声息的关闭了安全检测会很危险。他觉得倒是应该在编译环境下关闭过滤机制才合理。在debian-devel邮件列表上,同时也有不少人非常感谢Biedl打开这个过滤机制,Russ Allbery就希望看到更多地方使用seccomp,哪怕是缺省不打开也好,因为这样至少人们有机会能以更高安全等级来执行命令。例如他非常希望能禁止ghostscript执行程序或者写文件等等,这些虽然是PostScript的原生功能,但是对安全在意的人现在都会传入-dSAFER参数来降低安全风险,所以现有的例子里面其实本来就有类似功能,只不过麻烦一点,需要明确指定参数。

不过Biedl最终给大家报告了一个坏消息,他彻底关闭了"file"命令的系统调用过滤机制,因为他在那几天里收到了好几个bug report,不是很好修正,因此不得不先关闭了seccomp功能。

不过他也提到Grohne给出了一些可行建议:目前问题是file命令建立sandbox太早,因此需要允许众多系统调用。其实可以在加载file的目标文件并且访问过faked的database之后,再通过prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT,0,0,0)来打开SECCOMP沙盒。毕竟加载文件和faked database的代码危害较小,不去限制他们,也能避免导致fakeroot失败。

这种改法需要对file命令进行更多修改。Biedl会去进行实现。此外Colin Watson也给Biedl分享了他在打开seccomp()过滤机制时碰到的问题:“我当时在给man-db增加seccomp过滤机制(主要是为了防止groff等命令里的bug导致安全问题),碰到了众多麻烦的问题。最麻烦的是来自人们安装的众多第三方程序,有一些流行的Linux防病毒软件都需要使用LD_PRELOAD加载library来跟守护进程通讯,还有一个VPN程序也有这种行为,还有一个snoopy包会在每次execve的时候对/dev/log记录日志。我当时解决这些问题时折衷了一下,开放了一些尽量少的syscall调用,来减少人们给我报那些与我的程序无关的软件包导致的bug report。”

Vincent Bernat指出,seccomp()还是太脆弱了,跟glibc和kernel版本也总是绑定的太死。Philipp Kern觉得这些可以用自动测试来检测出来。Biedl也说他正在做类似的工作。

针对file命令,我们其实非常需要做安全保护,因为它会访问各种莫名来源的文件内容。不过用seccomp()过滤机制阻止攻击的方案目前在Debian上还是不够成功。实际上所有使用LD_PRELOAD的场景里运行的程序都很难进行seccomp()安全保护。那些仔细选择的白名单,很可能在换一个版本的glibc库或者kernel版本之后就失效了。

OpenBSD的pledge()系统调用实现就与seccomp()很不相同。开发者可以指定哪些syscall是允许执行的,不过粒度更粗,只能指定一类操作,例如stdio(多数文件操作),inet(IPv4和IPv6调用),proc(进程调用例如fork(),不过不包含execve因为属于exec类别)等等。因为不是针对某一个系统调用进行过滤,所以Linux的seccomp()机制碰到的问题就被避免了。并且OpenBSD的user space也不需要跟kernel版本绑定。

对file命令来说,OpenBSD会系统性的降低它的权限,通过调用多次pledge()调用来实现。首先关闭大多数系统调用,然后在处理命令行参数之后来开放少数几类。然后会fork一个进程来执行child()函数,会更进一步的降低权限,只拥有stdio和recvfd的权限。子进程会从附近城读取消息,每个消息都是包含一个需要检查的文件描述符(file descriptor)。这样这部分最有可能被攻破的代码就只有权限执行一些最少操作。

对Linux来说,seccomp()过滤机制看起来不太适合现存项目。最好能对每个项目先进行一次改动,把比较令人担心的代码(例如文件格式解析代码)和那些无法避免的代码(例如opening file)分割开来。不过这也就意味着现有代码需要被重写改头换面一番。LD_PRELOAD library进行的调用就是一类无法避免的操作,可能导致不得不退回到更弱的保护状态下去。这个方向目前还看不到尽头。

全文完

LWN文章遵循CC BY-SA 4.0许可协议。

极度欢迎将文章分享到朋友圈 

长按下面二维码关注:Linux News搬运工,希望每周的深度文章以及开源社区的各种新近言论,能够让大家满意~

640?wx_fmt=jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值