Securing Exception Handling
In Visual C++ and Visual Basic, a filter expression further up the stack runs before any finally statement. The catch block associated with that filter runs after the finally statement. For more information, see Using User-Filtered Exceptions. This section examines the security implications of this order. Consider the following pseudocode example that illustrates the order in which filter statements and finally statements run.
void Main()
{
try
{
Sub();
}
except (Filter())
{
Console.WriteLine("catch");
}
}
bool Filter () {
Console.WriteLine("filter");
return true;
}
void Sub()
{
try
{
Console.WriteLine("throw");
throw new Exception();
}
finally
{
Console.WriteLine("finally");
}
}
This code prints the following.
Throw
Filter
Finally
Catch
The filter runs before the finally statement, so security issues can be introduced by anything that makes a state change where execution of other code could take advantage. For example:
try
{
Alter_Security_State();
// This means changing anything (state variables,
// switching unmanaged context, impersonation, and
// so on) that could be exploited if malicious
// code ran before state is restored.
Do_some_work();
}
finally
{
Restore_Security_State();
// This simply restores the state change above.
}
This pseudocode allows a filter higher up the stack to run arbitrary code. Other examples of operations that would have a similar effect are temporary impersonation of another identity, setting an internal flag that bypasses some security check, or changing the culture associated with the thread. The recommended solution is to introduce an exception handler to isolate the code's changes to thread state from callers' filter blocks. However, it is important that the exception handler be properly introduced or this problem will not be fixed. The following example switches the UI culture, but any kind of thread state change could be similarly exposed.
YourObject.YourMethod()
{
CultureInfo saveCulture = Thread.CurrentThread.CurrentUICulture;
try {
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");
// Do something that throws an exception.
}
finally {
Thread.CurrentThread.CurrentUICulture = saveCulture;
}
}
Public Class UserCode
Public Shared Sub Main()
Try
Dim obj As YourObject = new YourObject
obj.YourMethod()
Catch e As Exception When FilterFunc
Console.WriteLine("An error occurred: '{0}'", e)
Console.WriteLine("Current Culture: {0}",
Thread.CurrentThread.CurrentUICulture)
End Try
End Sub
Public Function FilterFunc As Boolean
Console.WriteLine("Current Culture: {0}", Thread.CurrentThread.CurrentUICulture)
Return True
End Sub
End Class
The correct fix in this case is to wrap the existing try/finally block in a try/catch block. Simply introducing a catch-throw clause into the existing try/finally block does not fix the problem, as shown in the following example.
YourObject.YourMethod()
{
CultureInfo saveCulture = Thread.CurrentThread.CurrentUICulture;
try
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");
// Do something that throws an exception.
}
catch { throw; }
finally
{
Thread.CurrentThread.CurrentUICulture = saveCulture;
}
}
This does not fix the problem because the finally statement has not run before the FilterFunc
gets control.
The following example fixes the problem by ensuring that the finally clause has executed before offering an exception up the callers' exception filter blocks.
YourObject.YourMethod()
{
CultureInfo saveCulture = Thread.CurrentThread.CurrentUICulture;
try
{
try
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");
// Do something that throws an exception.
}
finally
{
Thread.CurrentThread.CurrentUICulture = saveCulture;
}
}
catch { throw; }
}