|=-----------------------------------------------------------------=| |=-----=[ D O N O T F U C K W I T H A H A C K E R ]=-----=| |=-----------------------------------------------------------------=| |=------------------------[ #3 File 0x02 ]-------------------------=| |=-----------------------------------------------------------------=| |=------------------=[CVE-2013-1858 exploit analysis]=-------------=| |=-----------------------------------------------------------------=| |=-------------------------=[ By JU ]=---------------------------=| |=-----------------------------------------------------------------=| 提要: 2013年3月13日,Suse的安全研究员Sebastian Krahmer发出一封名为: "CLONE_NEWUSER|CLONE_FS root exploit" 的邮件,暴出3.8版本的Linux内核存在一个提权漏洞,并给出了poc: http://www.openwall.com/lists/oss-security/2013/03/13/10 该exploit利用了3.8内核允许*普通用户*利用clone(... CLONE_NEWUSER | CLONE_FS, ...)创建新进程的漏洞,巧妙实现提权。该漏洞利用的精妙之处在于:只是利用两个概 念上的缺陷,不需要费尽心力写复杂的shellcode, 仅若干个普通API就实现了提权。而 且, 这个exploit完美地演绎了一个程序如何在不同的euid下3次运行,每次进行不同 的动作。三次运行环环相扣,一步步帮助获得系统的权限。 另一方面, 给力的是,当天内核开发者就及时给出了fix: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=e66eded8309ebf679d3d3c1f5820d1f2ca332c71 漏洞分析: 1.CLONE_NEWUSER与CLONE_FS狼狈为奸。 随着Linux 3.8版本释出,标志历经多年开发(从2007年的2.6.23版本开始)的用户名字 空间(user namespace)的开发工作基本完成。这是第6个完成的内核名字空间( http://lwn.net/Articles/531114/#series_index)。它们是实现内核容器(container) 中的一部分。内核容器是用于分隔资源,系统监管以及虚拟化的一个轻量级工具,简单 来说,就是把系统资源进行分隔,这样不同容器中的资源互不感知对方,实现有效分隔 。而用户名字空间的完成,有着重大意义,因为每个普通用户都可以建立自己的名字空 间。每一个用户名字空间中,有一套独立的uid系统,这意味着每个空间中有各自的roo t用户!也就意味着,在这个名字空间中,进程可以拥有任意权限(capabilities),包 括调用chroot切换根目录。而本来分隔的名字空间类似sandbox,不会带来太多安全问题 。但是,3.8内核允许普通用户创建名字空间! 对于CLONE_NEWUSER来说,它意味着创建的新进程需要独立的用户名字空间。 至于CLONE_FS标志,它表示创建的子进程要跟父进程共享文件系统属性,比如有相同的根目录... 独立与共享?!这隐隐约约就带来了坏味道!于是,隐患已经埋下,潘多拉的盒子即将被打开...... 2.chroot, 罪恶之*根* 前面讲了,两个奇怪的标志一结合,立马带来了隐患。一个是独立的名字空间,一个是 共享的文件系统空间,如何让封印于盒子里的怪兽逃出生天呢。工具之一就是chroot! chroot是在Linux系统中发挥根目录的切换作用的命令,它也带来系统的安全性等好处 。比如有玩过lfs或gentoo的同学都知道,当在host环境下搞定新环境的工具链后,一 条chroot命令就可以切换到新的环境,然后筚路蓝缕,开始打拼另一片天空。 如下: /( / : 原来的根目录) | -------------------- | | | | bin lib home/lcx ...... | newroot(/home/lcx/newroot : 新的环境的根目录) | ----------- | | | bin lib usr ..... 当执行: chroot /home/lcx/newroot 就将原来的/home/lcx/newroot变为新环境的根目录。此时,原来的文件系统被掩盖。 压迫不再,曾经的旧民翻身,新的政治秩序铺开。 那此时如果还用原来的名号,会发生什么情况呢?很简单,发生了"坐标系平移"。比如 旧环境的/bin/su,将被映射成新环境的/home/lcx/newroot/bin/su; 旧环境的/lib/ld-linux.so.2,将被映射成/home/lcx/newroot/lib/ld-linux.so.2. 好,咱花开两朵,先表一枝。这一厢先按下,稍后再叙。 3.动态链接器ld-linux.so, 引狼入室 动态库顾名思义就是程序运行才加载依赖的库。在linux上,这是由一个叫ld-linux.so 的家伙来执行加载动态库这项工作的。粗略地讲,ld-linux.so把程序加载与开始执行之 间活动,搜索程序用到的库,并映射到程序的进程空间,以及完成一些符号解析等dirty job, 然后,才把程序流控制权交到程序入口点开始执行。 此外,关于ld-linux.so还有一点需要强调, 它拥有与所执行程序相同的权限。意思就是 ,当程序以有效用户ID(euid)为root的ID运行时,那ld-linux也以root权限运行. 那ld-linux.so在哪? 它不应该包含在程序的可执行文件中。因为,如果系统有上千个 要用到动态库的程序, 哪岂不是有上千份拷贝. 其实, ld-linux.so是在系统中。在程序运行时,它首先被加载,然后由它加载动态库。 $ ls /lib | grep ld-linux (或ls /lib64 | grep ld-linux) 可以看到,存在/lib/ld-linux.so.2(我系统32位的,纯64位系统会发现它在/lib64中)。 由它的路径与名称看来,它本身就是一个库(用file命令查它户口,发现它是shared object, ELF格式中的一种:共享对象,其它库文件同样也是一这种格式)。 这里面隐含着,系统能加载ld-linux.so, 说明系统知道这个ld-linux.so的路径,它藏在哪呢,就在程序执行映象里: $ readelf -l /bin/ls 以上命令读取ls程序的可执行映象,能发现输出结果中出现有Requesting program interpreter:/lib/ld-linux.so.2的字样。 这里面,它作了一个假设: 我要找的动态链接器ld-linux.so它位于*根目录*下的lib目 录中。它硬编码了路径! 万一运行过程中这个根目录变了呢,这路径不就指到别的地方 去了?! 存在这种可能吗? 可能! 这就是这个exploit的妙处所在。 4. 打开魔盒 上面说的这个exploit妙在,它巧妙地在程序运行过程中,改变了根路径! 改变了根路径后,它执行了一个setuid程序su(运行ls -l /bin/su, 你会看到它的权限 是这样的:-rwsr-xr-x, 其中发现有一位变成了s。这个程序就叫setuid程序。至于这个 s的含义,涉及到真实用户ID与有效用户ID的关系,网上有详细解说,读者可以在网上找 到相关方面文章), 而这个su在运行前是先要运行ld-linux.so来加载动态库。 前面说了, ld-linux.so路径是硬编码成/lib/ld-linux.so.2的。但是, 现在根路径已 经变了! 所以, 此时的/lib/ld-linux.so.2已经不是指向真正的ld-linux.so了。它现 在指向了改变后的根目录下的lib/ld-linux.so.2。而这个位置, 放的就是exploit程序 自身! 这当然不是凑巧的,而是在改变根路径过程中使用了技巧而实现。 所以, 现在相当于又运行了exploit一次。不过, 这次的有效用户ID(EUID)是0。因为 前面说它执行了一个setuid的程序su。而在第3节说过, 执行ld-linux.so时, 它拥有跟 su一样的权限: 即EUID为0用户,也就是root的权限。这就是为什么要选用一个setuid权 限的程序的原因。 然后, 重新运行的exploit进程做了啥呢? 它把自己的uid设为0, 即让自己成为root用 户, 然后, 再运行一次bash。此时, 这个shell就是一个root权限的shell了。僭权成功 !!! 这就是这个exploit大概的思路, 至于是如何改变根路径的, 是重头戏, 有兴趣的读者 可以继续往下看。下面是对这个exploit更详细的讲解. ********************* 分隔线 ********************* 它是这么做的: 我们先设定这个exploit程序叫evil, 它的路径不妨设为/home/lcx/evil。然后我们进 入到/home/lcx目录,执行 $ ./evil 我们以普通用户运行这个程序。一切都还显得是风平浪静。我们称此时的进程为A。 A莫名其妙地做了这几个动作. 1. 读取/proc/self/exe获取自己的路径,也就是/home/lcx/evil(这不是多此一举,因为 程序自身并不知首自己的路径,而即使我们把程序放别的地方,它还是能准确找 己的巢)。 2. fork了一个进程B, 然后它就去睡觉, 间隔1秒醒来看evil这个程序文件的用户 ID是否变成root了。当然, 现在还不是,所以它去睡觉~~~ 创建出来的B干嘛呢? 1. 在当前目录(/home/lcx)创建了一个chroot, 也就是/home/lcx/chroot。 2. 在chroot下再建了两个目录,是为/home/lcx/chroot/bin和 /home/lcx/chroot/lib。 3. 在/hom/lcx/chroot/bin目录下, 它生成一个链接/home/lcx/chroot/bin/su, 指向原来/bin/su。 4. 在/hom/lcx/chroot/lib目录下, 它生成一个链接 /hom/lcx/chroot/lib/ld-linux.so.2, 但是, 它指向evil自己:/home/lcx/evil。 5. 调用pipe API生成一个管道准备通信。和谁通信?这个稍后说。 不妨以图示说明: / | ------------------------ | | | bin lib home/lcx ------ | | | | su ld-linux.so chroot evil ^ | ^ | ------- | | | | | | bin lib | | | | | |________________ su ld-linux.so___| 觉得跟上面介绍chroot一节时画的图有些类似? 没错, chroot要登场了, 前面这些莫名 其妙的动作不过是为舞台布景! 不过, 我们现在还是普通用户, 是没有root权限执行chroot的(是的,evil程序就是要获 取root权限, 但现在还不是)。但是,没有权限也要创造权限, 方法就是利用clone的 CLONE_NEWUSER, 生成一个新的子进程C。前面说过,它生成的子进程将在一个新的用户 命名空间里, 它可以做任何事, 包括chroot。 但是, 不要忘了, 现在我们是在子进程C的新的名字空间中, 再闹得天翻地覆也没用。 所以, 另一个标志出来救场了, 就是CLONE_FS。利用它我们可以巧妙地把魔鬼释放到盒 子外面! 前面说了这个标志是让子进程与父进程共享文件系统的属性的。那么, 当我们 在子进程里chroot后, 改变了根目录。既然是共享, 父进程的根目录也被改变了! 进程C做了什么好事呢? 1. C从管道中读取一个字符。我们前面说过B创建了一个管道。管道是Linux中父子进 程通信的一种方法。当一端读的时候,如果没数据,会被阻塞。所以,C从管道中读 取什么信息不重要,关键是,C必须得等到它的父进程B往里面写入数据。 那B写入数据前做了啥? - 它创建了UID映射,简言之。它写了/proc/${Pc}/uid_ma文件, 把B的uid映射到 到C的uid。这里${Pc}是指C的pid, 它是从clone返回的。 - 然后往管道中写了个字符。 - 调用waitpid()等待它的子进程C。 所以,等c读到这个字符时,实际上uid映射已经完成了。有什么用?接着看。 2. C用setuid()把自己uid设为root(记住此时C是在自己的名字空间中,所以是 允许它这么做的)。由于前面作了uid映射,所以此时B的uid也是root! 3. 当然,C最后调用chroot()切换了根目录! 前面说过, A每睡1秒就醒来看自己的uid是不是变成root了。现在,终于变成root了! 于是,A开心地再一次执行了evil这个程序(这是第2次运行这个程序)。此时,它的有效 用户ID(euid)是0,于是,它把/lib/ld-linux.so.2的权限设为04755。这个值表示, 这个程序现在是一个setuid程序了. 前面说过,C切换了根目录,并且CLONE_FS标志是 表示共享文件系统属性的,所以,这时修改的ld-linux.so.2其实就是evil!(参见上图) 。也就是, evil从一个普通的程序变成一个setuid程序! 然后,这个进程退出。 此时,C完成使命,它也退出。 另一方面,在管道中写入数据后就调用waitpid()等待C的B终于等到C退出, 它运行了一 个setuid程序su(前面通过uid映射,B此时的uid是0了,所以它能运行su)。如前面所说 , 这个su要调用/lib/ld-linux.so.2, 而当前根目录已经变为/home/lcx/chroot, 所以 ,此处调用的是/home/lcx/chroot/lib/ld-linux.so.2。而前面已经将/home/lcx/chroo t/lib/ld-linux.so.2链接到evil自身(见图). 所以,此时运行的还是evil自身.(这是evil这个程序第3次被运行了!) 不同的是此时运行的evil已经是一个setuid程序, 运行它时euid就是0! 还有,要注意, 现在是第三次运行eveil, 它已经是在chroot环境外了(受chroot影响的只有C和B)。 然后, evil把自己的uid设为0, 再运行bash。这是一个有root权限的bash, 并且是在 chroot环境外, 僭权成功!