Double Free in Aspnet
How Double Free Manifests in Aspnet
Double free vulnerabilities in Aspnet typically occur in memory management scenarios where an object is deallocated twice, leading to heap corruption. In Aspnet applications, this often manifests in several specific patterns.
One common manifestation occurs in custom HTTP module implementations. When developers manually manage resources in IHttpModule implementations, improper cleanup can lead to double free conditions:
public class VulnerableModule : IHttpModule
{
private IntPtr _nativeResource;
public void Init(HttpApplication context)
{
_nativeResource = AllocateNativeResource();
context.EndRequest += (sender, e) => CleanupResource();
}
private void CleanupResource()
{
// Vulnerable: Cleanup called multiple times if module is disposed and event fires
FreeNativeResource(_nativeResource);
_nativeResource = IntPtr.Zero;
}
}
Another Aspnet-specific scenario involves improper session state management. When custom session providers fail to handle cleanup correctly, double free can occur:
public class VulnerableSessionProvider : SessionStateStoreProviderBase
{
public override void RemoveItem(HttpContext context, string id, SessionStateStoreData item, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateItemExpireCallback expireCallback)
{
// Vulnerable: item is disposed here
item.Dispose();
// And potentially disposed again by Aspnet's internal cleanup
base.RemoveItem(context, id, item, out locked, out lockAge, out lockId, out expireCallback);
}
}
Custom output caching implementations also present double free opportunities. When developers implement their own cache providers without proper reference counting, objects may be freed multiple times:
public class VulnerableOutputCacheProvider : OutputCacheProvider
{
private Dictionary _cache = new Dictionary();
public override object Get(string key)
{
if (_cache.TryGetValue(key, out var item))
{
// Vulnerable: returning raw object without proper lifetime management
return item.Data;
}
return null;
}
public override void Remove(string key)
{
if (_cache.TryGetValue(key, out var item))
{
// First free
item.Data.Dispose();
// Second free when base.Remove is called
base.Remove(key);
}
}
}
SignalR applications in Aspnet present another vector. Custom Hub implementations that manage unmanaged resources without proper disposal patterns can lead to double free:
public class VulnerableHub : Hub
{
private IntPtr _buffer;
public override Task OnConnectedAsync()
{
_buffer = AllocateBuffer();
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
// Vulnerable: cleanup called in multiple paths
FreeBuffer(_buffer);
_buffer = IntPtr.Zero;
return base.OnDisconnectedAsync(exception);
}
public void ManualDisconnect()
{
// This could be called independently, causing double free
OnDisconnectedAsync(null);
}
}
Aspnet-Specific Detection
Detecting double free vulnerabilities in Aspnet applications requires both static analysis and runtime monitoring. Here are Aspnet-specific detection strategies:
Memory Profiler Integration
Aspnet's integration with .NET memory profilers allows for sophisticated double free detection. Using tools like dotnet-trace or Visual Studio's Diagnostic Tools, you can monitor allocation patterns:
// Using dotnet-trace to monitor Aspnet memory
dotnet-trace collect --providers Microsoft-AspNet --providers Microsoft-Windows-DotNETRuntime
Custom Diagnostic Middleware
Implement Aspnet middleware to track object lifecycles and detect potential double free patterns:
public class DoubleFreeDetectionMiddleware
{
private readonly RequestDelegate _next;
private readonly ConcurrentDictionary<IntPtr, int> _allocationTracker = new();
public DoubleFreeDetectionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Track allocations
var allocations = TrackAllocations();
try
{
await _next(context);
}
finally
{
// Check for double frees
CheckForDoubleFrees(allocations);
}
}
private List<IntPtr> TrackAllocations()
{
// Implementation would track allocations during request
return new List<IntPtr>();
}
private void CheckForDoubleFrees(List<IntPtr> allocations)
{
foreach (var ptr in allocations)
{
if (_allocationTracker.TryGetValue(ptr, out var count) && count > 1)
{
// Potential double free detected
LogWarning($"Potential double free at {ptr}");
}
}
}
}
middleBrick Scanning
middleBrick's black-box scanning approach is particularly effective for detecting double free vulnerabilities in Aspnet applications. The scanner tests the runtime behavior of your API endpoints, looking for memory corruption patterns and improper resource cleanup.
To scan your Aspnet API with middleBrick:
npm install -g middlebrick
middlebrick scan https://yourapi.com/api/endpoint
The scanner will test for memory management issues across your Aspnet application's attack surface, including:
- Authentication bypass attempts that might trigger double free in session management
- Input validation failures that could lead to buffer overflows and subsequent double free
- Rate limiting bypasses that might cause resource exhaustion and improper cleanup
OWASP ZAP Integration
Integrate OWASP ZAP with your Aspnet application for runtime double free detection:
zap.sh -daemon -port 8080 -host 127.0.0.1 -config api.key=changeme
Configure your Aspnet application to work with ZAP's passive scanning to detect memory corruption patterns.
Aspnet-Specific Remediation
Remediating double free vulnerabilities in Aspnet requires a combination of proper resource management patterns and Aspnet-specific best practices.
Safe Resource Management with IDisposable
Implement the IDisposable pattern correctly in Aspnet components:
public class SafeResource : IDisposable
{
private IntPtr _nativeResource;
private bool _disposed = false;
public SafeResource()
{
_nativeResource = AllocateNativeResource();
}
public void DoWork()
{
if (_disposed)
throw new ObjectDisposedException(nameof(SafeResource));
// Work with resource
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Free managed objects
}
// Free native resources
if (_nativeResource != IntPtr.Zero)
{
FreeNativeResource(_nativeResource);
_nativeResource = IntPtr.Zero;
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~SafeResource()
{
Dispose(false);
}
}
Aspnet Module Cleanup Best Practices
Implement IHttpModule with proper cleanup patterns:
public class SafeModule : IHttpModule
{
private IntPtr _nativeResource;
private bool _cleanedUp = false;
public void Init(HttpApplication context)
{
_nativeResource = AllocateNativeResource();
context.EndRequest += OnEndRequest;
context.Disposed += OnApplicationDisposed;
}
private void OnEndRequest(object sender, EventArgs e)
{
Cleanup();
}
private void OnApplicationDisposed(object sender, EventArgs e)
{
Cleanup();
}
private void Cleanup()
{
if (!_cleanedUp)
{
FreeNativeResource(_nativeResource);
_nativeResource = IntPtr.Zero;
_cleanedUp = true;
}
}
public void Dispose()
{
Cleanup();
}
}
Session State Provider Security
Implement secure session state providers with proper cleanup:
public class SecureSessionProvider : SessionStateStoreProviderBase
{
public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
{
// Create session item
var item = new SessionStateStoreData(new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), timeout);
// Store item safely without risk of double free
StoreItem(id, item);
}
public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
{
// Proper cleanup without double free
if (TryGetItem(id, out var item))
{
// Only dispose if not already disposed
if (item is IDisposable disposable && !disposableDisposed)
{
disposable.Dispose();
}
}
}
}
Middleware-Based Resource Tracking
Use Aspnet middleware to track and manage resources safely:
public class ResourceTrackingMiddleware
{
private readonly RequestDelegate _next;
private static ConcurrentDictionary<string, ResourceTracker> _trackers =
new ConcurrentDictionary<string, ResourceTracker>();
public ResourceTrackingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var tracker = new ResourceTracker();
var correlationId = context.TraceIdentifier;
_trackers[correlationId] = tracker;
try
{
await _next(context);
}
finally
{
// Single cleanup point
tracker.Cleanup();
_trackers.TryRemove(correlationId, out _);
}
}
}
public class ResourceTracker
{
private List<IDisposable> _resources = new List<IDisposable>();
public void RegisterResource(IDisposable resource)
{
_resources.Add(resource);
}
public void Cleanup()
{
foreach (var resource in _resources)
{
try
{
resource.Dispose();
}
catch
{
// Log but continue cleanup
}
}
_resources.Clear();
}
}