|Informative Information for the Uninformed|
The one basic requirement of any solution attempting to prevent the leveraging of SEH overwrites is that it must not be possible for an attacker to be able to supply a value for the Handler attribute of an exception registration record that is subsequently used in an unchecked fashion by the exception dispatcher when an exception occurs. If a solution can claim to have satisfied this requirement, then it should be true that the solution is secure.
To that point, Microsoft's solution is secure, but only if all of the images loaded in the address space have been compiled with /SAFESEH. Even then, it's possible that it may not be completely secure3.1. If there are any images that have not been compiled with /SAFESEH, it may be possible for an attacker to overwrite the Handler with an address of an instruction that resides within an unprotected image. The reason Microsoft's implementation cannot protect against this is because SafeSEH works by having the exception dispatcher validate handlers against a table of image-specific safe exception handlers prior to calling an exception handler. Safe exception handlers are stored in a table that is contained in any executable compiled with /SAFESEH. Given this limitation, it can also be said that Microsoft's implementation is not secure given the appropriate conditions. In fact, for third-party applications, and even some Microsoft-provided applications, these conditions are considered by the author to be the norm rather than the exception. In the end, it all boils down to the fact that Microsoft's solution is a compile-time solution rather than a runtime solution. With these limitations in mind, it makes sense to attempt to approach the problem from the angle of a runtime solution rather than a compile-time solution.
When it comes to designing a runtime solution, the important consideration that has to be made is that it will be necessary to intercept exceptions before they are passed off to the registered exception handlers by the exception dispatcher. The particulars of how this can be accomplished will be discussed in chapter . Assuming a solution is found to the layering problem, the next step is to come up with a solution for determining whether or not an exception handler is valid and has not been tampered with. While there are many inefficient solutions to this problem, such as coming up with a solution to keep a ``secure'' list of registered exception handlers, there is one solution in particular that the author feels is bested suited for the problem.
One of the side effects of an SEH overwrite is that the attacker will typically clobber the value of the Next attribute associated with the exception registration record that is overwritten. This occurs because the Next attribute precedes the Handler attribute in memory, and therefore must be overwritten before the Handler in the case of a typical buffer overflow. This has a very important side effect that is the key to facilitating the implementation of a runtime solution. In particular, the clobbering of the Next attribute means that all subsequent exception registration records would not be reachable by the exception dispatcher when walking the chain.
Consider for the moment a solution that, during thread startup, places a custom exception registration record as the very last exception registration record in the chain. This exception registration record will be symbolically referred to as the validation frame henceforth. From that point forward, whenever an exception is about to be dispatched, the solution could walk the chain prior to allowing the exception dispatcher to handle the exception. The purpose of walking the chain before hand is to ensure that the validation frame can be reached. As such, the validation frame's purpose is similar to that of stack canaries. If the validation frame can be reached, then that is evidence of the fact that the chain of exception handlers has not been corrupted. As described above, the act of overwriting the Handler attribute also requires that the Next pointer be overwritten. If the Next pointer is not overwritten with an address that ensures the integrity of the exception handler chain, then this solution can immediately detect that the integrity of the chain is in question and prevent the exception dispatcher from calling the overwritten Handler. Figure illustrates how this might look at execution time.
Using this technique, the act of ensuring that the integrity of the exception handler chain is kept intact results in the ability to prevent SEH overwrites. The important questions to ask at this point center around what limitations this solution might have. The most obvious question to ask is what's to stop an attacker from simply overwriting the Next pointer with the value that was already there. There are a few things that stop this. First of all, it will be common that the attacker does not know the value of the Next pointer. Second, and perhaps most important, is that one of the benefits of using an SEH overwrite is that an attacker can make use of a pop/pop/ret sequence. By forcing an attacker to retain the value of the Next pointer, the major benefit of using an SEH overwrite in the first place is gone. Even conceding this point, an attacker who is able to retain the value of the Next pointer would find themselves limited to overwriting the Handler with the address of instructions that indirectly transfer control back to their code. However, the attacker won't simply be able to use an instruction like jmp esp because the Handler will be called in the context of the exception dispatcher. It's at this point that diminishing returns are reached and an attacker is better off simply overwriting the return address, if possible.
Another important question to ask is what's to stop the attacker from overwriting the Next pointer with the address of the validation frame itself or, more easily, with 0xffffffff. The answer to this is much the same as described in the above paragraph. Specifically, by forcing an attacker away from the pop/pop/ret sequence, the usefulness of the SEH overwrite vector quickly degrades to the point of it being better to simply overwrite the return address, if possible. However, in order to be sure, the author feels that implementations of this solution would be wise to randomize the location of the validation frame.
It is the author's opinion that the solution described above satisfies the requirement outlined in the beginning of this chapter and therefore qualifies as a secure solution. However, there's always a chance that something has been missed. For that reason, the author is more than happy to be proven wrong on this point.