11.2.3 Race Conditions with -exec

The ‘-exec’ action causes another program to be run. It passes to the program the name of the file which is being considered at the time. The invoked program will typically then perform some action on that file. Once again, there is a race condition which can be exploited here. We shall take as a specific example the command

find /tmp -path /tmp/umsp/passwd -exec /bin/rm

In this simple example, we are identifying just one file to be deleted and invoking /bin/rm to delete it. A problem exists because there is a time gap between the point where find decides that it needs to process the ‘-exec’ action and the point where the /bin/rm command actually issues the unlink() system call to delete the file from the filesystem. Within this time period, an attacker can rename the /tmp/umsp directory, replacing it with a symbolic link to /etc. There is no way for /bin/rm to determine that it is working on the same file that find had in mind. Once the symbolic link is in place, the attacker has persuaded find to cause the deletion of the /etc/passwd file, which is not the effect intended by the command which was actually invoked.

One possible defence against this type of attack is to modify the behaviour of ‘-exec’ so that the /bin/rm command is run with the argument ./passwd and a suitable choice of working directory. This would allow the normal sanity check that find performs to protect against this form of attack too. Unfortunately, this strategy cannot be used as the POSIX standard specifies that the current working directory for commands invoked with ‘-exec’ must be the same as the current working directory from which find was invoked. This means that the ‘-exec’ action is inherently insecure and can’t be fixed.

GNU find implements a more secure variant of the ‘-exec’ action, ‘-execdir’. The ‘-execdir’ action ensures that it is not necessary to dereference subdirectories to process target files. The current directory used to invoke programs is the same as the directory in which the file to be processed exists (/tmp/umsp in our example, and only the basename of the file to be processed is passed to the invoked command, with a ‘./’ prepended (giving ./passwd in our example).

The ‘-execdir’ action refuses to do anything if the current directory is included in the PATH environment variable. This is necessary because ‘-execdir’ runs programs in the same directory in which it finds files – in general, such a directory might be writable by untrusted users. For similar reasons, ‘-execdir’ does not allow ‘{}’ to appear in the name of the command to be run.