Uninformed: Informative Information for the Uninformed

Vol 4» 2006.Jun

Gaining Control of the Unhandled Exception Filter

At this point, the only feasible vector for gaining control of the top-level UEF is to cause calls to be made to kernel32!SetUnhandledExceptionFilter. This is primarily due to the fact that the global variable has the current function pointer encoded. One could consider attempting to cause code to be redirected directly to kernel32!SetUnhandledExceptionFilter, but doing so would require some kind of otherwise-exploitable vulnerability in an application, thus making it not useful in the context of this document.

Given these restrictions, it makes sense to think a little bit more about the process involved in registering and deregistering UEFs. Since the chain of registered UEFs is implicit, it may be possible to cause that chain to become corrupt or invalid in some way that might be useful. One of the requirements that is known about the registration process for top-level UEFs is that the register and deregister operations must be symmetric. What happens if they aren't, though? Consider the diagram in figure [*], where Fx and Gx are registered and deregistered, but in asymmetric order.

Figure: Asymmetric register and deregister of UEFs
Image asymdiag

As shown in the diagram in figure [*], Fx and Gx are registered first. Following that, Fx is deregistered prior to deregistering Gx, thus making the operation asymmetrical. As a result of Fx deregistering first, the top-level UEF is set to Nx, even though Gx should technically still be a part of the chain. Finally, Gx deregisters, setting the top-level UEF to Fx even though Fx had been previously deregistered. This is obviously incorrect behavior, but the code associated with Gx has no idea that Fx has been deregistered due to the implicit chain that is created.

If asymmetric registration of UEFs can be made to occur, it might be possible for an attacker to gain control of the top-level UEF. Consider for a moment that the register and deregister operations in the diagram in figure [*] occur during DLL load and unload, respectively. If that is the case, then after deregistration occurs, the DLLs associated with the UEFs will be unloaded. This will leave the top-level UEF set to Fx which now points to an invalid region of memory. If an exception occurs after this point and is not handled by a registered exception handler, the unhandled exception filter will be called. If a debugger is not attached, the top-level UEF Fx will be called. Since Fx points to memory that is no longer associated with the DLL that contained Fx, the process will terminate -- or worse.

From a security prospective, the act of leaving a dangling function pointer that now points to unallocated memory can be a dream come true. If a scenario such as this occurs, an attacker can attempt to consume enough memory that will allow them to store arbitrary code at the location that the function originally resided. In the event that the function is called, the attacker's arbitrary code will be executed rather than the code that was was originally at that location. In the case of the top-level UEF, the only thing that an attacker would need to do in order to cause the function pointer to be called is to generate an unhandled exception, such as a NULL pointer dereference.

All of these details combine to provide a feasible vector for executing arbitrary code. First, it's necessary to be able to cause at least two DLLs that set UEFs to be deregistered asymmetrically, thus leaving the top-level UEF pointing to invalid memory. Second, it's necessary to consume enough memory that attacker controlled code can reside at the location that one of the UEF functions originally resided. Finally, an exception must be generated that causes the top-level UEF to be called, thus executing the attacker's arbitrary code.

The big question, though, is how feasible is it to really be able to control the registering and deregistering of UEFs? To answer that, chapter [*] provides a case study on one such application where it's all too possible: Internet Explorer.