作者:Matt Borland
翻译:nixe0n
简介
chroot牢笼(jail)概念综述
Postfix精灵进程分析
一个禁锢(jail)HOWTO:icecast
第一步:把icecast安装在牢笼(jail)环境中
第二步:配置牢笼(jail)环境
第三步:为这个精灵建立一个chroot包装
那些地方不能用牢笼环境
结论
简介
我们经常会听说计算机遭到基于Inte.net的远程攻击。通常位于攻击最前线的是一些服务器软件,例如:WEB、邮件和DNS,这些服务通常是通过监控进程(daemon,也可以叫作精灵进程)来提供的。这些服务即使是位于防火墙之后,也需要面对各种探测和攻击,以至让攻击者获得系统访问权限。大多数的恶意攻击者都会在系统安装rootkit,以便能够进行通常的系统操作。
一般情况下,这些rootkit和病毒需要使用目标系统的可执行文件、动态连接库以及配置文件。比如:一个安装在linux主机上的rootkit可能需要访问/bin/sh或者能够读写/etc目录下的系统配置文件。而且一旦系统被攻击者侵入,攻击者获得了root权限,就可以运行修改任何自己需要的文件。最近的lion、ramen蠕虫对BIND的攻击就可以证明以上观点。
但是如果你对精灵进程(daemon)进行限制,只允许其访问必要的文件,而不能访问整个文件系统,又将如何呢?这样就可以使我们的安全不再依赖于daemon软件开发人员的安全水平。
幸运的是,UNIX系统能够提供这样一种机制实现上面这种想法。在最近的BIND攻击中,Thaumaturgix, Inc的IT管理员William Cox提出了“在chrooted的环境中运行服务器进程,减小你的暴露程度(The best way to limit your exposure is to run the server in a @#chrooted@# environment))。这种chrooted环境通常叫作一个chroot牢笼(jail),实际上这种概念已经存在了很长时间了,但是一直没有被软件开发人员和系统管理人员充分利用。
chroot牢笼(jail)概念综述
实现一个chroot jail包括两种方式:
限制进程只能访问整个文件系统的一个子集。
以较低的权限运行服务进程。
这两种方式是由一些标准的系统调用来提供的。首先,是chroot(),这个系统调用能够限制进程对文件系统的访问。即使某个进程是以root的权限运行的,它对文件/目录的访问也都是相对于chroot()系统调用限制的整个文件系统的子目录。
例如,如果一个进程以/var/sample/jail为参数执行了chroot()系统调用,现在它要访问一个文件:/etc/passwd,但是操作系统最后会把这个访问请求替换为对 /var/sample/jail/etc/passwd的访问。chroot系统调用只能以root权限调用。
降低进程权限的系统调用主要包括:setuid()、setgid()和setgroups()。setuid()指定进程的实际和有效用户权限;而setgid()指定进程的实际和有效组权限。setuid()系统非常重要,一旦通过它进入较低的权限运行,进程将无法自己重新获得root权限。setgroups()定义进程的追加组成员关系。除此之外,还有一些其它的相关系统调用,用户可以从自己系统的手册页中获得相关文档。
其它种类的操作系统可能还有另外的进程束缚方法。例如在FreeBSD中就有一个jail()函数。为了使你更好地体会到jailing的好处,我将通过一个典型的rootkit加以说明,在不同的权限下以及有没有被限制在jail环境中rootkit对系统的危害是不同的。在此,要时刻记住一件事情,当一个服务被侵入,攻击者就能够拥有服务进程所具有的权限。请看下面这个表格:
+-----------------+---------------------------------------\
| | | |
| | running as root | running non-root |
| | | |
+-----------------+-------------------+-------------------|
| | | |
| entire fs | (a) | (c) |
| | | |
|-----------------+-------------------+-------------------|
| | | |
| jailed fs | (b) | (d) |
| | | |
+---------------------------------------------------------/
运行于(a)类(进程以root权限运行并且可以访问整个文件系统)状态下的进程是rootkit的首选目标,侵入这样的服务,攻击者可以修改二进制可执行文件,例如:/bin/ps、/bin/netstat等,打开特权网络端口,读取任意的系统文件包括:/etc/shadow文件。最糟糕的是,他们能够对系统进行完全的破坏,比如:rm -rf /。运行于这种状态,最为常见的一个例子就是sendmail(。
如果服务进程处于(b)类(root权限、牢笼环境)状态下,一般的rootkit就可能无法正常操作,因为它们一般需要一个shell(/bin/sh)和一些基本的命令,例如:/bin/rm和/bin/cp。然而,在这种状态下,rootkit照样可以打破牢笼(jail)。因为服务进程以root权限运行,攻击者可以通过攻击代码直接执行一些允许root执行的操作(例如:直接引用牢笼之外文件系统的索引节点)。因此,这种状态虽然比(a)类状态安全(门槛高的多),但是也无法对服务提供足够的保护。
服务进程处于(c)类(非root权限,可以访问整个文件系统)状态下,对整个系统的威胁比(a)状态小,因为毕竟攻击者无法直接获得root权限。但是,如果服务进程处于这种状态下,攻击者一旦侵入这个服务,就可以执行一些命令,获得一个shell进行交互,然后很可能通过本地攻击获得root权限。即使攻击者不能获得root权限,也可以访问到系统的绝大多数配置文件和其它信息,例如:用户的电子邮件帐户信息(大量的垃圾邮件将蜂拥而至)。
最后,处于(d)类(非root权限,jail环境)状态下的服务进程受到的限制最为苛刻,这样攻击者根本没有机会执行shell,也不可能获得大量的系统信息。而且,对系统进行的所有破坏都被限制在jail环境中。在这种状态下,最大的威胁就是攻击者在这个jail环境中防止一个可以被jail环境之外的进程访问的可执行文件,从而使攻击代码扩散出jail环境。因此,你应该经常监视你的牢笼,看一下里面的囚犯是否逃出来了:P。
下面我们将举一个现实的例子进行说明。
Postfix精灵进程分析
Postfix()是一个邮件传输代理(Mail Transfer Agent)。它允许用户收/发SMTP邮件,把给本地用户的邮件投递到用户主机。Postfix是sendmail的一个变通,作者Wietse Venema对安全问题倾注了很大的关注。这个软件是一个非常典型的所谓守纪律精灵(disciplined daemon),可以安装在一个chroot牢笼(jail)环境中。我相信理解了Postfix是如何工作的,你就可以实现一个简单的安全精灵(daemon)。
Postfix从一个叫作master的程序开始执行,master以root权限启动,然后继续以root权限运行。不过,master本身非常小,其作用这是spawn进程,这些进程大部分会被放在一个chroot牢笼(jail)环境中执行实际的工作。通过这种方式,以root权限运行的代码数量非常有限。
下面我们看一看master是怎样和smtpd进行交互的。master一旦检测到一个向25号端口(SMTP的监听端口)的连接,它就会执行smtpd。下面我们就看一下postfix使用的一些jailing方法,这些代码在src/util/chroot_uid.c文件中。在讲述过程中,我们把/var/spool/postfix作为chroot牢笼(jail)环境的根目录:
+-----------------------------+-----------------+--------------------\
| | user/group | filesystem |
+-----------------------------+-----------------+--------------------|
| master is run on startup | root/root | / |
|-----------------------------+-----------------+--------------------|
| master opens port 25 | root/root | / |
|-----------------------------+-----------------+--------------------|
| master detects connection | root/root | / |
|-----------------------------+-----------------+--------------------|
| master forks smtpd | root/root | / |
|-----------------------------+-----------------+--------------------|
| master continues execution | root/root | / |
|-----------------------------+-----------------+--------------------|
| smtpd inherits permissions | root/root | / |
|-----------------------------+-----------------+--------------------|
| smptd calls setgid() | root/postfix | / |
|-----------------------------+-----------------+--------------------|
| smptd calls initgroups()* | root/postfix | / |
|-----------------------------+-----------------+--------------------|
| smtpd calls chroot() | root/postfix | /var/spool/postfix |
|-----------------------------+-----------------+--------------------|
| smptd calls setuid() | postfix/postfix | /var/spool/postfix |
|-----------------------------+-----------------+--------------------|
| smtpd processes connection | postfix/postfix | /var/spool/postfix |
|-----------------------------+-----------------+--------------------|
| smtpd continues listening | postfix/postfix | /var/spool/postfix |
+-----------------------------+-----------------+--------------------/
* initgroups()类似于setgroups();设置多重组成员关系。
最后,smtpd进程的状态如下所示(503是postfix用户/组的ID):
#cat /proc/<pid>status
Name: smtpd
State: S (sleeping)
Pid: 1301
PPid: 665
TracerPid: 0
Uid: 503 503 503 503
Gid: 503 503 503 503
FDSize: 256
Groups: 503
[...]
处理完这个连接后,smtpd精灵会继续监听SMTP连接,如果在指定的时间内没有连接,就超时退出。
从上面的过程你可以对进程状态的演变有一个大体的了解。在整个生命周期中,smtpd从(a)状态开始,接着进入(b)状态,然后快速地进入(d)状态,最后在(d)状态中运行。实际上,这是把进程放到正确的chroot牢笼(jail)环境的标准步骤。
一个jail HOWTO:icecast
理解了这些背景知识非常有用,但是怎样对一个精灵进程进行禁锢?而且,对一个精灵进程进行禁锢,是否还要取决于精灵进程本身是否愿意?为了回答这些问题,我将讲述一个实际的精灵禁锢过程,虽然它比较简单,但是其作者并没有给出一个精灵禁锢文档。不过请注意:一些流行的精灵系统例如:BIND,已经有了如何创建一个chroot牢笼环境的文档。
在我们的例子中,我选择的程序叫作icecast()。icecast是一个音频流中继服务器,它从一个基于网络的客户程序(例如:winamp)获得音频流,然后接收一定数量听众的连接,让这些听众收听连续的音频流。我想在自己的机器上运行这个程序,但是我恐怕这个服务程序存在安全缺陷,所以想对其进行限制。
我们看一下这个服务起始的权限,3这个精灵进程打开两个不需root权限的端口,一个用于接收音频流,而另一个等待客户程序的连接。在运行过程中,还可以通过一个本地控制台对其进行维护,不过这不是必须的。除此之外,在运行过程中icecast还要维护一个日志文件。
这个程序以非root权限运行,这很好。但是,如果不对其进行禁锢(jail),在理论上如果它被攻击者侵入,让攻击者获得了一个shell,攻击者就可以窥探我的系统,甚至通过其它的本地攻击获得root权限。因此,我要对icecast进行禁锢(jail)。
下面是我们的禁锢过程:
把这个精灵安装到指定的目录,在保证它能够正常运行的情况下,为其分配尽可能小的文件访问权限。把所有不必要的东西从这个牢笼(jail)目录删除。
对这个牢笼环境进行必要的配置。这个环境一般包括:精灵运行的动态连接库文件,一个本地的/dev/null设备,可能还需要一些本地的时间信息。
如果必要,还要建立一个执行chroot和放弃教高权限的函数包装(wrapper)。如果这个精灵能够想postfix的子进程那样自己实现上述功能,你就可以不必自己来做了。要注意:一定要把这些包装(wrapper)放在牢笼(jail)环境之外。
第一步:把icecast安装在牢笼(jail)环境中
首先,我们把所有icecast的文件安装到/usr/local/icecast目录,在我们的例子中,/usr/local/icecast是icecast牢笼(jail)的根目录。你安装牢笼(jail)环境的地方非常重要,可能应该是一个只读的文件系统。
下面是其安装目录的内容,icecast安装目录的所有者是root
drwxr-xr-x 2 root root 4096 May 6 14:38 bin
drwxr-xr-x 2 root root 4096 May 19 18:43 conf
drwxr-xr-x 2 root root 4096 May 6 14:38 doc
drwxr-xr-x 2 root root 4096 May 6 14:41 logs
drwxr-xr-x 2 root root 4096 May 19 16:41 static
drwxr-xr-x 2 root root 4096 May 6 14:38 templates
功过阅读icecast的文档和一些实验,我们知道icecast正常运行需要以下的文件/目录权限:
可执行:bin/icecast
可写:logs目录
可读:conf、static和templates目录
现在,我决定建立一个叫icecast的用户,使用这个用户来运行icecast。在执行这个服务时,需要向logs目录中写入日志,而我又不想让任何人都能够写这个目录,因此才建立这个新的用户。
#useradd icecast -s /bin/false
然后,你应该在/etc/passwd中发现如下条目,而且这个帐户处于锁定状态,无法登录:
icecast:x:505:505::/usr/local/icecast:/bin/false
现在,我们需要决定需要那些文件以及它们应该放在哪里。首先,我们需要改变logs子目录的权限,还可以把没有必要的子目录doc删除。
icecast不象postfix的子进程,能够自己调用chroot系统调用,因此为了在执行chroot包装(wrapper)程序之后,能够执行icecast,我们需要把bin/icecast可执行文件放在这个牢笼(jail)环境中。然后,我们需要编写一个chroot包装程序,把他放在牢笼(jail)环境之外。
除此之外,icecast还需要在conf目录配置文件。我们假设icecast已经被安装在/usr/local/icecast目录中。
第二步:配置牢笼(jail)环境
这一步的目标是找出icecast运行需要的动态连接库并把它们安装到这个牢笼(jail)环境,这一部分可能因系统而异。没有这些动态连接库,icecast将无法执行,而且这个原因显示的执行错误信息也让人摸不着头脑,比如:/bin/icecast: File Not Found
在Linux系统中,你可以运行ldd程序来查看icecast需要的动态连接库以及它们在文件系统的位置。例如(所有的命令我们都是在/usr/local/icecast目录下执行的):
$ ldd bin/icecast
libm.so.6 => /lib/i686/libm.so.6 (0x40022000)
libpthread.so.0 => /lib/i686/libpthread.so.0 (0x40046000)
libc.so.6 => /lib/i686/libc.so.6 (0x4005b000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
现在,我们把这些动态连接库都复制到这个牢笼(jail)的相似位置:
# mkdir -p lib/i686
# cp -pi /lib/i686/libm.so.6 /lib/i686/libpthread.so.0 /lib/i686/libc.so.6 lib/i686
# cp -pi /lib/ld-linux.so.2 lib
这样,这些动态连接库就被复制到了牢笼(jail)的/lib目录下,也就是说bin/icecast能够在牢笼中执行了。
不过,这样还不够,在任何的牢笼环境中都建立一个/dev/null设备是一个非常不错的想法:
# mkdir dev
# mknod -m 666 dev/null c 1 3
这一步要谨慎,并确信你已经正确地建立了设备节点。
许多服务可能需要系统的/etc/localtime文件。最后,我们需要把/etc/localtime文件复制到/usr/local/icecast/etc/目录中,并填加一个到/usr/local/icecast/usr/lib/zoneinfo的符号连接:
# mkdir etc
# mkdir usr/lib
# cp /etc/localtime etc
# ln -s /etc/localtime usr/lib/zoneinfo
现在,我们为icecast精灵准备的牢房已经装修的差不多了,里面有如下目录:
drwxr-xr-x 2 root root 4096 May 6 14:38 bin
drwxr-xr-x 2 root root 4096 May 19 18:43 conf
drwxr-xr-x 2 root root 4096 May 19 18:01 dev
drwxr-xr-x 2 root root 4096 May 6 14:38 doc
drwxr-xr-x 2 root root 4096 May 22 14:41 etc
drwxr-xr-x 2 root root 4096 May 19 15:30 lib
drwxr-xr-x 2 icecast icecast 4096 May 6 14:41 logs
drwxr-xr-x 2 root root 4096 May 19 16:41 static
drwxr-xr-x 2 root root 4096 May 6 14:38 templates
drwxr-xr-x 3 root root 4096 May 22 14:42 usr
第三步:为这个精灵建立一个chroot包装
记住:执行chroot需要root权限。
你可能会想,我首先执行/usr/sbin/chroot,然后执行/bin/su,接着执行/bin/icecast,就能够把icecast精灵放到牢笼环境。但是别忘了,一旦执行了/usr/sbin/chroot,你就不能再访问整个文件系统了,也就是你将无法执行su程序了。把su也放到这个牢笼环境中也是可以的,但是其关联文件非常复杂,很难维护,而且su是一个SUID程序,放到了这个环境中,肯定会降低这个环境的安全性。
编写一个简短的C程序,通过这个程序调用必要的jail系统调用,然后执行icecast服务,是一个比较容易的解决方案。这个程序将在这个牢笼之外执行,它需要做以下事情:
执行chroot系统调用,进入牢笼。
把组识别符和组成员关系设置为icecast。
把用户识别符设置为icecast。
执行/bin/icecast。
有一点要注意:这个程序的权限不能设置setuid/setgid位。我们的目的是以超级用户的权限调用setuid()/setgid()系统调用降低系统的权限,和把程序的权限设置为SUID或者SGID有极大的差别。把程序的SUID/SGID置位表示程序以拥有者的权限运行,通常意味着提高程序运行的权限。
下面我提供一个简化的chroot包装程序。请注意:在实际过程中应该注意对每个系统调用错误的处理。
=================================================
#include
#include
main (argc, argv) {
int gidlist[] = {505};
chroot("/usr/local/icecast");
chdir("/");
setgid(505);
setgroups(1,gidlist); // also, could use initgroups
setuid(505);
execl("/bin/icecast","/bin/icecast",NULL);
}
==================================================
这里也要注意,不要把这个程序放在后台执行。在这个例子中,我只要修改icecast的配置文件就能够使icecast在后台运行。
现在我们的牢笼已经做好了,只要执行以上的包装程序,就可以把icecast囚禁到里面了:P。上面的包装程序你可以放在任何自己喜欢的位置,只是不要放在/usr/local/icecast目录下就可以了。你也可以在自动启动脚本中加入这个包装程序,以便icecast能够在系统启动是自动运行。
那些地方不能用牢笼环境
有些情况下,不太可能实现对精灵进程的禁锢管理。
让我们看一下apache WEB服务器。apache的开发者在开发这个软件时就非常注意其安全问题,httpd进程通常以root权限启动,然后其派生出来的进程都是在比较低的权限(通常是nobody)下处理运行的,就象master/smtpd之间的关系。也就是说,处理客户请求的httpd进程是在c类状态下运行的。
虽然你也可以禁锢Apache WEB服务器,但是在某些情况下,这项工作几乎是无法完成的。例如:在WEB服务器中,你使用了PHP模块,PHP模块的特征极为丰富,其动态连接库极为繁杂,在这种情况下,创建一个牢笼环境就有些得不偿失了。
还有一种情况就是如果精灵进程确实需要对这个文件系统的访问权限,例如postfix的某些组件确实需要root权限把邮件投递到用户的目录。
结论
我发现很少有人对实现一个chroot牢笼环境倾注注意力,希望通过这篇文章能够让大家知道禁锢精灵进程的好处,并对大家自己实现牢笼环境有所帮助。
参考:
(1) Fearnow, Matt. "Lion Worm." SANS Global Incident Analysis Center, April 2001.
(2) Radcliff, Deborah. "Stuck in a BIND" Computerworld, February 2001.
(3) FreeBSD, Inc. "jail() man page" April 1999.
(4) Burr, Simon. "How to break out of a chroot() jail." January 2001.
(5) Wunsch, Scott "Chroot-BIND HOWTO" Linux Documentation Project, September 2000.
(6) Deatrich, Denice. "How to @#chroot@# an Apache tree with Linux and Solaris." February 2001.
Moen, Rick "Attacking Linux" LinuxWorld.com, August 29 2000.
Fennelly, Carole "Real Hackers go to Usenix" Unix Insider, November 17 2000
Brumley, David. "invisible intruders: rootkits in practice" Usenix, November 1999.
Miller, Toby. "Analysis of the T0rn rootkit" GIAC, 2000.
文章来源于领测软件测试网 https://www.ltesting.net/