歡迎您光臨本站 註冊首頁

Linux入侵監測系統LIDS原理

←手機掃碼閱讀     火星人 @ 2014-03-29 , reply:0

  一、入侵

  隨著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用戶向內核插入模塊。那麼如何來平衡這個矛盾的問題呢?我們可以只允許在系統啟動的時候插入模塊,然後我們封裝模塊,在封裝后,內核不允許任何人插入模塊到內核里。通過這種封裝功能,我們能用它來保護重要的文件,進程,我們可以在系統啟動的時候只允許必要的進程,只改變必要的文件。在封裝內核后,我們就不能在對文件有任何的修改。

  二、保護文件系統

  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

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;
......
}



  注意:用來鑒定一個結點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 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是一個目錄文件的入口。通過這個入口,我們可以很容易的在文件的父目錄下移動。

  例如,如果你一文件的inode是(struct inode*)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 */
};



  上面的結構用一對來存儲保護文件或目錄的結點。「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結構,我們能很容易的初使化保護的文件或是在內核里執行以下函數。

在/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;

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()里。

  現在,讓我們看看LIDS是如何來檢查是否一個結點已經受到保護。

在/usr/src/Linux/fs/open.c
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保護方法的一個核心函數。

  你可以在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 ((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和它的父目錄是否被保護。



  注意:如果它的父目錄被保護,它下面的文件也會被保護。

  例如,如果「/etc/」被保護,「/etc/passwd」也一樣被保護。

  在內核保護系統調用

  為了保護系統,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
lids_fill_table(allow_dev_mem,&last_dev_mem,LIDS_MAX_ALLOWED,CONFIG_LIDS_DEV_MEM_PROGS);
#endif

#ifdef CONFIG_LIDS_ALLOW_RAW_DISKS
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_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 < last_dev_mem ;i++ ) {
if ( allow_dev_mem.ino == ino && allow_dev_mem.dev == dev) {
return LIDS_READONLY;
}
}
#endif
#ifdef CONFIG_LIDS_ALLOW_RAW_DISKS



  在allow_dev_mem包括了哪一個程序結點在系統啟動的時候在init_vfs_security()里初使化。用同樣的方法,除了一些特殊程序,我們可以保護設備,I/O訪問等等。

  四、保護重要進程

  進程是操作系統的動態入口。內核里有兩個特殊進程,進程ID 0 (swapd) 和進程ID 1(init)。Init進程是在系統啟動的時候所有進程的父進程。

  4.1 不可殺死的進程。

  就象你可以看到是否有人要奪得root特權一樣,我們可以很容易的殺死那些該內核發送特別信號的進程。為了殺死一個進程,你必須得到進程的ID,然後用kill命令來殺死它。

  系統殺死進程的調用是kill,是在內核里的sys_kill()命令里的調用。

  讓我們看看LIDS的保護代碼

在/usr/src/Linux/kernel/signal.c里

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) {
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保護,因為用戶在任何時候退出和殺死程序。

  4.2 隱藏進程

  另外一個保護進程的方法就是隱藏進程。當一個黑客危機你的系統。他會登陸,然後會看看有沒有一些已知的進程在監視它。然後他就殺死它。如果你隱藏了這個功能的進程,黑客就不會知道進程的所有情況並且你可以記錄他在你系統上做的任何事情。

  如何隱藏進程

  為了隱藏進程,你必須在配置內核的時候提供一個完全的路徑名。

  當內核啟動的時候,LIDS會訪問文件結點到一個叫proc_to_hide[]的結構里。

在include/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_hide[LIDS_MAX_ALLOWED];
int last_hide=0;
#endif
....

/* in fs/lids.c , init_vfs_security(),
fill up the hidden process in proc_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/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 *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文件系統里顯示。

[火星人 ] Linux入侵監測系統LIDS原理已經有500次圍觀

http://coctec.com/docs/linux/show-post-204486.html