I started ticking differently after taking on the Corelan Advanced Win32 Exploit Development training last month at BruCON 0x0B. I am very delighted and excited about that, and looking forward to putting the new experience to more practical use.
A NULL-pointer dereference
According to Wikipedia¹:
The program can potentially dereference a null pointer, thereby raising a NullPointerException. Null pointer errors are usually the result of one or more programmer assumptions being violated. Most null pointer issues result in general software reliability problems, but if an attacker can intentionally trigger a null pointer dereference, the attacker might be able to use the resulting exception to bypass security logic or to cause the application to reveal debugging information that will be valuable in planning subsequent attacks.
A null-pointer dereference takes place when a pointer with a value of NULL is used as though it pointed to a valid memory area.
Null-pointer dereferences, while common, can generally be found and corrected in a simple way. They will always result in the crash of the process, unless exception handling (on some platforms) is invoked, and even then, little can be done to salvage the process.
A vulnerable code
The following code was taken from an application that I am currently inspecting. The name of the application and references to the code will not be released for the time being. The code is very common in applications that want to enumerate available USB interfaces each time they change, due to a new USB device being plugged-in or unplugged.
dev_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); dev_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
Do you see it, my lovely little NULL-pointer dereference <3 ?
On line #1, memory of size required_size is being allocated and on a successful allocation, a pointer to the allocated memory chunk of type SP_DEVICE_INTERFACE_DETAIL_DATA_A is returned and stored in dev_detail_data.
On line #2, the size of the datatype SP_DEVICE_INTERFACE_DETAIL_DATA_A is calculated and stored at the corresponding address, that is referenced by the pointer we got from line #1.
But what would happen if the allocation fails, i.e. malloc() was unable to allocate the required_size? According to the documentation², it would then return a NULL pointer.
On success, a pointer to the memory block allocated by the function.
The type of this pointer is always
void*, which can be cast to the desired type of data pointer in order to be dereferenceable.
If the function failed to allocate the requested block of memory, a null pointer is returned.
That means, if we can force malloc() to fail, we would be able to force the application to assign a value to a dereferenced NULL-pointer on line #2 😈
In userland, this is hardly exploitable and highly dependant on the application’s code, however when running in kernel-mode, it’s a different story.
Exploiting this effectively requires running the vulnerable code in kernel-mode & being able to force malloc() to fail, by:
- being able to exhaust the free available memory, or
- being able to control the value of required_size to set it to an arbitrary large value.
Mitigating this is easy. It should not be taken for granted that malloc() would return a valid pointer to a memory chunk, therefore it is sufficient to compare the returned value of the pointer to NULL.
A NULL-pointer dereference occurs when an application uses a NULL-pointer unintentionally to read or write from/to a memory address referenced by the pointer. This happens when the pointer has been nullified or when it wasn’t assigned correctly in the first place.
|27.10.2019||The vulnerability has been reported to the Mooltipass/Moolticute team.|
|27.10.2019||Vulnerability confirmed. A public fix has also been pushed: https://github.com/mooltipass/moolticute/pull/483|
|30.10.2019||This issue has been assigned CVE-2019-18635|
|31.10.2019||This public disclosure|