/* 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