china.com
主页
新闻
体育
游戏
文化
教育
健康
财经
科技
旅游
军事
娱乐
商贸
  科技动态 硬件广场 下载基地 网络教室 网络冲浪 科学博览 移动时代 手机上网 桌面壁纸 科技商情  


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


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


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

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

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你也可以改变inode。只不过需要替换sys_execve(...)并且使用namei(...)(这个方法我们也用于执行控制),然后操纵inode(我会在5。3中给你们一个实用的例子)。

3.4.2 链接的补丁


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

我必须承认链接的补丁对于我们LKM来说是一个不是太容易接触到的层次。既没有输出的符号,也没有可以拦截的系统调用。解析链接是VFS做的。可以看看我们在第四部分解决这个问题的方法。(但是我不会使用第四部分的方法来进行系统安全维护)。你也许会奇怪为什么我不使用sys_readlink(...)系统调用来解决这个问题。因为当你运行'ls -a syslink'时,会进行这个系统调用,然而,当运行'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这样的东西。这些工具被hacker用来搜索整个磁盘的他们的原始IP地址或者DNS名字。在找到以后改变或者从硬盘中移出这些项。当然他们只可以在控制了这个系统以后才能这么做。那我们该怎么办呢?我发现下面的方法可以阻止这种进攻[当然了,同时也有很多方法来阻止这种保护 :( ]

启动你的系统

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

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

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

 

  摘自《赛迪网》 pragmatic/THC,(版本1.0)/文

 


科技检索


中华网推荐

  • 1000名医生在线咨询

  • 中国足球队官方网站

  • 鸦片玫瑰(新版)

  • 精选股票天地

  • 闪光的flash教程

  • 中华网汽车世界

  • 为你的爱情出谋划策

  • 网文精选——野百合集

  • 世界文化遗产在中国

  • 历届香港小姐风姿集




  • 网络教室编辑信箱
    版权声明 | 本站检索 | 联系方法 | 刊登广告 | 使用说明 | 关于中华网 | 豁免条款

    版权所有 中华网