CVE-2019-2054
前言
漏洞信息
- 实验环境:pixel2(内核版本4.4,安全补丁2019-04-05)
- 漏洞类型:TOCTOU(timer-of-check-time-of-use)
- 漏洞描述:在内核版本4.8之前,因为ptrace可以修改子进程进行系统调用时的syscall的调用号,而seccomp对系统调用的检查位于ptrace修改代码之前,这就可以通过ptrace来绕过seccomp对一些系统调用的检查,这可以配合一些漏洞进行提权操作。
- 调用链:syscall_trace_enter->secure_computing->__secure_computing->seccomp_phase1
漏洞原理
漏洞原理
这里简单记录下最近看的一个漏洞(CVE-2019-2054),漏洞发生在版本小于4.8的内核中的一个TOCTOU(timer-of-check-time-of-use)漏洞,被ptrace之后的进程可以绕过seccomp对系统调用的检查(当seccomp把安全系统调用检查之后被ptrace把这个系统调用替换为被过滤的调用,这使得seccomp前面的检查就没有意义了)。所以这些旧版本的内核中开启seccomp的进程就不应该具有使用ptrace的能力。避免恶意进程使用ptrace对seccomp过滤进行逃逸。
在android系统中zygote程序将seccomp沙箱应用在system_server和所有的app进程中,并且这个seccomp沙箱允许使用ptrace函数这刚好满足这个漏洞所需的要求。
漏洞造成的影响
通过这个漏洞可以实现对seccomp沙箱的绕过,从而调用一些系统限制我们调用的系统调用。增加了攻击面,结合别的漏洞可用做权限提升。
漏洞利用
seccomp机制
既然该漏洞功能是绕过seccomp沙箱那么我们首先来看下什么是seccomp沙箱
seccomp是secure computing的缩写,是linux kernel从2.6.23版本中引入的一种简洁的sandboxing机制。由于linux中大量的系统调用直接暴露给用户程序。但并不是所有系统调用都会用到,所以一些不安全的代码滥用系统调用会对系统造成安全威胁。seccomp机制能使进程进入一种”安全”运行模式。
首先如果我们要使用该机制,需要在内核编译时开启以下几个选项,这样在启动后的系统中就能使用seccomp机制了。
seccomp总共分为3种模式分别用0,1,2表示:
- 0.尚未启动seccomp
- 1.启动seccomp沙箱”STRICT”模式
- 2.启动seccomp沙箱”FILTER”模式
查看当前进程所使用的seccomp模式有2种方法。
1.通过/proc/<pid>/status
文件中的Seccomp字段来确定当前进程属于哪种模式。
2.通过prctl函数使用PR_GET_SECCOMP选项获取,返回值则是。
再来看看”STRICT”与”FILTER”两种模式有什么不同。
首先是”STRICT”模式,该模式下只能调用4种系统调用即read,write,exit,sigreturn,如果调用其他不允许的系统调用则进程会收到SIGKILL信号而被终止运行。
用户程序中开启”STRICT”模式的方法:
可以看到STRICT模式把权限设置还是比较死的,只能使用4个默认的系统调用,灵活性不够啊。那么有什么方法可以使我们自己设置过滤的系统调用吗?当然可以那就是”FILTER”模式,该模式我们可以自己编写过滤策略,相对于前面的模式来说”FILTER”模式就灵活多了。但也有一个限制那就是开启该模式需要具备CAP_SYS_ADMIN属性或当前进程设置了no_new_privs选项,可通过prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
来开启no_new_privs选项。如果不具备前面两个条件的任意一个那么使用SECCOMP_SET_MODE_FILTER选项时函数就会返回错误。这样设定的原因主要为了避免无特权的进程新增恶意bpf策略,no_new_privs选项主要是用于规避execve启动的程序权限大于父进程权限而造成一些列安全问题
开启”FILTER”模式的方法和前面的差不多只需要换下参数:(注意上面提到的该模式的前提条件)
最后的args参数指向一个sock_fprog结构体,而sock_fprog结构体的filter成员指向的就是我们的bpf过滤代码。具体的结构体如下:
当触发一个过滤检查时(也就是调用限制系统调用时),seccomp过滤器函数会根据过滤代码返回一个由两部分组成的32位值。前16-bit为SECCOMP_RET_ACTION,后16-bit为SECCOMP_RET_DATA。
SECCOMP_RET_ACTION定义了以下几种行为:
SECCOMP_RET_KILL – 不执行system call,立即中止process (SIGSYS)。
SECCOMP_RET_TRAP – 不执行system call,进程发出(SIGSYS)system call, 并system。 call相关信息存放到siginfo_t
SECCOMP_RET_ERRNO – 不执行system call,SECCOMP_RET_DATA返回errno。
SECCOMP_RET_TRACE – 启动ptrace base的tracer(如gdb), 让tracer可以接手处理。若没有tracer则返回-ENOSYS
SECCOMP_RET_ALLOW – system call正常运行
如果同时符合多个条件,则SECCOMP_RET_ACTION只会返回优先级较高的值。
SECCOMP_RET_DATA则表示我们的返回值。
使用seccomp沙箱
列出一个linux中使用该模式的例子:https://elixir.bootlin.com/linux/latest/source/samples/seccomp/dropper.c
关闭seccomp沙箱
通过adb shell setenforce 0 && adb stop && adb start
指令可关闭对zygote进程的seccomp的安装,因为无法从正在运行的进程中移除seccomp策略,所以需要重启shell以使该选项生效。
seccomp检测工具
AOSP项目中/cts/tests/tests/security/jni/android_security_cts_SeccompTest.cpp
可用来检测当前设备阻止了哪些系统调用,原理就是不断试错。
BPF策略
BPF(BSD Packet Filter)一种过滤机制,更多时候用来过滤Unix内核网络数据包,我们这里是seccomp用它来做系统调用的过滤操作,BPF采用一种叫过滤器伪机的方式(filter Pseudo-machine)对BPF过滤代码做解释执行,这种伪机器是一个轻量级,高效的状态机。BPF伪指令形式为”opcode jt jf k”也就是前面的sock_filter结构体,分别表示操作码,寻址方式,判断正确的跳转和失败的跳转,以及操作所使用的的通用数据域。|opcode|jt|jf|k|
下面是一组BPF代码,这段代码比较好理解,用来定义内核对系统调用nr的过滤,如果目标进程如果使用了nr系统调用,则会执行SECCOMP_RET_KILL选项也就是进程被直接杀掉,使用别的调用则会使用SECCOMP_RET_ALLOW选项直接放行。
涉及到的几个指令函数:
BPF_LD+BPF_W+BPF_ABS A <- P[k:4] /将一个Word即4byte的值赋给寄存器(accumulator)/
BPF_JMP+BPF_JEQ+BPF_K pc += (A == k) ? jt : jf /若A等于K则跳转jt行执行否则跳转jf行执行/
更多指令含义
学过汇编的朋友第一眼是不是感觉和汇编代码很相似,反正我感觉都差不多。
ptrace使用
ptrace想必大家都有所了解这里就不多介绍了,主要记录下用到的选项:
PTRACE_SETREGSET:用来修改tracee的寄存器,这里指定为NT_ARM_SYSTEM_CALL就可以把子进程的系统调用给改掉了,需要改的值放在iov结构体中。
ptrace(PTRACE_SETREGSET, child, NT_ARM_SYSTEM_CALL, &iov);
利用代码
|
|
总结
感觉这段时间尽看些逻辑漏洞,逻辑漏洞利用起来不用过各种保护机制真是好,而且还特稳定,不像内存破坏漏洞还需要绕过各种保护机制才能提权成功,而某些逻辑漏洞直接就能提权。
唉,早点休息早点休息,狗命要紧。
参考
|
|
Author: Let_go
Link: http://github.com/2019/04/10/CVE-2019-2054/
Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.