Linux内核监控
本文将简单罗列Linux监控的实现方式,安全及审计软件的前提就是能在关键点上接管系统的执行流程,而关键点的接管必须依赖Linux内核的监控能力:
1 LSM HOOK
Linux内核中为发实现其安全机制,已经预留了LSM HOOK埋点,以4.4.131的内核为例,系统共提供了多达198个监控点回调,4.4.131是当前麒麟操作操作系统的内核版本,而较新的5.4.2内核已经支持多达216监控回调:
类别 | 钩子函数个数 | 功能 |
---|---|---|
ptrace | 2 | Ptrace操作的权限检查 |
能力机制 | 3 | Capabilities相关操作的权限检查 |
磁盘配额 | 2 | 配盘配额管理操作的权限检查 |
超级块 | 13 | mount/umount/文件系统/超级块相关操作的权限检查 |
dentry | 2 | dentry相关操作的权限检查 |
文件路径 | 11 | 文件名/路径相关操作的权限检查 |
inode | 27 | inode相关操作的权限检查 |
file | 13 | file相关操作的权限检查 |
程序加载 | 5 | 运行程序时的权限检查 |
进程 | 27 | 进程/进程相关操作的权限检查 |
IPC | 21 | IPC消息/共享内存/信号量等操作的权限检查 |
proc文件系统 | 2 | proc文件系统相关操作的权限检查 |
系统设置 | 2 | 日志,时间等相关操作的权限检查 |
内存管理 | 1 | 内存管理相关操作的权限检查 |
安全属性 | 7 | 对安全相关数据结构的操作的权限检查 |
网络 | 37 | 网络相关操作的权限检查 |
XFRM | 11 | XFRM架构想要关操作的权限检查 |
密钥 | 4 | 密钥管理相关操作的权限检查 |
审计 | 4 | 内核审计相关操作的权限检查 |
Android Binder | 4 | Android Binder架构有关操作的权限检查 |
总计 | 198 |
其中我们最关心的是进程、程序加载、内存及文件相关的监控回调,从数量及功能上已足够强大,但具体能否满足我们的需求,还要在实现过程中进行分析和验证。比如以进程创建为例,Linux内核的进程创建是通过sys_clone(sys_fork/sys_vfork)及sys_execve实现的,但LSM的监控点为了避免多入口的问题,放在了task_alloc这个点上:调用栈如下图所示:
&"backtrace\n"
#0 security_task_alloc (task=0xffff923cad2f8000, clone_flags=4001536) at security/security.c:1472
#1 0xffffffffa0a88ff4 in copy_process (clone_flags=4001536, stack_start=<optimized out>, stack_size=<optimized out>, child_tidptr=<optimized out>, pid=0x0, trace=<optimized out>, tls=<optimized out>, node=-1) at kernel/fork.c:1746
#2 0xffffffffa0a8a59f in copy_process (node=<optimized out>, tls=<optimized out>, trace=<optimized out>, pid=<optimized out>, child_tidptr=<optimized out>, stack_size=<optimized out>, stack_start=<optimized out>, clone_flags=<optimized out>) at kernel/fork.c:1574
#3 _do_fork (clone_flags=4001536, stack_start=<optimized out>, stack_size=<optimized out>, parent_tidptr=<optimized out>, child_tidptr=<optimized out>, tls=<optimized out>) at kernel/fork.c:2056
#4 0xffffffffa0a8a969 in SYSC_clone (tls=<optimized out>, child_tidptr=<optimized out>, parent_tidptr=<optimized out>, newsp=<optimized out>, clone_flags=<optimized out>) at kernel/fork.c:2166
#5 SyS_clone (clone_flags=<optimized out>, newsp=<optimized out>, parent_tidptr=<optimized out>, child_tidptr=<optimized out>, tls=<optimized out>) at kernel/fork.c:2160
#6 0xffffffffa0a03ae3 in do_syscall_64 (regs=0xffff923cad2f8000) at arch/x86/entry/common.c:287
#7 0xffffffffa1400081 in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:237
#8 0x00007ffc00600600 in ?? ()
#9 0x000056066c0ceec0 in ?? ()
#10 0x0000000000000000 in ?? ()
在进程创建这个点上我们需要取到哪些信息:主进程、子进程、调用栈及相应的符号信息等等,但security_task_alloc这个点不能满足我们的需求,但再利用程序加载(BPRM)的相关回调我们即可将所有的信息串联起来,有了这个信息我们就能够做进详细的检测与判定,以上就是我们解决问题的主要思路。
本项目主要以天狗R及天狗S为基础,但二者所依赖的监控点数量已经很多,我们会根据重要程度及回调类型在两个批次中分别实现,进程、文件及权限相关的核心操作列在第一批次,第一批为高优先级实现,具体涉及哪些监控点本文后面有详细介绍。
2 Syscall_Table HOOK
这种方式就类似于Windows SSDT HOOK,但Linux内核并没有类似于Windows内核的PatchGuard (KPP)的防护机制,可以说内核本身是不设防的。但要注意的一点就是:为了缓解Meltdown漏洞Linux内核引入的KPTI以及ARM64系统上引入的Syscall Table Wrapper (4.19之后) 都给Syscall Table HOOK带来挑战,应对此场景还需要特殊处理。
此外也确实有例外场景存在,如国产化PKS或龙芯理台实现了可信计算,可信计算会对内核的内存镜像及syscall table有进行安全度量以及内存防改保护。
3 代码Inline HOOK
代码Inline hook做为针对Syscall Table hook的延伸,但要针对不同的CPU架构及指令集做不同处理。Inline hook的方式同样面临可信度量及内存保护的挑战。
4 ftrace & kprobe机制
Linux内核为了提升在线解决问题的效率,实现了动态插桩机制,可以在内核”任意函数“点进行插桩,用以输出参数及函数变量,甚至改变函数执行流程。当然这种技术主要是用于排错应急,如果用于生产环境还需要更多的技术验证。这种技术的明显限制是针对同一个函数只能进行一个插桩,并且插桩行为是能够被可信计算发现的,亦可被阻止。
5 中断向量HOOK
X86体系下应用层通过syscall或sysenter切换至内核模式,ARM体系下通过SVC或SWI可以切换至内核,切换模式的指令最终是通过自陷阱(Trap)触使CPU进行模式切换,并自动执行事先指定位置处的代码。我们可以通过接管中断向量的方式实现Syscall的HOOK,但是此方式会有以下难点:
- 入口片要处理相当多的初始化工作,只能用汇编级指令实现
- 针对开启KPTI的内核,要保证入口代码在Shadow内核空间
6 GhostHOOK
这种其实是无HOOK方式,即采用高精度时钏或于硬件PMU以微妙甚至纳秒级间隔来执行检测代码,可通过劫持当前CPU的执行流程的方式实现控制流的转换,即内核HOOK。Intel及AMD在各自的CPU上均实现了类似的机制,ARM架构上的CP15协处理器亦实现了类似的功能,但相对Intel的PMU在功能上要弱一些,但均可以达成类似的HOOK效果。
7 内核热补丁
此功能主要应用服务Rebootless的需求而开发的,目前已有多个方案实现,不同的发行版亦有不同的选择及集成,如Ksplice, Kgraft, Kpatch, Livepatch, KernelCare等方案,有的是通过inline hook的方式实现,有的是通过篡改内核vmlinux镜像的elf导出表实现的。更多内容信息可参见History of Linux Kernel Live Patching
Leave a Reply