I had a cool idea for a way to get the user to passively elevate your application without socially engineering them to do so or requiring exploits. Obviously you could just go ahead and start mass infecting executables, but that would cause a lot of unforeseen problems and would also mean digitally signed applications from trusted providers would now appear as untrusted files. A good alternative would be hijacking a single dll. I won’t call this a “UAC Bypass” as it still requires the user to elevate an application (just not yours).
This is something most people should already know, but I’ll go ahead and clarify for anyone that doesn’t. When an application calls LoadLibrary on a dll but doesn’t supply the full path to the file: The system will first check the KnownDlls registry key for the path, if it’s not found there, then the system will the look in the directory the application was executed from, before finally looking in system paths such as system32/syswow64.
If you were to write a dll to the same path as an application and give it the same name as a commonly loaded system dll, it would likely be loaded by the application instead of the real thing; However, the dll must meet the following criteria.
- The application must load the dll by its name and not the full path (this is common).
- The dll must not exist in HKLMSYSTEMControlSession ManagerKnownDLLs.
- The dll must match the process architecture (64-bit processes will quietly skip 32-bit dlls and vice versa).
- The dll should exist in system32 or syswow64, special paths don’t appear to work.
ZeroAccess abused this method to “social engineer” the user into elevating the file. This was done by downloading the Adobe Flash installer from the official site, writing the bot’s dll to the same path as the installer, then running it. When the installer was executed, the user account control popup would state that the application was from a verified publisher “Adobe Systems Incorporated” and the user would probably allow it to elevate (resulting in the elevated installer loading the bot’s malicious dll).
|Is it a real flash update? Is it just ZeroAccess? Nobody knows.
A Less Invasive Method
What if there was a folder where 90% of the applications that require UAC elevation reside and what if it was writable from a non-elevated process? Well it turns out that folder exists: say hello to %userprofile%Downloads. You can probably see where I’m going with this.
Although I wasn’t expecting to find a dll that is loaded by most applications and meets all the criteria for a hijackable dll, after about 5 minutes of searching I found the motherload: dwmapi.dll. Not only does this dll meet all the criteria, but it appears to be loaded by all setup files… So let’s make a hello world dll, name it dwmapi.dll, drop it to the downloads folder, and run a setup file.
Success! The only problem here is that as soon as we start the setup it’ll crash because we’ve replaced an important dll, however this is a fairly easy fix: dll infection.
Writing a DLL Infector
My first idea was to simply add a new section header, change the NumberOfSections field in the PE header, then just append my section on to the end of the PE file. As it happens, directly after the last section header is the bound imports directory, which would be overwritten by our new section header. So after about 2 hours of writing an application to rebuild the entire PE from scratch, someone reminded me that the bound imports directory is just there to speed up the loading of imports and can simply be overwritten then disabled in the PE header.
Following 15 minutes of holding CTRL + Z, I’m back to where I started and feeling a bit silly. An additional 2 lines of code has my infector working perfectly and we’re ready to move on to the next step. The current infector simply disable and overwrite the bound imports directory with the new section header, append the new section to the end of the PE file, adjusts the SizeOfImage to accommodate the new section, then changes the AddressOfEntryPoint to point to our new section.
All we need now is some code for the section.
The obvious choice was the make the new section execute shellcode so we don’t have to worry about relocations or imports. The actual code is pretty simple and written using some handy FASM macros, I’ll quickly run over how it works.
- Checks the stack to make sure that dwmapi.dll was called with DLL_PROCESS_ATTACH
- Navigates the PEB Ldr structure to get the base address of Kernel32 and Ntdll.
- Usess a simple GetProcAddress implementation to import the following functions: NtOpenProcessToken, NtQueryInformationToken, NtClose, ExpandEnvironmentStringsA, CreateProcessA.
- Opens the current process token and queries it to confirm the application we are running from is UAC elevated.
- Gets the path of cmd.exe then executes it (UAC elevated of course).
- Passes execution back to the real dwmapi.dll entry point so execution can continue.
Putting It All Together
The final product infects dwmapi.dll with our shellcode and places it in the download folder, once the user downloads and runs a setup that requires UAC elevation, our elevated command prompt will be spawned ( Because of Wow64FsRedirect and the fact that most setups run under wow64, we can use the same code on 32-bit and 64-bit windows).