Fabulous Adventures In Coding
Eric Lippert is a principal developer on the C# compiler team. Learn more about Eric.
All that talk a while back about error handling in VBScript got me thinking about some error handling security issues that involve VB.NET specifically, though they apply to managed code written in any language.
Consider this scenario: you provide a fully trusted library of useful functions which can be used by partially trusted callers. The partially trusted callers might be hostile -- that's why they're partially trusted, after all. You have a particular method which needs administration rights. When it's called, it prompts the user for the administrator password, impersonates the administrator, does work, and then reverts the impersonation.
Public Sub DoTheThing() ' Omitted -- prompt user to obtain password NewIdentity = GetWindowsIdentity(AdminName, Domain, Password) NewContext = NewIdentity.Impersonate() DoTheAdminThing() NewContext.Undo()End Sub
There's a potentially huge security hole here. What if DoTheAdminThing throws an exception? There's no Catch or Finally block around the Undo, so control can return to the partially trusted caller with the impersonation still intact! That seems bad! Let's stick a Finally on there:
NewIdentity = GetWindowsIdentity(UserName, Domain, Password) NewContext = NewIdentity.Impersonate() Try DoTheAdminThing() Finally NewContext.Undo() End Try
Clearly this an improvement from a code-quality perspective, but does it solve the security problem?
No! A Finally block guarantees that the code will run eventually, but it does not guarantee that no code will run between the point of the exception and the Finally block! VB .NET provides a syntax specifically for running code before the Finally block: the exception filter. Suppose our hostile code looked like this:
Public Sub IAmSoEvil() Try NiceObject.DoTheThing() Catch Ex As System.Exception When ExploitFlaw() DoSomethingElse() End TryEnd SubPublic Function ExploitFlaw() As Boolean DoSomethingEvil() ExploitFlaw = TrueEnd Function
Let's trace through what happens.
Of course, this doesn't apply to just thread impersonation. Any time that important global state can be made inconsistent requires very careful exception handling to ensure that no hostile code runs while the state is vulnerable. We can do that by catching the error before the hostile code can:
Dim Reverted As BooleanReverted = False NewContext = NewIdentity.Impersonate()Try DoTheAdminThing()Catch Ex As Exception NewContext.Undo() Reverted = True Throw ExFinally If Not Reverted Then NewContext.Undo() End IfEnd Try
Gross, but there's not much else you can do about it. Fortunately, these kinds of situations are pretty rare.
Pop quiz: Is this another example of the flaw pattern?
Does this do the same thing as before -- potentially pass control to a partially-trusted exception filter before the assertion is reverted? If so, how do you defend against it? If not, why not, and are there any other potential attacks here?
Tune in next time to find out! I'll be AFK for the Labour Day weekend, so we'll have the thrilling conclusion next week. Have a nice weekend, everyone.