Class 5

advertisement
Race conditions and
synchronization issues
Exploiting UNIX
access(2) system call

SUID programs run with privileges equal to the program owner
(typically root or some privileged user) instead of caller


SUID programs, when accessing files, may need to decide if the
caller has privileges to the file


More specifically, SUID have effective ID equal to the owner, while real
ID equal to caller
It cannot rely on the basic UNIX mechanism for enforcing file
permissions, as the privileges of the program’s owner are higher than
that of the caller
The access(2) system call was introduced to address this issue

Signature: int access(const char* path, int mode)
Real ID vs. Effective ID

access(2) determines privileges based on the real ID of the
process. The mode can be any combination or read, write, execute,
or existential test. It returns 0 (if success) or -1 in these events:










[ENOTDIR] A component of the path prefix is not a directory.
[ENAMETOOLONG] A component of a pathname exceeded {NAME_MAX}
characters, or an entire path name exceeded {PATH_MAX} characters.
[ENOENT] The named file does not exist.
[ELOOP]
Too many symbolic links were encountered in translating the pathname.
[EROFS]
Write access is requested for a file on a read-only file system.
[ETXTBSY] Write access is requested for a pure procedure (shared text) file
presently being executed.
[EACCES] Permission bits of the file mode do not permit the requested access, or
search permission is denied on a component of the path prefix.
[EFAULT] Path points outside the process's allocated address space.
[EIO]
An I/O error occurred while reading from or writing to the file system.
[EINVAL]
An invalid value was specified for mode.
Using access(2)

In usage, a call is made to access(2) to check the caller’s privileges, and if it
returns 0, a subsequent call to a file system operation such as open(2) is
performed.

Even if tightly coupled, the two operations are never atomic. In particular, both
system calls take as inputs a path name, and must evaluate it in terms of a
handle to a file

As a result, it is proper to say that access(2) determines privileges with respect
to a “snapshot” of the file system, while the subsequent call accesses a
(possibly different) snapshot.

Attackers can exploit this short time span to make improper changes to the file
system. These changes will succeed because UNIX does not enforce write
locks (e.g., modifications of the file system structure). Such enforcement is a
necessary condition for security of the combined transaction.
A TOCTTOU attack
QuickTime™ and a
TIFF (Uncompressed) decompressor
are needed to see this picture.
QuickTime™ and a
TIFF (Uncompressed) decompressor
are needed to see this picture.
Proposed solution (fork)

Dean and HU propose two portable solutions
for the access(2) TOCTTOU vulnerability:

Avoid access(2) altogether. If the SUID program needs
to open a file that should be checked against caller’s
privileges, do:
1.
2.
3.
4.

fork a new child process
use setuid(2) system call to set the effective ID of child
process equal to the caller (real ID) of parent process
Let the forked process obtain a file descriptor
Use standard UNIX IPC to pass the descriptor to parent
This solution enforces consistency, so it is
secure. However, it is also slow.
Proposed solution (k-race)
Strategy of k-race



Force the attacker to win multiple races
Each call to access must be preceded by a switch
of the symbolic link to some public file, while each
call to open must resolve to the same file
(attacker’s intended target)
Additionally, after the first race, requests for file
descriptors access the cache, which being very
fast, results in attackers always losing the race.
New attack



Circumvent caching by creating many symbolic links,
without re-using a link in any following race
Force victim to sleep on I/O, giving attacker execution time
between races in which to modify symlinks
Dramatically increase the probability of success by:


Using a symlink maze to dramatically increase the probability
that the victim will sleep in each race
Use atime system call (which says when a directory has been
traversed last) to predict when it is safe to change an
intermediate symlink to point to system file
I/O delays





Instead of dir0, use dir/dir/…/dir/lnk
Victim will sleep if at least one directory in the path is not in
cache
Use chains of MAXPATHLEN (typically 1024-4096
characters) and use links to connect many chains in a
maze (up to max symlinks supported in path)
Victim must traverse C*N directories to resolve a name,
where C is the number of symlinks and N the depth of
each chain
In Linux, attack may be able to force traversal of 80,000
directories!
Increasing probability of success

The attacker polls the directory structure to find
out when the system updates access time on one
of the symbolic links in the maze


Experimental results: Success probability
between 19% to 100% on k=100.


At that point, it is safe to switch that symlink to point to the
system file resource.
Even randomizing when to make calls to access(2) or
open(2) results in reasonable attack success probabilities
K=100 already slower than fork-based solution
Conclusion

Needed: Standardization of UNIX kernel
capabilities to either:



support temporary privilege drop
support an O_RUID flag that makes system calls fail or
succeed based on the real ID of the process
Note: UNIX flavors often support better
mechanisms than access(2), and therefore this
call is in disuse

However, code that uses such infrastructures is not
portable
Download