荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: cycker (快过年吧.我想回家), 信区: Linux
标  题: Linux 可卸载内核模块完全指南(十五)(转寄)[转载]
发信站: 荔园晨风BBS站 (Thu Jan  2 18:24:08 2003), 站内信件

【 以下文字转载自 cycker 的信箱 】
【 原文由 xiaofong@bbs.pku.edu.cn 所发表 】
发信人: chenhao (努力学习), 信区: Linux
标  题: Linux 可卸载内核模块完全指南(十五)
发信站: 北大未名站 (2000年11月20日01:24:33 星期一) , 转信

第三部分 解决方案(给系统管理员)

3.4 使用LKMs来防护你的linux内核

对于Phrack的读者来说,这一节听起来有些耳熟.Route介绍了很多使linux系统更为安全的
方法.他使用了很多补丁.我想说明一些方法也可以通过LKMs实现.记住不隐藏这些LKMs有时
候也是有用的.(当然隐藏使你必须做的一些事情).因为route的补丁当某人控制系统时几乎
不起作用;而且一个没有特权的用户是不能移走我们的LKM的.但是他可以看见他.使用LKMs
替代静态的内核补丁的优点:你可以很容易很安全的控制整个系统,可以在正在运行的系统
中很容易的进行安装.没有必要每时每刻在一个敏感的系统上安装新的内核.Phrack的补丁
也增加了一些我没有实现的纪录功能.但是有很多种方法来做这个.最简单的可以用printk
(...)


[注意:我并没有细看route的补丁的每一个方面.也许真正一个好的内核探索者可以通过LK
Ms来做更多的东西.]


3.4.1 为什么我们必须允许任何一个程序都拥有可执行的权限


下面是像route的内核补丁里面的一个检查执行权限功能的LKM.


#define __KERNEL__

#define MODULE



#include <linux/version.h>

#include <linux/mm.h>

#include <linux/unistd.h>

#include <linux/fs.h>

#include <linux/types.h>

#include <asm/errno.h>

#include <asm/string.h>

#include <linux/fcntl.h>

#include <sys/syscall.h>

#include <linux/module.h>

#include <linux/malloc.h>

#include <linux/kernel.h>

#include <linux/kerneld.h>



/*系统调用的位置*/


int __NR_myexecve = 0;


extern void *sys_call_table[];


int (*orig_execve) (const char *, const char *[], const char *[]);


int (*open)(char *, int, int);

int (*close)(int);



char *strncpy_fromfs(char *dest, const char *src, int n)

{

char *tmp = src;

int compt = 0;


do {

dest[compt++] = __get_user(tmp++, 1);

}

while ((dest[compt - 1] != '\0') && (compt != n));

return dest;

}


int my_execve(const char *filename, const char *argv[], const char *envp[])

{

long __res;

__asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long)

(filename)), "c"((long) (argv)), "d"((long) (envp)));

return (int) __res;

}


int hacked_execve(const char *filename, const char *argv[], const char *envp[]
)

{

int fd = 0, ret;

struct file *file;


/*我们需要inode结构*/

/*我使用了open来实现,因为在学习过LKM传染者之后,你必须能够理解这个,下面会有一个
好一些的实现*/

fd = open(filename, O_RDONLY, 0);



file = current->files->fd[fd];



/*是root的文件吗?*/



/*要记住:在这儿你可以做一些其他的检查(route做了很多的检查),但是这里只是为了演示
.你可以看看inode的结构,看看有一些什么东西是要检查的(linux/fs.h)*/


if (file->f_inode->i_uid!=0)

{

printk("<1>Execution denied !\n");

close(fd);

return -1;

}

else

/*否则让用户执行文件*/

{

ret = my_execve(filename, argv, envp);

return ret;

}

}


int init_module(void)        /*初始化模块*/

{

printk("<1>INIT \n");

__NR_myexecve = 250;

while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)

__NR_myexecve--;

orig_execve = sys_call_table[SYS_execve];

if (__NR_myexecve != 0)

{

printk("<1>everything OK\n");

sys_call_table[__NR_myexecve] = orig_execve;

sys_call_table[SYS_execve] = (void *) hacked_execve;

}


open = sys_call_table[__NR_open];

close = sys_call_table[__NR_close];

return 0;

}


void cleanup_module(void)      /*卸载模块*/

{

sys_call_table[SYS_execve]=orig_execve;

}


这和route的内核的补丁并不是完全一样。route检查路径,而我们检查的是文件。(检查路
径也是可以的,但是依我看来文件的检查会更好一些)。我只检查了文件的UID。一个管理员
可以加更多的过滤器。正如我所说的,上面我所用的open/fd实现并不是最简单的方法。我
在这里使用他是因为你应该很熟悉这种方法了。(记住,LKM传染者使用过这种方法)。对
于我们的这个目的,下面的内核函数也是可以用的(比较简单的方法):


int namei(const char *pathname, struct inode **res_inode);


int lnamei(const char *pathname, struct inode **res_inode);


这些函数用一个路径作为参数,返回相对应的inode结构。两个函数的区别在于对于链接的
处理:lnamei不解析链接并且返回那个链接本身的inode。作为一名hacker你也可以改变i
node。只不过需要替换sys_execve(...)并且使用namei(...)(这个方法我们也用于执行控
制),然后操纵inode(我会在5。3中给你们一个实用的例子)。


3.4.2 链接的补丁


当谈到系统安全时,每一个linux用户都知道链接的错误是常常会引起严重问题的。Andre
w Tridgell开发出一个内核的补丁来防止一个进程进行恶意的链接。Solar的设计者也增加
了一些代码来阻止用户进行不正确的链接。


我必须承认链接的补丁对于我们LKM来说是一个不是太容易接触到的层次。既没有输出的符
号,也没有可以拦截的系统调用。解析链接是VFS做的。可以看看我们在第四部分解决这个
问题的方法。(但是我不会使用第四部分的方法来进行系统安全维护)。你也许会奇怪为
什么我不使用sys_readlink(...)系统调用来解决这个问题。因为当你运行'ls -a syslin
k'时,会进行这个系统调用,然而,当运行'cat symlink'就不会了。


我认为你可以把这个问题留给内核补丁。当然你可以编一个LKM拦截sys_symlink(...)来阻
止在/tmp目录下新建链接.看链接的LKM,有相似的模块实现.


OK,链接问题对于LKM来说有点困难.但是Solar设计者的关于限制链接的想法是怎么样的呢
?这是可以被LKM实现的。我们只需要截获sys_link(...)。这个系统调用是负责新建所有硬
链接的。让我们看看替换的系统调用(这个代码段并不和内核补丁完全一样,因为我们只
须检查/tmp目录,不是sticky位。但是这个可以通过看inode的结构实现[见5。1]):


int hacked_link(const char *oldname, const char *newname)

{

char *kernel_newname;

int fd = 0, ret;

struct file *file;


kernel_newname = (char*) kmalloc(256, GFP_KERNEL);

memcpy_fromfs(kernel_newname, newname, 255);


/*是链接到/tmp目录的么?*/

if (strstr(kernel_newname, (char*)&hide ) != NULL)

{

kfree(kernel_newname);


  /*我再次使用了open方法*/

fd = open(oldname, O_RDONLY, 0);



file = current->files->fd[fd];


  /*检查UID*/

if (file->f_inode->i_uid!=current->uid)

{

printk("<1>Hard Link Creation denied !\n");

close(fd);

return -1;

}

}

else

{

kfree(kernel_newname);

/*一切正常-〉用户可以新建链接了*/

return orig_link(oldname, newname);

}

}


这是你可以控制新建链接的方法。


3.4.3 /proc权限的补丁


我已经演示了一些如何隐藏一些进程的信息的方法。route的隐藏的想法和我们的完全不一
样。他想通过改变目录的权限来限制/proc/的存取(存取进程信息要到这个目录)。因此
他检查了proc的inode。如果你加载了他,一个普通用户是不可以读取/proc的fs的。如果
你卸载了,他就可以了。下面我们看看:


/*非常坏的编程风格(也许我们必须使用一个函数来获得inode),但是他确实能用*/


#define __KERNEL__

#define MODULE

#define BEGIN_KMEM {unsigned long old_fs=get_fs();set_fs(get_ds());

#define END_KMEM set_fs(old_fs);}


#include <linux/version.h>

#include <linux/mm.h>

#include <linux/unistd.h>

#include <linux/fs.h>

#include <linux/types.h>

#include <asm/errno.h>

#include <asm/string.h>

#include <linux/fcntl.h>

#include <sys/syscall.h>

#include <linux/module.h>

#include <linux/malloc.h>

#include <linux/kernel.h>

#include <linux/kerneld.h>


extern void *sys_call_table[];


int (*open)(char *, int, int);

int (*close)(int);



int init_module(void)        /*初始化模块*/

{

int fd = 0;

struct file *file;

struct inode *ino;


/*再一次使用open方法*/

open = sys_call_table[SYS_open];

close = sys_call_table[SYS_close];


/*我们必须准备一些内核空间的数据给系统调用*/

BEGIN_KMEM

fd = open("/proc", O_RDONLY, 0);

END_KMEM

printk("%d\n", fd);

file = current->files->fd[fd];



/*proc目录的inode*/

ino= file->f_inode;


/*改变权限*/

ino->i_mode=S_IFDIR | S_IRUSR | S_IXUSR;


close(fd);

return 0;

}


void cleanup_module(void)      /*卸载模块*/

{

int fd = 0;

struct file *file;

struct inode *ino;


BEGIN_KMEM

fd = open("/proc", O_RDONLY, 0);

END_KMEM

printk("%d\n", fd);

file = current->files->fd[fd];


/*这里是proc目录的inode*/

ino= file->f_inode;


/*改变权限*/

ino->i_mode=S_IFDIR | S_IRUGO | S_IXUGO;


close(fd);

}


加载这个模块,并尝试ps,top或者其他的,他们不会运行的.每一次/proc的存取都被拒绝了
.当然,作为root你还是可以察看每一个进程或者其他的任何事情的。这只不过是一个愚昧
你的用户的方法。


[注意:这是一个很实际的在运行中改变inode的例子。你可以见到很多运用这种方法的例
子。]


3.4.4 安全级别的补丁


这个补丁的目的:我引用route的话"这个补丁实际上不是一个很象样的补丁.他只不过简单
的把安全级别增加.从0增加到1.这会设置文件的不可改变只能增加位.任何人都不可以改变
他们.(通过普通的接口)。在打开这个选项以前,你应该可以使得某些关键的文件不能被改
变,某些纪录文件只能被增加。尽管还是有可能打开底层的磁盘设备,然而,一般的只会
照搬代码的hacker对此会一筹莫展。


OK,这对于一个LKM来说是很容易实现的。我们很幸运,因为关于安全级别的符号是公开的
,(见/proc/ksyms),因此我们可以很容易的改变他。我不会给出关于这一位的任何代码
。只要引入安全级别,然后在模块初始化的时候设置就可以了。


3.4.5 底层磁盘补丁


我开发了一个简单的工具来防止一些像THC的manipate-data这样的东西。这些工具被hack
er用来搜索整个磁盘的他们的原始IP地址或者DNS名字。在找到以后改变或者从硬盘中移出
这些项。当然他们只可以在控制了这个系统以后才能这么做。那我们该怎么办呢?我发现
下面的方法可以阻止这种进攻[当然了,同时也有很多方法来阻止这种保护 :( ]


启动你的系统

安装一个阻止直接存取你的保存你的纪录的那个分区的LKM


这个方案可行是因为只有当某些(很少)操作时系统(通常)需要直接存取底层磁盘。这
个LKM只要拦截sys_open(...)并过滤掉需要的设备文件就可以了。我想在这里没有必要显
示如何编他。看看2。4。2。这个方法可以保护任何/dev/* 下面的文件。问题是通过这个
方法,当LKM加载时,没有人可以直接访问他们了。


[注意:会有一些函数不能运行,或者会拖跨整个系统,但是一个正常的网络服务器,或者
邮件服务器应该都是可以正常运行的。]



--
※ 来源:.北大未名站 bbs.pku.edu.cn [FROM: 162.105.45.129]
--
※ 转寄:·北大未名站 bbs.pku.edu.cn·[FROM: 210.39.3.50]
--
※ 转载:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.36.220]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店