New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom Garbage Collector Path Traversal #38078
Comments
I couldn't figure out the best area label to add to this issue. Please help me learn by adding exactly one area label. |
Thank you for the report. Per MSRC, we do not consider this to be a security vulnerability. Exploiting this would require the adversary to modify the environment block, at which point they're already in control over other aspects of the application's execution. Raymond Chen has also written about this and has some interesting stories to share: see https://www.bing.com/search?q=oldnewthing+%22It+rather+involved+being+on+the+other+side+of+this+airtight+hatchway%22 for some examples. |
There's also a very straightforward and documented way to achieve the same thing if you control the environment block - no need to jump through path traversal hoops: https://medium.com/criteo-labs/c-have-some-fun-with-net-core-startup-hooks-498b9ad001e1 |
Abstract
Thursday 19Th March 2020, I reported to Microsoft Security Response Center (MSRC) a path traversal bug that can be exploited in order to load a custom garbage collector (GC) into any .NET Core application with a low privileged account. This can be abused to archive application whitelisting bypass and arbitrary code execution from within a legitimate .NET Core application.
After almost 3 months, MSRC still did not deem this as a security issue; hence why I am open this issue today. I consider this finding as a small security design flaw and I strongly believe that this is a legitimate attack vector.
Description
A developer or administrator can set a custom GC, in the form of a DLL, via the
COMPLUS_GCName
configuration knob.Since .NET Core 3.0, the configuration knob is supposed to be the name of the GC that will be probed under the .NET Core installation directly (e.g.
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\<version folder>\
). On windows, this directory is writable solely by local administrators.The
COMPLUS_GCName
configuration knob value is not sanitised before being passed to the GCHeapUtilities::LoadAndInitialize function:On Windows, the non-sanitised value is then going through all these functions:
LoadStandaloneGc
->CLRLoadLibrary
->CLRLoadLibraryEx
->WszLoadLibraryEx
->LoadLibraryExWrapper
. Ultimately,LoadLibraryExW
Windows API function is called to map into the virtual private memory of the process the DLL.CLRLoadLibrary
being called from the LoadStandaloneGc function:Due to the lack of sanitisation or check, there is a path traversal bug. Local administrator privileges are therefore no longer needed because the GC can be loaded from any location on the system. As a result, arbitrary unmanaged code can be executed through any .NET Core application with a low privilege account.
Exploitation Example
The first function being invoked from the loaded GC is
GC_VersionInfo
, from the LoadAndInitializeGC function.This mean that only one function has to be exported to execute arbitrary unmanaged code. The DLL entry point (i.e.
DllMain
) may also work, but there is limitations related to the Windows loader. The following C++ code can be compiled into a DLL and will display aasMessageBox
, once loaded by a .NET Core application. This is a PoC, an attacker will be more interested by injecting shellcode instead of displaying aMessageBox
.Example of execution once the
COMPLUS_GCName
configuration knob was set to a location writable by any low privileged user.Configuration
This has been tested with .NET Core 5.0 and .NET Core 3.1.4 in a Windows x64 10 2004 operating system. Additional tests and a review of the code shows that MacOS and Linux operating systems are also affected.
The text was updated successfully, but these errors were encountered: