Saturday, September 27, 2014

Unix Prog: System Function

1. Definition:

 /* Execute the given line as a shell command.  
   
   This function is a cancellation point and therefore not marked with  
   __THROW. */  
 extern int system (const char *__command) __wur;  

system function is implemented with "fork", "exec" and "waitpid"
If argument is NULL, it will return non-zero if system function is supported.
If "fork" fails, it will return -1.
If "exec" fails, it will return 127.
If "everything succeed", it will return status specified by "waitpid"

2. Own System Implementation without signal handling

system.c:
 #include<sys/wait.h>  
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
   
 void pr_exit(int status)  
 {  
  if(WIFEXITED(status)) {  
   printf("normal termination: exit status = %d\n", WEXITSTATUS(status));  
  }  
  else if(WIFSIGNALED(status)) {  
   printf("abnormal termination: exit status = %d%s\n",  
       WTERMSIG(status), WCOREDUMP(status)? "(core file generated)" : "");  
  }  
  else if(WIFSTOPPED(status)) {  
   printf("child stopped, signal number = %d\n", WSTOPSIG(status));  
  }  
 }  
   
 int system(const char* cmdstring)  
 {  
  int status;  
  pid_t pid;  
   
  // Return non-zero if argument is NULL and system function is supported  
  // in current system  
  if(cmdstring == NULL)  
   return 1;  
   
  // If "fork" fail, return -1  
  if((pid = fork()) < 0) {  
   status = -1;  
  } else if (pid == 0) {  
   execl("/bin/sh", "sh", "-c", cmdstring, (char*)0);  
   _exit(127); // if "exec" fails, return status 127  
  } else {  
   if(waitpid(pid, &status, 0) < 0) {  
    printf("waitpid error!\n");  
    status = -1; // if "waitpid" fails, return status -1  
   }  
  }  
   
  return(status);  
 }  
   
 int main(int argc, char* argv[])  
 {  
  int status;  
   
  status = system("date");  
  pr_exit(status);  
   
  status = system("nosuchcommand");  
  printf("status: %d\n", status); // This is wrong, only part of bits indicate the return code  
                  // We need to use macro to get it.  
  pr_exit(status);  
   
  exit(0);  
 }  

shell:
1) First line, output by command "date"
2) Second line, output by pr_exit per status returned by "date"
3) Third line, output by command "nosuchcommand"
4) Fourth line, output by printing out status directly, which is wrong value, since only parts of bits are returning status
5) Fifth line, use macro to retrieve the termination status, output 127 successfully.
 ubuntu@ip-172-31-23-227:~$ ./system.out  
 Sat Sep 27 16:05:57 UTC 2014  
 normal termination: exit status = 0  
 sh: 1: nosuchcommand: not found  
 status: 32512  
 normal termination: exit status = 127  

The advantage of using "system" function is it has all error handling and signal handling, in our above implementation, we don't have any signal handling yet.

3. Security hole

tsys.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
   
 int main(int argc, char* argv[])  
 {  
  int status;  
   
  if(argc < 2){  
   printf("command-line arguments required!\n");  
   exit(1);  
  }  
   
  if((status = system(argv[1])) < 0) {  
   printf("system error!\n");  
   exit(2);  
  }  
   
  exit(0);  
 }  

printuid.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
   
 int main(int argc, char* argv[])  
 {  
  printf("real user id: %d, effective user id: %d\n", getuid(), geteuid());  
  exit(0);  
 }  

shell:
1) Run tsys.out to execute printuid.out, and real user id and effective user id are both 1000
2) change the owner of tsys.out to "root", super user
3) setup the set-user-bit at tsys.out, so any process running tsys.out, its effective user id will be set to the owner of tsys.out, root
4) List out task files.
5) Run the tsys.out to execute printuid.out, current process's effective user id is already changed to "root". So it means, any user can use tsys.out to do whatever we want, which is a serious security hole.
 ubuntu@ip-172-31-23-227:~$ ./tsys.out ./printuid.out  
 real user id: 1000, effective user id: 1000  
 ubuntu@ip-172-31-23-227:~$ sudo chown root ./tsys.out  
 ubuntu@ip-172-31-23-227:~$ sudo chmod u+s ./tsys.out  
 ubuntu@ip-172-31-23-227:~$ ls -lrt ./*.out  
 -rwsrwxr-x 1 root  ubuntu 9748 Sep 27 17:33 ./tsys.out  
 -rwxrwxr-x 1 ubuntu ubuntu 9775 Sep 27 17:36 ./printuid.out  
 ubuntu@ip-172-31-23-227:~$ ./tsys.out ./printuid.out  
 real user id: 1000, effective user id: 0  

No comments:

Post a Comment