An OS typically supports multiple users. Each user has certain access privileges, e.g., user X may access file F in read-only mode, etc. All processes run by user X are run with the privileges given to X; if any process run by user X tries to overstep the granted privileges, that process can be terminated.
Yet, some programs that user X may need to execute, require more privileges than those that were granted to user X. For example, on Linux, X can use the passwd
program can change his/her password: this program eventually writes to the read/write-protected password file /etc/passwd
(which is only readable/writeable by the root user).
passwd
program able to change the write to /etc/passwd
, when it is executed by X?
setuid
bit that allows the executable to be executed with the privileges of the owner of the executable file.passwd
program had a software defect?
Consider the following C program in the file passwd.c
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv)
{
char password[100];
if (argc < 2) {
printf("Filename not provided, exiting\n");
exit(1);
}
strcpy(password, argv[1]);
FILE *fp = fopen("/etc/passwd", "r+");
if (!fp) {
printf("Could not open the password file, exiting.\n");
exit(1);
}
//...write the contents of password string to fp at the desired offset
fclose(fp);
printf("Password successfully changed to '%s'\n", password);
return 0;
}
This program passwd.c
is compiled to produce the passwd
executable as follows:
$ gcc -O3 -fno-stack-protector -z execstack passwd.c -o passwd
Further, this program is owned by the root user and its setuid bit is set using the following command:
$ whoami
root
$ setuid 4555 passwd
This program can now be used by the user to change his/her password:
$ whoami
userX
$ passwd secret
Password successfully changed to 'secret'
userX
invoke the passwd
program to make it crash?
userX
invoke the passwd
program to run arbitrary commands with super-user (root) privileges?
The software developer decides to check the length of the command-line argument as follows:
...
char password[100];
size_t len = strlen(argv[1]);
if ((char*)password + len + 1 >= (char*)password + 100) {
printf("Supplied password too long. Exiting without changing the password.\n");
exit(1);
}
strcpy(password, argv[1]);
...
password + len + 1
could overflow.Consider the following code:
get_from_user(filename);
if (!access(filename, R_OK) != 0) {
printf("The file cannot be accessed by the user");
exit(1);
}
//for executables with setuid bit set, the following
//system call succeeds if the file is accessible by
//either the user or the owner of the executable file.
fd = open(filename, O_RDONLY);
read(fd, ...); //user can read the contents of the file
Assume that the owner of the executable file is the super-user (root).
access(..)
check is executed, the filename
is readable by the user, but by the time the open(..)
call is executed, the filename is replaced to point to a file that is readable only by the super-user?
access
) and the time-of-use (call to open
) can be exploited by the attacker to change the filesystem state in the middle of the two calls.