网络技术是从1990年代中期发展起来的新技术,它把互联网上分散的资源融为有机整体,实现资源的全面共享和有机协作,使人们能够透明地使用资源的整体能力并按需获取信息。资源包括高性能计算机、存储资源、数据资源、信息资源、知识资源、专家资源、大型数据库、网络、传感器等。 当前的互联网只限于信息共享,网络则被认为是互联网发展的第三阶段。 一、入侵 随着Internet上的Linux主机的增加,越来越多的安全漏洞在当前的GNU/Linux系统上发现。你也许在Internet上听说过 在Linux下发现bug,它会导致系统很容易的被黑客攻击。 因为Linux是一个开放代码的系统,漏洞很容易发现 ,并且也会很快的有补丁出来。但是当漏洞没有公布的时候,并且管理员很懒,没有去打补丁。黑客就会很容易的攻击这个系统,取得root权限,在现有的GNU/Linux下,他就可以做任何他想做的事情。现在你可以问,我们现在到底可以做些什么呢? 1.1 现在的GNU/Linux错误在哪里? 超级用户会滥用职权,他能够做所有他要做的事情。作为root。他会改变所有的东西。 许多系统文件很容易被更改。这些文件可能是很重要的文件,如/bin/login,如果一个黑客进入,他可以上传一个login程序来覆盖/bin/login,这样他就可以不用登陆名和密码来登陆系统。但是这些文件不需要经常改动,除非你要升级系统。 模块modules很容易用来中断内核。模块是为了让Linux内核更模块话和更高效而设计的。但是当模块加入到内核,它就会成为内核的一部分并且能做原始内核能做的工作。因此,一些不友好的代码可以写成模块来加入到内核里,这些代码就会重定向系统调用并且作为一个病毒来运行。 进程是不受保护的,一些进程,如后台的web服务器,一直都认为是没有严格保护的程序。因此,他们就会很容易被黑客攻击。 1.2 LIDS的设想是什么。 保护重要文件。因为文件很容易被root更改,为什么不严格文件操作呢?因此,LIDS改变了文件系统在内核里的安全系统调用。如果某个时候一些人访问一个文件,他就会进入系统调用然后我们就可以检查文件名并且看她们是否被保护。如果它已经被保护,我们就可以拒绝这个访问者的要求。 保护重要的进程。这个和上面的保护进程的想法不是一样的。当一个系统里运行一个进程,它会在/proc 文件系统里有一个用pid作为路径名的入口。所以,如果你用“ps –axf”你就可以显示出当前运行的进程。你可以问如果保护这些进程。如果你要杀死一个进程的话,首先,你键入“ps”来得到进程的PID,然后,你键入“kill 〈pid〉”来杀死它。但是,如果我不让你看到进程,你怎么来杀死这个进程呢?因此,LIDS是用隐藏进程来保护它的。 另外一个重要的方法就是不让任何人可以杀死进程,包括root用户。LIDS能够保护父进程是init(pid=1)的所有进程 。 封装内核。有时候我们需要要把一些必要的模块加入到内核里来使用,另外,我们也要拒绝任何人包括root用户向内核插入模块。那么如何来平衡这个矛盾的问题呢?我们可以只允许在系统启动的时候插入模块,然后我们封装模块,在封装后,内核不允许任何人插入模块到内核里。通过这种封装功能,我们能用它来保护重要的文件,进程,我们可以在系统启动的时候只允许必要的进程,只改变必要的文件。在封装内核后,我们就不能在对文件有任何的修改。 [page_break]二、保护文件系统 2.1 保护文件系统是LIDS的重要功能之一。这个功能是在内核的VFS(虚拟文件系统)层实现的,我们可以保护任何种类的文件系统,如EXT2,FAT。 在LIDS,保护的文件按种类分为以下几种: 只读的文件或目录。只读文件意味着它们不被允许改写,如,在目录/usr/bin,/sbin。这些类型的文件大多数都是二进制系统程序或是系统配置文件,除了在升级系统的时候,我们不需要改变它们。 只可增加文件或目录。这些文件是那些只可以增加大小的文件。大多数是系统的日值文件,如在/var/log里的只可增加文件。 额外的文件或目录,这些文件没有被保护。一般来说,你想要保护目录下的所有文件,但是,还需要有一些特殊的文件不要被保护。所以我们可以定义这些文件作为额外的其他的只读文件。 保护挂载或卸载文件系统。当你在启动的时候挂载文件系统的时候,你可以禁止所有人,甚至是root,去卸载文件系统。你也可以禁止任何人在当前文件系统下挂载文件系统来覆盖它。 2.2 LIDS如何在内核保护文件 在这部分,我们会看到一些内核的代码来理解LIDS是如何保护文件的。 Linux文件系统数据结构程序 首先,我们必须了解Linux的虚拟文件系统。 在Linux里的每一个文件,不管是什么样子的,都有一个结点inode数,文件系统提供了以下数据结构。 在/usr/src/Linux/include/Linux/fs.h 注意:用来鉴定一个结点inode。这个意思是你可以用一对来得到一个系统里独一无二的inode。 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 information */ 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是一个目录文件的入口。通过这个入口,我们可以很容易的在文件的父目录下移动。 [page_break]LIDS保护数据结构 在分析完Linux文件系统后,让我们来看看LIDS是如何容VFS来保护文件和目录的。 在/usr/src/Linux/fs/lids.c 上面的结构用一对来存储保护文件或目录的结点。“type”是用来标明保护结点文件类型的。 int lids_add_inode(unsigned long int inode ,kdev_t dev , int type) { if ( last_secure == (LIDS_MAX_INODE-1)) return 0; secure[last_secure].ino = inode; secure[last_secure].dev = dev; secure[last_secure].type = type; secure[++last_secure].ino = 0; #ifdef VFS_SECURITY_DEBUG printk("lids_add_inode : return %d ",last_secure); #endif return last_secure; } 就象你在上面代码上可以看到的,给secure_ino加到一个结点上是非常容易的。被保护的结点会在系统启动的时候初使化。初使化程序在/usr/src/Linux/fs/lids.c的init_vfs_security()里。 int do_truncate(struct dentry *dentry, unsigned 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 < 0) return -EINVAL; #ifdef CONFIG_LIDS if (lids_load && lids_local_load) { error = lids_check_base(dentry,LIDS_READONLY); if (error) { lids_security_alert("Try to truncate a protected file (dev %d %d,inode %ld)", MAJOR(dentry->d_inode->i_dev), MINOR(dentry->d_inode->i_dev), dentry->d_inode->i_ino); ..................... 这个是LIDS加到内核里做检测的一个例子。你会看到lids_check_base()是LIDS保护方法的一个核心函数。 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 ((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 ) != NULL) ); return 0; } lids_check_base()会检查一个给定文件的dentry和它的父目录是否被保护。 注意:如果它的父目录被保护,它下面的文件也会被保护。 [page_break]在内核保护系统调用 为了保护系统,LIDS会在一些检查临界的系统调用的时候做检查。因此,我们可以保护系统调用和限制文件系统的用户调用。 这些是一些例子, open(),open是通过禁止一些权利来保护文件的打开。 你可以在打开调用open_namei()调用的时候LIDS在检测它。 mknod(),mknod是用来在指定目录下保护mknod。 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.。这个是要基于系统结构的,要是到其他平台,就需要注意它们的变化。 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,如果选择这个开启,你就可以允许一些特殊的程序来访问物理磁盘。 CONFIG_LIDS_ALLOW_IO_PORTS,如果你选择了开启这个功能,你就可以允许一些特殊的程序来访I/O端口。 当系统运行fs/lids.c里的init_vfs_security()的时候初使化就被调用。 #ifdef CONFIG_LIDS_ALLOW_DEV_MEM 如果一个进程或是程序要直接访问ip端口或是磁盘设备,LIDS就会检查它在数组 allow_raw_disk,last_io_ports,等)。这个检查是通过调用lids_check_base()里的lids_search_inode(inode)来实现的。 #ifdef CONFIG_LIDS_ALLOW_DEV_MEM for( i = 0 ; i < last_dev_mem ;i++ ) { if ( allow_dev_mem[i].ino == ino && allow_dev_mem[i].dev == dev) { return LIDS_READONLY; } } #endif #ifdef CONFIG_LIDS_ALLOW_RAW_DISKS 在allow_dev_mem包括了哪一个程序结点在系统启动的时候在init_vfs_security()里初使化。用同样的方法,除了一些特殊程序,我们可以保护设备,I/O访问等等。 asmlinkage int sys_kill(int pid, int sig) { struct siginfo info; #ifdef CONFIG_LIDS_INIT_CHILDREN_LOCK pid_t this_pid; int i; #ifdef CONFIG_LIDS_ALLOW_KILL_INIT_CHILDREN if (!(current->flags & PF_KILLINITC)) #endif if (lids_load && lids_local_load && LIDS_FISSET(lids_flags,LIDS_FLAGS_LOCK_INIT_CHILDREN)) { this_pid = pid>0?pid:-pid; for(i=0;i if( this_pid == lids_protected_pid[i]) { 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程序,你可以在隐藏内核前运行它,然后,你还可以杀死它。但是一些人如果telnet到你的机器,inetd就会创造子进程来为用户服务,这个子进程不会被LIDS保护,因为用户在任何时候退出和杀死程序。 [page_break]4.2 隐藏进程 另外一个保护进程的方法就是隐藏进程。当一个黑客危机你的系统。他会登陆,然后会看看有没有一些已知的进程在监视它。然后他就杀死它。如果你隐藏了这个功能的进程,黑客就不会知道进程的所有情况并且你可以记录他在你系统上做的任何事情。 如何隐藏进程 为了隐藏进程,你必须在配置内核的时候提供一个完全的路径名。 当内核启动的时候,LIDS会访问文件结点到一个叫proc_to_hide[]的结构里。 在include/Linux/sched.h里 PF_HIDDEN是否用户可以用显示进程的命令(如“ps –a”)来显示和检查进程,如果一个进程被LIDS隐藏,当他执行的时候,进程就会得到一个PF_HIDDEN的属性。然后,当系统输出系统进程信息到用户的时候,它就会可以检查当前输出进程是否有PF_HIDDEN标志。如果发现了,它就不会输出这个进程的信息。 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的文件入口。 static struct dentry *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); if (!inode) return ERR_PTR(-EINVAL); inode->i_flags|=S_IMMUTABLE; } ... } 然后如果进程被PF_HIDDEN标记,它就不会在proc文件系统里显示。 网络的神奇作用吸引着越来越多的用户加入其中,正因如此,网络的承受能力也面临着越来越严峻的考验―从硬件上、软件上、所用标准上......,各项技术都需要适时应势,对应发展,这正是网络迅速走向进步的催化剂。 |
温馨提示:喜欢本站的话,请收藏一下本站!