《Linux入侵监测系统LIDS原理.doc》由会员分享,可在线阅读,更多相关《Linux入侵监测系统LIDS原理.doc(21页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Linux入侵监测系统LIDS原理一、入侵 随着Internet上的Linux主机的增加,越来越多的安全漏洞在当前的GNU/Linux系统上发现。你也许在Internet上听说过 在Linux下发现bug,它会导致系统很容易的被黑客攻击。 因为Linux是一个开放代码的系统,漏洞很容易发现,并且也会很快的有补丁出来。但是当漏洞没有公布的时候,并且管理员很懒,没有去打补丁。黑客就会很容易的攻击这个系统,取得root权限,在现有的GNU/Linux下,他就可以做任何他想做的事情。现在你可以问,我们现在到底可以做些什么呢? 1.1 现在的GNU/Linux错误在哪里? 超级用户会滥用职权,他能够做所
2、有他要做的事情。作为root。他会改变所有的东西。 许多系统文件很容易被更改。这些文件可能是很重要的文件,如/bin/login,如果一个黑客进入,他可以上传一个login程序来覆盖/bin/login,这样他就可以不用登陆名和密码来登陆系统。但是这些文件不需要经常改动,除非你要升级系统。 模块modules很容易用来中断内核。模块是为了让Linux内核更模块话和更高效而设计的。但是当模块加入到内核,它就会成为内核的一部分并且能做原始内核能做的工作。因此,一些不友好的代码可以写成模块来加入到内核里,这些代码就会重定向系统调用并且作为一个病毒来运行。 进程是不受保护的,一些进程,如后台的web服
3、务器,一直都认为是没有严格保护的程序。因此,他们就会很容易被黑客攻击。 1.2 LIDS的设想是什么。 保护重要文件。因为文件很容易被root更改,为什么不严格文件操作呢?因此,LIDS改变了文件系统在内核里的安全系统调用。如果某个时候一些人访问一个文件,他就会进入系统调用然后我们就可以检查文件名并且看她们是否被保护。如果它已经被保护,我们就可以拒绝这个访问者的要求。 保护重要的进程。这个和上面的保护进程的想法不是一样的。当一个系统里运行一个进程,它会在/proc 文件系统里有一个用pid作为路径名的入口。所以,如果你用“ps axf”你就可以显示出当前运行的进程。你可以问如果保护这些进程。如
4、果你要杀死一个进程的话,首先,你键入“ps”来得到进程的PID,然后,你键入“kill pid”来杀死它。但是,如果我不让你看到进程,你怎么来杀死这个进程呢?因此,LIDS是用隐藏进程来保护它的。 另外一个重要的方法就是不让任何人可以杀死进程,包括root用户。LIDS能够保护父进程是init(pid=1)的所有进程 。 封装内核。有时候我们需要要把一些必要的模块加入到内核里来使用,另外,我们也要拒绝任何人包括root用户向内核插入模块。那么如何来平衡这个矛盾的问题呢?我们可以只允许在系统启动的时候插入模块,然后我们封装模块,在封装后,内核不允许任何人插入模块到内核里。通过这种封装功能,我们能
5、用它来保护重要的文件,进程,我们可以在系统启动的时候只允许必要的进程,只改变必要的文件。在封装内核后,我们就不能在对文件有任何的修改。二、保护文件系统 2.1 保护文件系统是LIDS的重要功能之一。这个功能是在内核的VFS(虚拟文件系统)层实现的,我们可以保护任何种类的文件系统,如EXT2,FAT。 在LIDS,保护的文件按种类分为以下几种: 只读的文件或目录。只读文件意味着它们不被允许改写,如,在目录/usr/bin,/sbin。这些类型的文件大多数都是二进制系统程序或是系统配置文件,除了在升级系统的时候,我们不需要改变它们。 只可增加文件或目录。这些文件是那些只可以增加大小的文件。大多数是
6、系统的日值文件,如在/var/log里的只可增加文件。 额外的文件或目录,这些文件没有被保护。一般来说,你想要保护目录下的所有文件,但是,还需要有一些特殊的文件不要被保护。所以我们可以定义这些文件作为额外的其他的只读文件。 保护挂载或卸载文件系统。当你在启动的时候挂载文件系统的时候,你可以禁止所有人,甚至是root,去卸载文件系统。你也可以禁止任何人在当前文件系统下挂载文件系统来覆盖它。 2.2 LIDS如何在内核保护文件 在这部分,我们会看到一些内核的代码来理解LIDS是如何保护文件的。 Linux文件系统数据结构程序 首先,我们必须了解Linux的虚拟文件系统。在Linux里的每一个文件,
7、不管是什么样子的,都有一个结点inode数,文件系统提供了以下数据结构。在/usr/src/Linux/include/Linux/fs.h struct inode struct list_head i_hash; struct list_head i_list; struct list_head i_dentry; unsigned long i_ino; - inode number. unsigned int i_count; kdev_t i_dev; - device number. umode_t i_mode; nlink_t i_nlink; uid_t i_uid; . 注
8、意:用来鉴定一个结点inode。这个意思是你可以用一对来得到一个系统里独一无二的inode。 在/ur/src/Linux/cinclude/Linux/dcache.h里 struct dentry int d_count; unsigned int d_flags; struct inode * d_inode; /* Where the name belongs to - NULL is negative */ struct dentry * d_parent; /* parent directory */ struct dentry * d_mounts; /* mount infor
9、mation */ struct dentry * d_covers; struct list_head d_hash; /* lookup hash list */ struct list_head d_lru; /* d_count = 0 LRU list */ struct list_head d_child; /* child of parent list */ struct list_head d_subdirs; /* our . dentry是一个目录文件的入口。通过这个入口,我们可以很容易的在文件的父目录下移动。 例如,如果你一文件的inode是(struct inode*)
10、file_inode,如果你可以用file_inode-d_entry来得到它的目录入口并且用file_inode-d_entry-d_parent来得到父目录的目录入口。 LIDS保护数据结构在分析完Linux文件系统后,让我们来看看LIDS是如何容VFS来保护文件和目录的。 在/usr/src/Linux/fs/lids.c struct secure_ino unsigned long int ino; /* the inode number */ kdev_t dev; /* the dev number */ int type; /* the file type */ ; 上面的结构
11、用一对来存储保护文件或目录的结点。“type”是用来标明保护结点文件类型的。 LIDS有4种类型在/usr/src/Linux/include/Linux/fs.h #define LIDS_APPEND 1 /* APPEND ONLY FILE */ #define LIDS_READONLY 2 /* Read Only File */ #define LIDS_DEVICE 3 /* Protect MBR Writing to device */ #define LIDS_IGNORE 4 /* Ignore the protection */ 通过secure_ino结构,我们能很
12、容易的初使化保护的文件或是在内核里执行以下函数。 在/usr/src/Linux/fs/lids.c int lids_add_inode(unsigned long int inode ,kdev_t dev , int type) if ( last_secure = (LIDS_MAX_INODE-1) return 0; securelast_secure.ino = inode; securelast_secure.dev = dev; securelast_secure.type = type; secure+last_secure.ino = 0; #ifdef VFS_SECU
13、RITY_DEBUG printk(lids_add_inode : return %d,last_secure); #endif return last_secure; 就象你在上面代码上可以看到的,给secure_ino加到一个结点上是非常容易的。被保护的结点会在系统启动的时候初使化。初使化程序在/usr/src/Linux/fs/lids.c的init_vfs_security()里。 现在,让我们看看LIDS是如何来检查是否一个结点已经受到保护。 在/usr/src/Linux/fs/open.c int do_truncate(struct dentry *dentry, unsig
14、ned long length) struct inode *inode = dentry-d_inode; int error; struct iattr newattrs; /* Not pretty: inode-i_size shouldnt really be off_t. But it is. */ if (off_t) length d_inode-i_dev), MINOR(dentry-d_inode-i_dev), dentry-d_inode-i_ino); . 这个是LIDS加到内核里做检测的一个例子。你会看到lids_check_base()是LIDS保护方法的一个核
15、心函数。 你可以在LIDS要保护的地方看到很多LIDS保护方法用到lids_check_base()函数,特别是在Linux内核的子目录下。 在/usr/src/Linux/fs/lids.c int lids_check_base(struct dentry *base, int flag) . inode = base-d_inode; /* get the inode number */ parent = base-d_parent; /* get the parent diretory */ . - do if ( inode = parent-d_inode) break; if (
16、retval = lids_search_inode(inode) if ( retval = LIDS_IGNORE | (retval = LIDS_DEVICE & flag != LIDS_DEVICE) break; if ( flag = LIDS_READONLY | ( flag = LIDS_APPEND & retval flag ) | ( flag = LIDS_DEVICE & flag = retval ) return -EROFS; break; inode = parent-d_inode; while( (parent = parent-d_parent )
17、 != NULL) ); return 0; lids_check_base()会检查一个给定文件的dentry和它的父目录是否被保护。注意:如果它的父目录被保护,它下面的文件也会被保护。 例如,如果“/etc/”被保护,“/etc/passwd”也一样被保护。 在内核保护系统调用 为了保护系统,LIDS会在一些检查临界的系统调用的时候做检查。因此,我们可以保护系统调用和限制文件系统的用户调用。 这些是一些例子, open(),open是通过禁止一些权利来保护文件的打开。 你可以在打开调用open_namei()调用的时候LIDS在检测它。 mknod(),mknod是用来在指定目录下保护mk
18、nod。 unlink(), 在内核代码检查do_unlink()。 三、保护设备 Linux的设备会在/dev/目录下以文件的形式列出,我们可以用上面保护文件的方法来保护设备。但是在一些情况下,用户也可以用IO操作来旁路文件系统来读写设备,我们必须注意这个问题。 3.1 设备,内核I/O 在GNU/Linux系统下的设备会以文件的形式表达,所以我们可以用保护文件系统那样来保护设备。 用户的I/O访问是通过系统调用sys_operm和sys_iopl来实现的。你可以看看/usr/src/Linux/arch/i386/kernel/ioport.。这个是要基于系统结构的,要是到其他平台,就需要
19、注意它们的变化。 3.2 如何用LIDS来保护 大多数情况下,程序不需要通过在/dev的设备文件名称来访问设备。但是,一些特殊的程序需要直接访问,如X Server,这个会写到/dev/mem和甚至是I/O设备。我们需要一些额外的东西来保护设备。LIDS会在配置内核的时候来定义这个功能。 CONFIG_LIDS_ALLOW_DEV_MEM,如果你选择了开启这个功能,你就可以允许一些特殊程序来访问/dev/men和/dev/kmen这些内核临界的设备。如果你想要用内核的X Server,选择这个功能就会在配置内核的时候提供整个路径和文件名。 CONFIG_LIDS_ALLOW_RAW_DISKS
20、,如果选择这个开启,你就可以允许一些特殊的程序来访问物理磁盘。CONFIG_LIDS_ALLOW_IO_PORTS,如果你选择了开启这个功能,你就可以允许一些特殊的程序来访I/O端口。 当系统运行fs/lids.c里的init_vfs_security()的时候初使化就被调用。#ifdef CONFIG_LIDS_ALLOW_DEV_MEM lids_fill_table(allow_dev_mem,&last_dev_mem,LIDS_MAX_ALLOWED,CONFIG_LIDS_DEV_MEM_PROGS); #endif #ifdef CONFIG_LIDS_ALLOW_RAW_DIS
21、KS lids_fill_table(allow_raw_disks,&last_raw_disks,LIDS_MAX_ALLOWED,CONFIG_LIDS_RAW_DISKS_PROGS); #endif #ifdef CONFIG_LIDS_ALLOW_IO_PORTS lids_fill_table(allow_io_ports,&last_io_ports,LIDS_MAX_ALLOWED,CONFIG_LIDS_IO_PORTS_PROGS); #endif 如果一个进程或是程序要直接访问ip端口或是磁盘设备,LIDS就会检查它在数组 allow_raw_disk,last_io_
22、ports,等)。这个检查是通过调用lids_check_base()里的lids_search_inode(inode)来实现的。 如,让我们看看CONFIG_LIDS_ALLOW_DEV_MEM /* in lids_search_inode() */ #ifdef CONFIG_LIDS_ALLOW_DEV_MEM for( i = 0 ; i flags & PF_KILLINITC) #endif if (lids_load & lids_local_load & LIDS_FISSET(lids_flags,LIDS_FLAGS_LOCK_INIT_CHILDREN) this_p
23、id = pid0?pid:-pid; for(i=0;i if( this_pid = lids_protected_pidi) lids_security_alert(Try to kill pid=%d,sig=%d,pid,sig); return -EPERM; #endif . 你可以在内核里看到两个标签,,CONFIG_LIDS_INIT_CHILDREN_LOCK 和CONFIG_LIDS_ALLOW_KILL_INIT_CHILDREN. 在CONFIG_LIDS_INIT_CHILDREN_LOCK的开启状态,LIDS能保护初使的运行程序。如,如果你在系统里运行inetd程
24、序,你可以在隐藏内核前运行它,然后,你还可以杀死它。但是一些人如果telnet到你的机器,inetd就会创造子进程来为用户服务,这个子进程不会被LIDS保护,因为用户在任何时候退出和杀死程序。 4.2 隐藏进程 另外一个保护进程的方法就是隐藏进程。当一个黑客危机你的系统。他会登陆,然后会看看有没有一些已知的进程在监视它。然后他就杀死它。如果你隐藏了这个功能的进程,黑客就不会知道进程的所有情况并且你可以记录他在你系统上做的任何事情。 如何隐藏进程 为了隐藏进程,你必须在配置内核的时候提供一个完全的路径名。 当内核启动的时候,LIDS会访问文件结点到一个叫proc_to_hide的结构里。 在in
25、clude/Linux/sched.h里 #ifdef CONFIG_LIDS_HIDE_PROC #define PF_HIDDEN 0x04000000 /* Hidden process */ #endif /* in fs/lids.c */ #ifdef CONFIG_LIDS_HIDE_PROC struct allowed_ino proc_to_hideLIDS_MAX_ALLOWED; int last_hide=0; #endif . /* in fs/lids.c , init_vfs_security(), fill up the hidden process in p
26、roc_to_hide */ #ifdef CONFIG_LIDS_HIDE_PROC lids_fill_table(proc_to_hide,&last_hide,LIDS_MAX_ALLOWED,CONFIG_LIDS_HIDDEN_PROC_PATH); #endif PF_HIDDEN是否用户可以用显示进程的命令(如“ps a”)来显示和检查进程,如果一个进程被LIDS隐藏,当他执行的时候,进程就会得到一个PF_HIDDEN的属性。然后,当系统输出系统进程信息到用户的时候,它就会可以检查当前输出进程是否有PF_HIDDEN标志。如果发现了,它就不会输出这个进程的信息。 在in fs/
27、exec.c int do_execve(char * filename, char * argv, char * envp, struct pt_regs * regs) . if (retval = 0) #ifdef CONFIG_LIDS_HIDE_PROC if (lids_search_proc_to_hide(dentry-d_inode) current-flags |= PF_HIDDEN; . 因为每一个Linux的进程都有一个在/proc文件系统的入口,我们为了隐藏进程也需要修改proc的文件入口。在fs/proc/root.c static struct dentry
28、*proc_root_lookup(struct inode * dir, struct dentry * dentry) . inode = NULL; #ifdef CONFIG_LIDS_HIDE_PROC if ( pid & p & (! (p-flags & PF_HIDDEN) & lids_load & lids_local_load) ) #else if (pid & p) #endif unsigned long ino = (pid 16) + PROC_PID_INO; inode = proc_get_inode(dir-i_sb, ino, &proc_pid);
29、 if (!inode) return ERR_PTR(-EINVAL); inode-i_flags|=S_IMMUTABLE; . 然后如果进程被PF_HIDDEN标记,它就不会在proc文件系统里显示。五密封内核 我们需要在系统启动的时候做一些必要的操作,但是我们也需要在系统运行的时候保护它们。 例如,我们需要象内核里插入一些需要的模块,但是我们不希望在系统运行的时候插入任何模块,因为那样会十分危险。如何解决这个问题呢?这里就有一些密封的方法。我们可以在系统启动的时候做我们任何想做的事情,然后我们就密封内核。然后,我们就不能做那些以前在没有密封的时候可以做的事情。用密封的方法,我们可以用
30、模块来解决问题,我们可以在密封前向内核里插入我们想要的模块,在密封后我们就不可以在内核里插入或是删除任何模块。 5.1 用LIDS密封内核 为了密封内核,我们可以用下面的LIDS命令 #lidsadm I - -CAP_xxx. 它们可以放到脚本里让系统启动的时候就执行它。具体你们可以看我以前在linuxbyte和chinabyte发表的文章。LIDS是通过/proc/sys/lids/locks和内核通讯的。 当你密封了内核,lidsadm是调用lidsadm.c的lids_init()的调用。 #define LIDS_LOCKS /proc/sys/lids/locks . void l
31、ids_init(int optind, int argc, char *argv) . if (fd=open(LIDS_LOCKS,O_RDWR) = -1) perror(open); exit_error (2, cant open LIDS_LOCKS); if (read(fd,&locks,sizeof(lids_locks_t)=-1) perror(read); exit_error (2, cant read LIDS_LOCKS); lids_set_caps(optind,argc,argv,&locks); locks.magic1=LIDS_MAGIC_1; . i
32、f (write(fd,&locks,sizeof(lids_locks_t)=-1) perror(write); exit_error (2, cant write LIDS_LOCKS); . 这个系统调用在LIDS_LOCKS生成新的变量loks,内核会通过lids_proc_locks_sysctl()命令来读取它。Lids_proc_locks_sysctl也会从用户区完全检查并读取它,然后改变密封的变量lids_first_time为0。 你会看到当LIDS保护系统开启(lids_load=1)和当前系统没有密封(lids_firest_time 为1)的时候,内核就会检查当前程
33、序是否在LIDS的lids_check_base()保护下。如果没有被保护,它就会启动报警信息。 作为管理员一个需要理解的重要东西是每一个capability的意思。然后,在密封内核的时候禁止capability,并用密码来改变它们。七LIDS在内核中的安全级别 一些时候,我们需要改变保护系统的配置。那样我们该怎么做呢?LIDS给我们提供了两种方法。 我们可以重新启动系统,在LILO:里键入security=0. 我们可以在用lidsadm S中用密码来转换安全级别。 7.1 在内核中的两个安全级别 LIDS在内核中定义了两个安全级别,安全的security和无安全的none_security
34、.默认情况下,是设置成安全的级别。如果你需要改变它。就在启动的时候键入security=0. 在内核中有一个全局变量lids_load。它表明是否lids的安全变量security被开启。它默认是1。如果在系统启动的时候在LILO:键入security=0 ,所有的LIDS的保护都会失效,就象那些没有LIDS保护的系统一样。 /* variant defined in fs/lids.c */ int lids_reload_conf=0; int lids_load=0; /* it is raised to 1 when kernel boot */ int lids_local_on=1
35、; int lids_local_pid=0; /* in init/main.c */ #ifdef CONFIG_LIDS /* * lids_setup , read lids info from the kernel. */ static void _init lids_setup(char *str, int *ints) if (ints0 0 & ints1 = 0) = _lids_load= ints1; #endif . /* init the LIDS when the system bootup up */ static void _init do_basic_setu
36、p(void) . /* Mount the root filesystem. */ mount_root(); #ifdef CONFIG_LIDS /* init the ids file system */ - lids_load=_lids_load; lids_local_on=_lids_load; lids_flags=lids_load * (LIDS_FLAGS_LIDS_ON | LIDS_FLAGS_LIDS_LOCAL_ON); = printk(Linux Intrusion Detection System %s n,lids_load=1?starts:stops
37、); init_vfs_security(); #endif . 在系统启动的时候,你可以看到”Linux Intrusion Detection System 0.9 starts”,表明LIDS的保护开启了。当保护停止的时候,你可以看到”Linux Intrusion Detection System 0.9 stops”。这里的0.9是当前的LIDS版本号。 7.2 用lidsadm来改变系统安全级别 一些时候,有也可以在线的时候改变你的安全级别,你必须把CONFIG_LIDS_ALLOW_SWITCH功能开启。并且在编译前配置内核的时候提供一个RipeMD-160 encrypted
38、password 。 这个密码可以用lidsadm p 命令来获得 用内核鉴定 用提供的密码,LIDS可以鉴定用户来区分哪个用户可以转换内核的安全级别。 这个功能也要用到lidsadm的-S参数。如。 # /sbin/lidsadm -S - -LIDS SWITCH Password:xxxxxx # 在输入密码后,我们就可以转换LIDS的安全关闭。 让我们看看内部的编码来了解它是如何工作的, /* in the fs/lids.c lids_proc_locks_sysctl() */ int lids_proc_locks_sysctl(ctl_table *table, int wri
39、te, struct file *filp, void *buffer, size_t *lenp, int conv, int op) lids_locks_t locks; byte hashcodeRMDsize/8; char rmd160sig170; . locks.passwdsizeof(passwd_t)-1=0; /* We dont take the risk */ rmd160sig0=0; #ifdef CONFIG_LIDS_ALLOW_SWITCH if (!lids_first_time) | (locks.passwd0) RMD(byte *)locks.p
40、asswd,hashcode); memset(char *)locks.passwd,sizeof(passwd_t); for (i=0; i sprintf(rmd160sig+2*i,%02x, hashcodei); if ( (lids_first_time) & (!locks.passwd0) | - (!strncmp(rmd160sig,CONFIG_LIDS_RMD160_PASSWD,160) ) #else if (lids_first_time) & (!locks.passwd0) #endif /* access granted ! */ number_fail
41、ed=0; if (lids_process_flags(locks.flags) cap_bset=locks.cap_bset; lids_security_alert(Changed: cap_bset=0x%x lids_flags=0x%x,cap_t(cap_bset),lids_flags); lids_first_time=0; . 在密码检查正确后。Lids_process_flag()就会改变当前的lids标记为关闭状态,然后你就可以在不受保护的系统做你想要做的事情了。你可以看看fs/lids.c的lids_process_flag的代码来了解它。 转换LIDS和LIDA_
42、GLOBAL 如果你把LIDS的保护关闭,你会有两个结果,一,关闭后其它没有被LIDS保护的控制台一样不受保护,二,可以本地的关闭它们,在其它的控制台,所有的系统依然被LIDS保护。它们一样很安全。 这些细节是fs/lids的lids_process_flag()来实现。 八内核的网络安全 用LIDS,你可以用下面的功能来保护你的网络。 8.1 保护防火墙和路由的规则 如果你的主机包含一些防火墙规则。你可以用LIDS来保护它们。你可以开启 CONFIG_LIDS_ALLOW_CHANGE_ROUTESLAI 实现这个功能。你也必须在密封内核的时候关闭CAP_NET_ADMIN。 然后,你也可以允许程序更改路由规则。 让我们来看看保护防火墙规则的代码吧。每一个改变防火墙的请求都会调用内核的ip_