linux可以动态的加载内核模块,在很多场合可能需要确保加载内核的安全性。如果被攻击者加载恶意内核模块,将会使得内核变得极其危险。

  当然,稳妥的做法就是给内核模块进行签名,内核只加载能正确验证的签名。这是最首先想到的方法,当然,这个方法并不是很简单,你需要选用一套公钥加密方法,一般就是rsa算法了。难点是要在内核中进行验证模块的签名,这需要修改内核中的一些对应的地方。明显在load_moduler处需要添加签名验证逻辑。为此还需要在内核里实现rsa算法,rsa算法实现的难点就是大数的支持,可能需要一直类似于mpi这样的库。另外还需要在内核中解析公钥文件,以及与应用层的公钥文件交互问题。这些实现起来,工作量确实不小,但令人欣慰的是,linux在3.7版本的内核里都已经实现了这些。其实这些工作以前是以补丁的方式存在的,参见http://lwn.net/Articles/470435/,称为linux-modsign,终于在3.7的内核里修成正果了.呵呵

  

  其实,看到3.7内核里实现了这些,并不能引起某些人的欣慰.因为自己使用的或者公司使用的内核版本没有这么高,估计都是在2.6的样子.你可能想着将3.7的机制移植到2.6上去.我想这工作量也足够大的让人心出胆怯.内核移植,搞不好造成不稳定,容易引起很大的麻烦.从http://lwn.net/Articles/470435/ 上的给出的git地址 http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-modsign.git/的历史中,可以看出工作量还是挺大的 . 况且内核为了提供通用性和扩展性,使用的都是一些小框架,还实现了体系结构相关以及对加密硬件模块的支持,这些都会让源代码难以阅读和理解。如果是我的话,我更宁愿冒着升级内核的风险,而不是去尝试移植代码。当然熟悉内核的人,完全可以跟踪linux-modsign去给自己的系统打补丁,或者根据原理实现一个简化的版本[参见论文 http://www.ee.ryerson.ca/~courses/coe518/LinuxJournal/elj2004-117-signedmodules.pdf].(毕竟内核主线实现的版本还是太复杂了,个人短时间内无法搞定)

  

  鉴于内核改动的风险比较高,可能很多人更倾向于把问题尝试在用户层来解决.这个思想是好的,因为避开了风险高的途径.关键问题是,能否在用户空间来解决这个问题呢.结果是否定的.完全的依赖用户层来解决内核模块的安全问题是不现实的,或者只能解决一部分的问题而已.要想达到上述的在内核中给内核模块进行签名验证的同级别安全问题,是不可能的.在root权限下,用户空间的安全壁垒会被完全攻破.这估计也是linux内核主线使用签名验证机制的一个主要原因.确实这是一个真理:没有内核的支持,操作系统无法在用户层保障安全的问题。

  但用户层空间的路走死了嘛?其实不然,我们还可以另辟蹊径。

  在内核模块的支持下,可以在用户层进行内核模块的签名验证工作。

  首先需要实现一个lsm模块(在高本版内核里,例如2.6.32,内核不再导出register_security类的符号,可能动态 的加载lsm模块需要重新编译内核,让内核导出相关的符号,关于lsm的使用,大家可以查阅相关资料,这里不再介绍),该模块的作用主要是保护insmod不被篡改,保护模块自身不被篡改以及相关的启动脚本不被篡改。lsm做到这些并不复杂,可以将他们放在一个特定目录下,让lsm阻止任何对该目录的写操作。

  内核模块使用工具insmod或者modprobe(属于modutils提供系统程序)来加载内核模块,modprobe最后还是会调用insmod来加载。insmod最终会调用init_module系统调用来加载模块。

  如果修改insmod,在调用init_module系统调用之前进行签名验证逻辑的处理,那么这个前提是insmod的安全性要得到保证,insmod不能被攻击者篡改。

  在lsm模块被加载前,可能运行的是被污染的insmod程序,当然它可以加载恶意的内核模块。因此,应该在系统启动后,首先加载lsm模块,然后加载其他的模块。这只是理论上的,实际上,这些过程都很快,攻击者是无法在这些模块加载之间实施攻击的(因为网络服务什么的都还没开启)。

  因此,问题被转移到保障lsm安全模块的顺利加载。当然该模块可能是使用insmod加载的。(如果是被编译到内核,随内核启动而启动的,这个问题就得到保障了)。所以,insmod是不能被替换的,以及lsm模块本身也不能被替换。

  有三个关键点:

1. insmod不会被替换;
2. lsm模块必须被正确加载。
3. lsm模块不能被卸载。(卸载意味着安全机制不再起作用了)

问题1和2解决后,能达到在root权限没有被攻击者获取额前提下,实施对内核模块的保护。Root权限会卸载你的lsm模块,然后修改insmod,然后加载恶意模块....不过攻击者不能持续保留这种攻击行为,下次启动后,以前加载的恶意模块便不会被加载。

从上面的论述看来,问题1和2的解决是可以的:

假设一个前提:系统第一次启动是绝对安全的。

在这个前提下,在lsm模块里阻止任何对insmod和lsm模块本身的修改操作(利用lsm的hook点实现这些很轻松,实际情况里还需要保护一些加载lsm模块之类的敏感的脚本等),即使root权限也不行。这样就解决了这两个问题。第一次启动是安全的,也就意味着lsm模块被正确加载,这样保护机制就建立起来了。因为假设的前提是可以保证的,因此原理上是可行的。这个问题看上去像是insmod保护内核模块,而lsm模块又反过来保护insmod和lsm模块字节,相互帮助实现安全。

对于问题3,确实很棘手。如果不解决,那么系统的安全级别还是不够高。其实最容易想到的方法就是把lsm模块直接编译进内核,这样就一起解决了问题2和问题3.不过,又要改内核了,不过这里的改动相对在内核里改load_module等相关联代码而言,要简单的多。

在2.6的内核里,添加了CONFIG_MODULE_UNLOAD标记,用于禁止卸载内核模块的,参见网址http://www.ibm.com/developerworks/cn/linux/l-cn-kernelmodules/

其实还可以通过编程的技巧来决绝这个问题,不注册模块的module_exit函数,将会导致模块无法被卸载。
看一下一个hello内核模块代码:

 #include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h> static int __init lkp_init(void)
{
printk("<1>hello world! form the kernel sapce ...\n");
return ;
} static void __exit lkp_cleanup(void)
{
printk("<a>goodbye ,world,leaving kernel sapce ...\n");
} module_init(lkp_init);
//module_exit(lkp_cleanup);
MODULE_LICENSE("GPL");

这里我故意注释掉了module_exit(lkp_cheanup),模块被编译后正常加载到内核中。
使用lsmod可以看到信息:

[root@localhost hello]# lsmod
Module Size Used by
hello [permanent]
8021q
garp 8021q
stp garp
llc garp,stp

可以看到,hello模块被标记为permanent,意味着模块被永久加载,也就是不能卸载,下面使用rmmod测试一下:

root@localhost hello]# rmmod hello.ko
ERROR: Removing 'hello': Device or resource busy

居然是不能卸载该模块,呵呵。可见这方法也能达到效果,而且还很简单。

上述代码测试环境:

Cenos 6.4
[root@localhost hello]# uname -a
Linux localhost.localdomain 2.6.-.el6.i686 # SMP Thu Feb :: UTC i686 i686 i386 GNU/Linux

由上可见,在内核模块的基础上,配合应用程序也可以实现内核模块的安全加载。

本文简要介绍了linux内核模块安全的问题,随着内核3.7对于内核模块签名验证的支持,这个问题后面越来越变得简单啦。不过不过对于很多老的版本的内核也可以使用一些其他的方法来达到同样的功能。如果读者有什么更好的方法或者建议,渴望被指导!

linux内核模块的安全的更多相关文章

  1. 5.linux内核模块基础,内核模块学习

    linux内核模块基础 一.定义 Linux 内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢: 方法 1:把所有的组件都编译进内核文件,即:zImage 或 bzImage,但这样会 ...

  2. Linux内核模块简介

    一. 摘要 这篇文章主要介绍了Linux内核模块的相关概念,以及简单的模块开发过程.主要从模块开发中的常用指令.内核模块程序的结构.模块使用计数以及模块的编译等角度对内核模块进行介绍.在Linux系统 ...

  3. Smart210学习记录-------linux内核模块

    Linux 驱动工程师需要牢固地掌握 Linux 内核的编译方法以为嵌入式系统构建可运行的Linux 操作系统映像.在编译 LDD6410 的内核时,需要配置内核,可以使用下面命令中的 一个: #ma ...

  4. linux内核模块相关命令:lsmod,depmod,modprobe,modinfo,insmod,rmmod 使用说明

    加载内核驱动的通常流程: 1.先将.ko文件拷贝到/lib/module/`uname -r`(内核版本号)/kernel/driver/...目录下, 根据具体用途的区别分为net.ide.scsi ...

  5. linux内核模块编程实例

    linux内核模块编程实例 学号:201400814125 班级:计科141 姓名:刘建伟 1.确定本机虚拟机中的Ubuntu下Linux的版本 通过使用命令uname -a/uname -r/una ...

  6. Linux内核模块编程——Hello World模块

    Linux内核模块编程 编程环境 Ubuntu 16.04 LTS 什么是模块 内核模块的全称是动态可加载内核模块(Loadable Kernel Modul,KLM),可以动态载入内核,让它成为内核 ...

  7. Linux内核模块编写详解

    内核编程常常看起来像是黑魔法,而在亚瑟 C 克拉克的眼中,它八成就是了.Linux内核和它的用户空间是大不相同的:抛开漫不经心,你必须小心翼翼,因为你编程中的一个bug就会影响到整个系统,本文给大家介 ...

  8. 3、Linux内核模块学习

    一.内核模块的学习   内核的整体框架是非常的大,包含的组件也是非常多,如何将需要的组件包含在内核中呢?选择一,就是将所有的组件全部编译进内核,虽然需要的组件都可以使用,但是内核过分庞大,势必带来效率 ...

  9. Linux内核模块编程与内核模块LICENSE -《具体解释(第3版)》预读

    Linux内核模块简单介绍 Linux内核的总体结构已经很庞大,而其包括的组件或许多.我们如何把须要的部分都包括在内核中呢?一种方法是把全部须要的功能都编译到Linux内核.这会导致两个问题.一是生成 ...

随机推荐

  1. SPSS--回归-多元线性回归模型案例解析

    多元线性回归,主要是研究一个因变量与多个自变量之间的相关关系,跟一元回归原理差不多,区别在于影响因素(自变量)更多些而已,例如:一元线性回归方程 为: 毫无疑问,多元线性回归方程应该为: 上图中的 x ...

  2. 201709019工作日记--sleep、wait、notify的使用详解

    1. sleep()和wait()的区分 (1)这两个方法来自不同的类分别是,sleep来自Thread类,wait来自Object类. sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在 ...

  3. (并查集)Travel -- hdu -- 5441(2015 ACM/ICPC Asia Regional Changchun Online )

    http://acm.hdu.edu.cn/showproblem.php?pid=5441 Travel Time Limit: 1500/1000 MS (Java/Others)    Memo ...

  4. hdu 2049 不容易系列之考新郎 && 对错排的详解

    题目 错排:  当n个编号元素放在n个编号位置,错排的方法数记着D(n) ⒈把第n个元素放在一个位置,比如位置k,一共有(n-1)种方法: ⒉放编号为k的元素,这时有两种情况: 1°把它放到位置n,那 ...

  5. Hibernate 注解和配置文件两种方法的对比(有实例)

    hibernate多对多形式(User类<---->Educate类) 1.基于注解的形式: User类: package com.ssh.entities; import java.ut ...

  6. 论文笔记(2)-Dropout-Regularization of Neural Networks using DropConnect

    这篇paper使用DropConnect来规则化神经网络.dropconnect和dropout的区别如下图所示.dropout是随机吧隐含层的输出清空,而dropconnect是input unit ...

  7. FP-Growth in Spark MLLib

    并行FP-Growth算法思路 上图的单线程形成的FP-Tree. 分布式算法事实上是对FP-Tree进行分割,分而治之 首先,假设我们只关心...|c这个conditional transactio ...

  8. Android实现自带横线的EditText

    (一)问题 怎样实现带有横栏的EditText(像记事本的编辑界面那样)? (二)初步思路 1.通过修改EditText背景来实现(系统背景是一个框形图片,内部透明,替换为一个带有横栏的图片即可) 2 ...

  9. .Net Core 自定义配置源从配置中心读取配置

    配置,几乎所有的应用程序都离不开它..Net Framework时代我们使用App.config.Web.config,到了.Net Core的时代我们使用appsettings.json,这些我们再 ...

  10. ASP.NET Core 2 学习笔记(五)静态文件

    之前的ASP.NET网站,只要把*.html.*.css.*.jpg.*.png.*.js等静态文件放在项目根目录,默认都可以直接被浏览:但ASP.NET Core 小改了浏览静态文件的方式,默认根目 ...