本篇介绍Linux与安全相关的三个特性,当正确使用他们时,将可能实现安全沙箱功能,特此记录~
chroot
这里涉及到进程的两个目录cwd
与root
(为与root用户区分,后写作rootDir
),前者表示当前的工作目录,后者表示此进程的根目录,正常来讲,在哪个目录执行的程序,cwd
就是该目录,而rootDir
总是文件系统的那个根目录,而C库提供两种函数分别对其进行更改:
int chdir(const char *path); //依据目录名改cwd int fchdir(int fd); //依据文件描述符改cwd int chroot(const char *path); //依据目录名改rootDir
其中对cwd
的更改要常见的多也要容易理解的多,而对rootDir
的更改应用就比较特殊了,当它指定一个目录作为新的rootDir
,由于此目录为根,它是最顶级目录,那么在文件系统中此目录的父目录等在此进程中将会不可见,例如下图,当新的rootDir
为/var/ftp/
则此时执行ls /
结果为bin etc pub
:
此特性可以做很多事,此处关注安全沙箱,它能通过将用户可使用的文件限定在一定范围来实现安全性,这种根目录限制也叫做chroot jail,注意它能做到一定程度上的安全但它并不是一个安全特性,意味着它是容易被突破的(叫越狱?)。使用man -a chroot
可以看到chroot
的两个定义,一个是可执行程序一个是库函数,他们都能做如名字所述操作-change root directory
!另外,这两个工具都只有拥有CAP_SYS_CHROOT
权能的用户才能使用它。
chroot命令
此命令能执行指定程序,并以指定的目录作为进程新的根目录:
NAME
chroot - run command or interactive shell with special root directorySYNOPSIS
chroot [OPTION] NEWROOT [COMMAND [ARG]…]
chroot OPTION
一个例子如下:
1 | ➜ test tree #test文件夹的目录结构 |
以上busybox和bash都是静态编译的,否则必须将依赖的动态链接库也复制到此目录。静态编译bash命令:
1 | wget http://ftp.gnu.org/gnu/bash/bash-5.0-beta.tar.gz |
特别注意--userspec=USER:GROUP
选项是极其重要的,一定不要直接一直以root身份运行程序。
同样的,构建’沙盒环境’只需要把允许的命令及其依赖复制到新建的rootDir即可,当然很多工具其实提供选项与chroot配合,使用这些选项将会更加安全与方便
chroot函数
此函数实现同样的功能,文档描述:
NAME
chroot - change root directory
SYNOPSIS
#include <unistd.h>
int chroot(const char *path);
1 |
|
另外,使用jailkit可以轻松的创建安全的jail环境,安装方法:
1 | wget https://olivier.sessink.nl/jailkit/jailkit-2.20.tar.bz2 |
它的各个工具的用法可以看man手册。
逃逸
越狱依据一个很滑稽的原理:当进程中存在文件在当前root目录树外,即在jail外,即表明越狱成功,此时的root就是原来文件系统的root了。
root用户
它一定能逃逸!相对于seccomp
无论什么用户都能限制syscall
,chroot
这个系统调用唯一做的事就是更改了进程的rootDir
,所以root用户依然能做任何事,现在需要做的事就是构建一种情形:有一个文件在jail外,于是根据Chw00t: Breaking unices’chroot solutions有如下方法:
- 在jail rootDir下调用
chroot("test")
能将jail缩小到子目录test下,内核不会改变CWD
,也就是说CWD
还在原来的根下,它不属于test及其子目录,此时就已经越狱成功了,CWD
为逃犯,让其不停的..
向上移动,再调用chroot(.)
即可将rootDir
恢复为文件系统真实的根。 - 打开jail的rootDir得到文件描述符,cd到jail下的子目录再调用
chroot(".")
,使用fchdir
改变CWD
,此时CWD
已经越狱,让其不停的..
向上移动,再调用chroot(.)
即可将rootDir
恢复为文件系统真实的根。 - 和2类似,这种方式不手动打开文件而是搜索自己的文件描述符表查看是否已经有被打开并且在在jail外的文件描述符,接着过程同上了。
- 创建子进程,子进程打开jail rootDir并且绑定socket等待连接欸,父进程在jail子目录使用
chroot
再次缩小jail大小,并使用socket连接子进程,接收子进程的fd,此fd在jail外,越狱成功。 - 利用
proc
,将其挂载到jail子目录下,就可以访问到其他进程(包括未在jail里的进程)数据,这些数据含有文件目录信息,也就是存在不在jail里的文件描述符,这可以通过遍历的方式找到,然后切换CWD
到那个地方,越狱成功。 - 再次挂载所有块设备,将可能挂载到
root
- chroot只改变rootDir,使用
ps
命令还是可以看到其他进程的,此时使用ptrace
附着其他未被困的进程,向其中注入shellcode,再连接shellcode创建的shell就可以得到rootDir不受限的shell。(非root用户只能attach到子进程,但是子进程会继承父进程的CWD和rootDir故非root无法用此越狱) - 创建子进程,子进程在jail子目录下使用chroot,并且子进程在这个新的jail下再次创建子目录并cd进入它,父进程把这个新目录移出子进程的jail外,逃逸成功。
以上操作可以直接使用大佬编写的程序完成,编译如下:
1 | wget https://raw.githubusercontent.com/earthquake/chw00t/master/chw00t.c |
非root用户
想办法获取root权限:
- 本地提权漏洞
- 配置错误:如SUID/SGID,/etc/passwd可写等
问题
嗯,现在还没搞懂其中的原理,应该要看内核代码看文件名->inode
这个映射的实现细节才能找到原因吧,这就要留在以后了。
rbash
即restricted shell
,当以-r
参数或rbash
启动shell时将会以受限的方式启动shell,在这个受限的shell里:
- 在执行命令时命令不能带
/
- 不能改变当前工作目录
- 不能更改
PATH
或SHELL
变量 - 不能使用重定向输出
- etc..
1 | root@VM-49-106-ubuntu:/home/ubuntu/test# ./lal # 1 |
可以看到它本身并不能实现沙箱,因为它只限制可执行的命令名格式,完全不限制文件操作,这并不是一个安全特性,是很容易绕过的,例如配置错误时:
- 可使用vim,vi等内部可执行命令的程序时
1
2
3
4
5
6
7root@VM-49-106-ubuntu:/home/ubuntu/test# cd #受限
rbash: cd: restricted
root@VM-49-106-ubuntu:/home/ubuntu/test# vi #打开vi
:set shell=/usr/bin/zsh #定义
shell #执行
➜ test cd #新生成的shell不受限
➜ ~ - 可使用mv,cp类文件操作命令:所以,若要单独使用此特性,须采用白名单方式并严格限制可使用的程序!
1
2
3
4
5
6root@VM-49-106-ubuntu:/home/ubuntu/test# echo $PATH #此时的环境变量
/home/ubuntu/test
root@VM-49-106-ubuntu:/home/ubuntu/test# cp /bin/bash /home/ubuntu/test #将外部的程序cp到PATH目录
root@VM-49-106-ubuntu:/home/ubuntu/test# bash #执行
root@VM-49-106-ubuntu:/home/ubuntu/test# cd #新生成的shell不受限
root@VM-49-106-ubuntu:~#UID
回顾一下passwd
这条命令,所有用户都能够使用它更改自己的密码,而密码等信息被存放在shadow
及passwd
文件下:理论上既然无权限,passwd命令就无法写这两个文件,但事实却是可以写,究其原因,是1
2
3
4
5
6➜ ~ ls -l /etc/shadow
---------- 1 root shadow 830 Oct 3 2017 /etc/shadow #都无
➜ ~ ls -l /etc/passwd
-rw-r--r-- 1 root root 1223 May 13 2018 /etc/passwd #只有root用户可写
➜ ~ ls -la /usr/bin/passwd
-rwsr-xr-x 1 root root 47032 May 17 2017 /usr/bin/passwd #任何用户可执行改密操作,注意属主的执行位不是x而是sSUID
的存在(可见Linux系统管理#SUID权限),那么它的具体实现呢?其实Linux的原始的访问控制就只有ugo
及其的rwx
,所以SUID
的实现还是从user
与group
下手的(明显的,SUID是从user下手,对应的还有SGID,全都一样,下面只以前者说明)。原来,为了解决类似passwd
这类命令权限问题,Linux下为进程设置了有三个UID! - Real UID(RUID):进程创建者的UID,正常情况下它一直不会变化,永远表示进程创建者,但root用户是可以更改它的。
- Saved UID(SUID):即上面提到的,属主可以为自己的可执行程序设置SUID位,设置后任何人执行程序,程序启动时都将获得程序属主的权限。这实际是SUID和EUID都变为程序属主的UID。
- Effective UID(EUID):正如其名为权限检查时实际生效的UID,意味着在判断用户权限时并不检查RUID及SUID,只看EUID。
例如:
1 |
|
结果如下:
1 | ➜ test sudo -u ubuntu ./tuid # ubuntu用户500 |
现在来做权限切换,从设计的角度来看:
- RUID==root:三个UID可以任意赋值
- RUID!=root:①RUID无法改变 ②EUID可以变为SUID或RUID ③SUID可以变为RUID或EUID
例如:
1 |
|
结果:
1 | ➜ test sudo -u ubuntu ./tuid |
1 | ➜ test chmod u+s ./tuid #原属主为root,设置SUID |
一个典型的例子就是当程序执行完高权限后使用setresuid
进行降权操作并未完全抹除高权限,如下:
1 |
|
结果如下:
1 | ➜ test ./tuid |
参考
https://github.com/earthquake/chw00t/blob/master/Presentations/Balazs_Bucsay_Hacktivity2015_chw00t.pdf
https://www.ibm.com/developerworks/cn/linux/l-cn-chroot/
https://blog.csdn.net/seekkevin/article/details/50041577
https://en.wikipedia.org/wiki/Restricted_shell#Weaknesses_of_a_restricted_shell
http://www.kernel.org/doc/man-pages/man {setresuid,getresuid}
https://people.eecs.berkeley.edu/~daw/papers/setuid-usenix02.pdf