Linux Security Module逆向分析实战

Linux Security Module逆向分析实战

本文记录了对某发行版Linux中一个安全模块(LSM)的逆向过程,该LSM对系统中待运行的程序进行安全校验,数据流穿越内核态与用户态,涉及系统内核及系统服务。此LSM对系统安全性的增强效果明显,其设计思路值得防守方研究学习,可于个人终端或服务器安全防护中应用。特此对逆向内容记录,希望能为读者在终端防护方面拓宽思路,同时欢迎感兴趣的师傅们交流学习。

一. LSM框架简介

Linux安全模块(Linux Security Module,LSM)框架是Linux操作系统内核提供的一种安全机制,它通过内核扩展实现hook函数以完成多种安全检查,通常用于强制访问控制(Mandatory Access Control)。虽然被称作“模块”,但不同于LKM,这些扩展并不是可加载的内核模块,而是和内核代码一起编译在内核文件(vmlinuz)中。可以通过如下命令查看本机启用的LSM,cat /sys/kernel/security/lsm。常见的LSM包括SELinux、Yama等。

LSM框架的hook点设置于内核访问关键对象前,通过调用LSM中实现的hook函数,判断是否可以进行访问。如果有多个LSM,则会根据初始化的顺序依次判断,都允许才能进行访问。上述关键对象包括程序、进程、套接字、文件系统等,可在/usr/src/linux-headers-YOURSYSTEMVERSION/include/linux/lsm_hooks.h中查看详细的hook说明。

这里以程序启动过程为例,简单说明LSM工作的机制。当通过execve系统调用执行一个新程序时,内核最终会执行到__do_execve_file函数完成相关工作,在这里会调用prepare_binprm函数填充struct linux_binprm,填充前会调用security_bprm_set_creds进行安全检查。security_bprm_set_creds就是LSM框架提供的hook,它会依次调用注册在这个钩子上的回调函数,完成安全检查。此流程上相关代码以及此钩子的说明如下。
Linux Security Module逆向分析实战_第1张图片
LSM开发时,通过如下函数定义安全模块的hook函数,逆向时通过此函数可快速定位具体的LSM以及相关回调函数。
在这里插入图片描述

二. 安全模块逆向分析

2.1 分析准备
本次分析的对象为某发行版Linux,此系统提供了可执行文件的签名校验功能,仅有签名的程序可以被执行,本次逆向的目标就是试图还原校验功能的框架和逻辑。由于此安全检查的存在,提权、后门等程序因为此机制的存在而无法直接运行,从某种程度提高了操作系统的安全性。编译一个hello world程序,运行,会提示无法通过系统安全校验目前不能运行。strace跟一下,看到在execve时就被干掉了(如下图),可以肯定是在内核中完成这个动作的,大概率是通过内核扩展实现。
在这里插入图片描述
下面尝试定位此内核扩展。lsmod看一眼,没有发现明显相关的。查看此系统的所有LSM,有一个名为elfverify的模块,通过此命名可以推断,就是此模块完成了程序的校验功能。
在这里插入图片描述
由于LSM是被编译在内核中的,因此接下来需要获取到此系统的内核文件。系统的内核文件通常可以在/boot目录中找到,其中vmlinuz的文件是压缩后的内核,可以通过extract-vmlinux工具提取未压缩的内核文件vmlinux进行分析。然而提取后的文件是没有符号的,符号信息存储在同目录下System.map文件中。这里推荐使用vmlinux-to-elf这个工具完成提取与符号修复工作,通过此工具可输出一个带符号的ELF文件,方便逆向分析。

2.2 LSM分析

反编译查看恢复后的内核,在elfverify的初始化函数elfverify_init()中,security_add_hooks()的第二个参数count为5,即此LSM一共注册了5个hook函数;跟踪一下第一个参数security_hook_list,可得到所有实现的回调函数,总结如下。
Linux Security Module逆向分析实战_第2张图片
Linux Security Module逆向分析实战_第3张图片
可以看出此LSM还对套接字、设备挂载进行了安全校验,这里专注于程序运行的校验,锁定hook_bprm_set_creds进行进一步分析。

跟进hook_bprm_set_creds,该函数首先对一些参数进行检查,接着判断是否开启了开发者模式(判断依据是某个文件的内容),如果符合条件则放行(返回0),否则调用access_verify进行进一步判断。此函数的参数类型是struct linux_binprm, 源码中此结构体被标记为__randomize_layout,这是Linux内核中的一项防御机制,有此标记的结构体其中的元素将作乱序排列,从而攻击者难以找到偏移具体对应的元素。因此通过静态分析,也暂时无法确定传递给access_verify的参数。
Linux Security Module逆向分析实战_第4张图片
在access_verify中,会将当前进程通过add_wait_queue挂起等待,并将90-pid-UNKNOWN信息写入某个设备中,这里的90应该是LSM开发者自定义的一个“魔数”(socket校验中是91),而第三段的内容由于结构体的随机化也暂时无法确定(之后分析会知道其实是程序路径)。之后再从此设备中读取信息,根据内容选择是否继续执行,并从队列中移除进程。

LSM中关于elf校验的流程到这个函数基本就结束了,并不包含具体的校验逻辑,只是将待校验的进程信息写入某个设备并等待结果,可见校验应该是在另一处完成。经过跟踪分析,此LSM注册了一个杂项设备(在register_elf_verifier_dev()中),名为elf_verifier。下一步找到了谁从这个设备中读取信息,大概率就能定位到完成校验的具体代码。

然而,在内核中并没有找到相关的代码,下一步得去用户态的程序找找。

2.3 安全校验逻辑分析
查看一下系统进程,发现一条程序名为*-elf-verify,其ppid为1,看了下是系统服务,推断这就是处理杂项设备中数据的程序了。为验证推断,将此服务停止,运行一个自己编译的程序,待运行的进程“僵死”,推断应该是LSM将进程送入等待队列后,没有从杂项设备中读到校验结果,就造成了一直挂起的局面。这也确认了正是这个程序处理设备数据并给与返回结果,校验逻辑应当就在其中。

对此系统服务程序进行分析与调试,在进行ELF文件安全校验时,它会循环的从/dev/elf_verifier这个设备中读取内容,读取到的内容包括PID和完整的程序路径,并依据此信息进行校验,其主要检查如下两点:

判断此路径的程序是否在白名单或黑名单中

在ELF文件头的特殊节中提取签名(PKCS7),然后进行验证(证书在系统某路径中)
上述的黑白名单位于系统/usr目录下,仅root用户可编辑。而ELF文件头中的特殊节在其他普通的ELF文件中不会出现,应当是在对ELF文件进行签名时加上的,而将签名信息添加到文件头中又不会对程序的正常运行造成影响。如果能够顺利读取到签名信息,则调用openssl的相关函数进行校验。校验完成后,将检验结果写入/dev/elf_verifier,通知内核进行后续动作;同时如果校验不通过,会通过dbus通知GUI弹出提示告知用户。

至此,一次校验完成,整个签名校验功能的脉络基本摸清了。在其中还有一些细节检查,例如文件格式、ELF文件头等等,就不一一展开介绍了。

三. 总结

该系统的可执行程序签名校验功能,通过安全模块(LSM)elfverify、系统服务联合完成,二者通过杂项设备/dev/elf_verifier传递数据,完成了用户态和内核态的交互。在内核中,通过实现LSM的security_bprm_set_creds钩子在程序运行前获取到待运行程序的完整路径,将进程暂时挂起,同时将信息写入设备中;用户态程序从设备中读取到信息后,判断此路径程序是否在黑白名单、程序是否是经过签名的ELF程序,并将判断结果写入设备;内核LSM根据返回的结果确定是否允许执行。

整个过程涉及LSM、设备、ELF文件格式、签名校验等知识,有深有浅,本文记录的比较简单,欢迎感兴趣的师傅深入交流。同时,该方式利用LSM框架提供的钩子,对某些操作进行安全校验,同时将复杂的校验逻辑在用户态完成,通过设备完成数据传递,此类模式在终端安全防护上亦可借鉴。

最后

关注私我,获取【网络安全学习攻略

Linux Security Module逆向分析实战_第5张图片

你可能感兴趣的:(网络,安全,程序员,linux,网络安全,计算机网络)