Heap corruption in HttpCacheModule while you try to remove HTTP headers in your custom HTTP module.

Some users want to remove certain HTTP headers in IIS 7.0/7.5 to avoid divulging platform information through response headers. They usually implement a custom HTTP module and remove the headers in PreSendRequestHeaders notification.

 

       

         public void Init(HttpApplication app)
{
app.PreSendRequestHeaders += new EventHandler(this.PreSendRequestHeaders);
}
private void PreSendRequestHeaders(object sender, EventArgs e)
{
HttpContext.Current.Response.Headers.Remove("Server");
HttpContext.Current.Response.Headers.Remove("X-AspNet-Version");
HttpContext.Current.Response.Headers.Remove("X-Powered-By");
}

Unfortunately heap corruption will occur if you enable HttpCacheModule in IIS 7.0/7.5. The crash call stack is as follows:

 

Child-SP RetAddr Call Site
00000000`01dee2e8 00000000`76dd55e7 ntdll!DbgBreakPoint
00000000`01dee2f0 00000000`76dd562a ntdll!RtlReportException+0x47
00000000`01dee320 00000000`76dd6ee5 ntdll!RtlpTerminateFailureFilter+0x1a
00000000`01dee350 00000000`76d6ecad ntdll!RtlReportCriticalFailure$filt$0+0x16
00000000`01dee380 00000000`76d6e13d ntdll!__C_specific_handler+0x8c
00000000`01dee3f0 00000000`76d6ea57 ntdll!RtlpExecuteHandlerForException+0xd
00000000`01dee420 00000000`76d759f8 ntdll!RtlDispatchException+0x20c
00000000`01deeac0 00000000`76dd6e97 ntdll!KiUserExceptionDispatch+0x2e
00000000`01def060 00000000`76dd79d6 ntdll!RtlReportCriticalFailure+0x67
00000000`01def130 00000000`76dd9136 ntdll!RtlpReportHeapFailure+0x26
00000000`01def160 00000000`76ddaa44 ntdll!RtlpHeapHandleError+0x16
00000000`01def190 00000000`76dad84f ntdll!RtlpLogHeapFailure+0xa4
00000000`01def1c0 00000000`76c2c7da ntdll!RtlFreeHeap+0x35fbf
00000000`01def240 000007fe`f5ae3714 kernel32!HeapFree+0xa
00000000`01def270 000007fe`f5ae3f7e cachhttp!RESPONSE_ENTRY::~RESPONSE_ENTRY+0xe4
00000000`01def2a0 000007fe`f5ae3fba cachhttp!RESPONSE_ENTRY::`scalar deleting destructor'+0xe
00000000`01def2d0 000007fe`f5ae52d6 cachhttp!RESPONSE_ENTRY::DereferenceResponseEntry+0x1a
00000000`01def300 000007fe`f5ae765e cachhttp!OUTPUT_ENTRY::ClearIdentityResponseEntry+0x26
00000000`01def330 000007fe`f5ae76be cachhttp!OUTPUT_ENTRY::~OUTPUT_ENTRY+0xe
00000000`01def360 000007fe`f5ae76f9 cachhttp!OUTPUT_ENTRY::`scalar deleting destructor'+0xe
00000000`01def390 000007fe`f5ae7818 cachhttp!OUTPUT_ENTRY::DereferenceOutputEntry+0x19
00000000`01def3c0 000007fe`faf162ec cachhttp!OUTPUT_CACHE::AddRefRecord+0x18
00000000`01def3f0 000007fe`faf1b2bb iisutil!CLKRLinearHashTable::_DeleteNode+0x3c
00000000`01def420 000007fe`faf15830 iisutil!CLKRLinearHashTable::_DeleteIf+0x59db
00000000`01def4a0 000007fe`f5ae434f iisutil!CLKRHashTable::DeleteIf+0xd0
00000000`01def4f0 000007fe`f5ae27ac cachhttp!CTypedHashTable<UL_RESPONSE_CACHE,UL_RESPONSE_CACHE_ENTRY,UL_RESPONSE_CACHE_KEY * __ptr64,CLKRHashTable>::DeleteIf+0x2f
00000000`01def540 00000000`76d42233 cachhttp!OUTPUT_CACHE::ScavengerCallback+0x9cc
00000000`01def5b0 00000000`76d420f3 ntdll!RtlpTpTimerCallback+0xc3
00000000`01def5f0 00000000`76d426d3 ntdll!TppTimerpExecuteCallback+0x103
00000000`01def650 00000000`76c2466d ntdll!TppWorkerThread+0x560
00000000`01def8d0 00000000`76d58791 kernel32!BaseThreadInitThunk+0xd
00000000`01def900 00000000`00000000 ntdll!RtlUserThreadStart+0x1d

 

The reason why this problem occurs is because the HttpCacheModule and IIS pipleline assume that the response will not be modified once it is taken from the output cache.

The problem can be avoided if the HTTP headers are removed on any event before UpdateRequestCache event.

(Refer to https://learn.iis.net/page.aspx/243/aspnet-integration-with-iis-7/  for more details on request pipeline)

For example, we can remove HTTP headers in PostReleaseRequestState event:

 

        public void Init(HttpApplication app)
{
app.PostReleaseRequestState += new EventHandler(app_PostReleaseRequestState);

}
void app_PostReleaseRequestState(object sender, EventArgs e)
{
HttpContext.Current.Response.Headers.Remove("Server");
HttpContext.Current.Response.Headers.Remove("X-AspNet-Version");
HttpContext.Current.Response.Headers.Remove("X-Powered-By");
}

 

Regards,

XinJin