Tricking Rapid Fail Protection

If an attacker or a piece of poorly written code can crash an IIS worker process you might face a Denial-of-Service condition. IIS would spawn up a new worker process which crashes again as soon as it executes the vulnerable code. Spawning up processes is a pretty expensive operation on Windows and constantly trying to start new worker processes might bring your web server to a halt. And that's why the Rapid Fail Protection feature was introduced in IIS6.

The default RFP setting is that a worker process can't crash more than five times within 5 minutes (settings are configurable). Is this limit reached the Application Pool will be stopped and clients get '503 Service unavailable' errors. This way Rapid Fail Protection protects the machine and other Application Pools from vulnerable applications.

 

Now there might be instances where you know that you have an app that crashes, but you can't find the error or you don't have the source code. If RFP is enabled the administrator has to manually go to the machine and reenable the Application Pool every time RFP kicks in.

 

To avoid that I wrote this little script that restarts the failed Application Pool after a configurable timeout. It uses WMI to listen to the RFP eventlog entry. When the script gets notified about an RFP event it waits for 30 seconds and restarts the Application Pool. To make this script asynchronous you can simple move the Sleep and the restart into a different script and call it from within the loop. Here you go:

 

Timeout= 30000
set events = GetObject("winmgmts:{impersonationLevel=impersonate}").ExecNotificationQuery("select * from __instancecreationevent where targetinstance isa 'Win32_NTLogEvent' and TargetInstance.LogFile='System' and TargetInstance.EventCode=5002")
Do
    WScript.Echo "==========================================================================="
    WScript.Echo "Listening for IIS Rapid Fail Protection Events"
    Set objLatestEvent = events.NextEvent
    Wscript.Echo objLatestEvent.TargetInstance.Message
    ' get the AppPool name from the Eventlog message

    appPool = objLatestEvent.TargetInstance.InsertionStrings(0)
    WScript.Echo "Restarting Application Pool '" & appPool & "' in " & Timeout & " milliseconds"
    WScript.Sleep(Timeout)

    'construct ADSI path to failed AppPool and start by setting AppPoolCommand to 1
    set pool = GetObject("IIS://localhost/w3svc/AppPools/" & appPool)
    pool.AppPoolCommand = 1
    pool.SetInfo
    WScript.Echo "AppPool " & appPool & " restarted"
    WScript.Echo "==========================================================================="
    WScript.Echo
Loop

 

No Comments