SSD Advisory – FreeBSD Use After Free due to Race Condition

Vulnerability Summary
In FreeBSD there is a cryptographic device module called cryptodev which is accessible by any user on the system. Due to an absence of a locking mechanism, an attacker is able to create a race condition in the device mechanism and trigger a Use After Free vulnerability. If performed correctly, an attacker is able to use this vulnerability to gain control of the kernel and gain access to the attacked machine.

Credit
An independent Security Researcher, Avi S., has reported this vulnerability to SSD Secure Disclosure program.

Affected Systems
FreeBSD 4.8

Vulnerability Details
Since FreeBSD 4.8, an in-tree cryptographic device module was included called cryptodev, found in the source tree under sys/opencrypto/cryptodev.c. This module creates a device /dev/crypto, which has permissions 666, making it globally accessible to any user.
Interaction with this driver occurs by calling the CRIOGET ioctl on the device. This allows users to create an instance of a cryptof device, which represents an instance of a device for a user, which is given back to the user as a file descriptor.
The resulting file descriptor can be used in subsequent calls, which are then handled by cryptof_ioctl. This ioctl handles session establishment between the hardware accelerators and the user, acting as a hardware abstraction layer (HAL) for the supported devices.

Bug
The bug itself has to do with the locking, or lack there of, in the ioctl handler for cryptof_ioctl, and similarly, cryptof_close. While locking exists in a few select portions of the code base, in general, most operations will occur unlocked.
This becomes an issue particularly around the session end, where operations are releasing memory. Racing a close() operation on a syscall with partically any other operation in the ioctl can give you the ability to trigger a use-after-free vulnerability.

Exploit
The proof-of-concept exploit targets a race between cryptof_close, and the ioctl CIOCFSESSION. If the race wins then cryptof_ioctl should call csedelete on a released struct csession, or, cryptof_close will attempt to TAILQ_REMOVE a released struct csession from its linked list. There is also a spraying thread, which sprays fake struct csessions using the syscall mac_set_fd, which will create a heap allocation and copyin user supplied data, then subsequently error out due to invalid data being supplied and release it.
With all 3 threads going at the same time (in practice more are used to guarantee success), this allows us to get an invalid TAILQ_REMOVE, which will be used to overwrite the null_cdevsw.d_ioctl. A few threads will be spawned which will try to trigger this ioctl indefinitely to gain control of the instruction pointer.
However, due to the fact that the TAILQ_REMOVE procedure will also attempt to write to the value which is used for the overwrite, this race usually fails and instead we get an error attempting to write to the address. This however is just used for demonstration of the bug, and in practice this demo value could be replaced with a more useful pointer that could be written to.
You can find the exploit on our Github repository:
SSD FreeBSD Advisory Github Repository

Additional Notes
This race can be used to attack other commands in the ioctl, and while we briefly explored the possibility of attacking those commands, the double release case seemed like the path of least friction.
There is an additional bug in cryptodev which will create a massive allocation: this is the fact that mackeylen in struct session_op is a signed integer, so if set to 0xFFFFFFFF it will create a massive allocation, which additionally gets sign extended up to 64 bits. This triggers a bug in the large allocation function in the kernel malloc function, where size = roundup(size, PAGE_SIZE); which occurs in the large memory allocator causes it to overflow the size of the allocation.
Unfortunately this is most likely unexploitable (atleast from this vector), and instead it just causes a kernel panic on a dereference to an address that can’t be allocated.
Also note, there is a provided fake_cryptodev.h, which can be used for linting on a non-FreeBSD platform. You can use a flag passed into the compiler in build.sh to switch between the real and fake cryptodev.h files.

Leave a Reply

Your email address will not be published. Required fields are marked *