Windows: Profile API CreateEnvBlock Local Information Disclosure
Platform: Windows 8.1 Update 32/64 bit (7 not tested)
Class: Information Disclosure
The exported function CreateEnvBlock from profapi.dll (which is used by the CreateEnvironmentBlock API function) has an information disclosure vulnerability when building the environment block for a user which can be locally exploited to disclose heap memory of a process calling the API. For example it's common when a privileged service spawns a process as another user to load the environment block, the information disclosure will be from the caller.
The bug occurs in the SetEnvFromRegistryKey method which has roughly the following code:
DWORD dwMaxValueLen;
RegQueryInfoKey(hKey, ..., &dwMaxValueLen, ...);
// Increment for guaranteed NUL
dwMaxValueLen += 2;
WCHAR* lpBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwMaxValueLen);
RegEnumValue(hKey, ..., lpBuf, &dwMaxValueLen);
RtlSetEnvironmentVariable(..., lpBuf, wcslen(lpBuf)...);
The code adds an additional 2 bytes to the maximum length, and ensures the entire heap block is zeroed so those two bytes should stay zero. There's a race condition between the original query for the maximum size and the point where RegEnumValue is called. As the call uses the size with the NUL character allowance and not the original size it's possible to change the biggest value during the process to fill the entire buffer and as registry strings do not need to be NUL terminated the subsequent read will continue after the value buffer until it hits a NUL wide character. By choosing appropriate sizes this will read the heap header then try and read part of the following heap block.
Exploiting the race reliably would seem to be difficult, fortunately the function provides us with a way of winning the race. If the function encounters an environment variable with the names TEMP or TMP it will pass the value to the API GetShortPathName. By building a suitable path and using an OPLOCK on part of the directory hierarchy we can get signaled when the GetShortPathName API is being called on the path.
One place this can be exploited locally is in scheduled tasks. When a scheduled task is executed the service calls CreateEnvBlock with the user's token to build the environment for the new process. By exploiting the race condition a new scheduled task process can be created with the permissions of the user, the leaked heap memory can then be extracted and inspected. This can be repeated indefinitely until useful memory is exposed. As the svchost process which contains the task scheduler has some interesting services it might be possible to disclose sensitive information. As it typically discloses heap header information it might be a useful technique for exploiting a heap overflow in the service if one could be found.
Additionally while not a security issue there's a bug in the operation of the CreateEnvBlock function in that it doesn't take the Session ID from the token when querying for the Volatile Environment key. Instead it uses the session ID of the process which leads to the incorrect result, although for the purposes of exploitation it makes it easier.
Provided is a PoC which exploits the issue on Windows 8.1 update. Due to the way in which the race condition is exploited this doesn't seem to work on 7 (seems you can't add an oplock to a directory on Windows 7 or at least my code is wrong). Therefore I've not checked it on Windows 7, but I believe the bug still exists based on analysis of the DLL. There are other ways of winning the race such as using the WebDav redirector to forward requests for GetShortPathName to a locally listening host. To reproduce execute the following steps:
1) Execute the file Poc_CreateEnvBlock_InfoDisclosure.exe
2) It should try and exploit the race and run a new copy of itself from the task scheduler printing any leaked memory.
It might require running a few times and for the system to have been used a little to ensure the heap of the task scheduler process has anything in it. The size of the value was chosen empirically, I've changed it to different sizes but most don't work.
Expected result:
The environment variable shouldn't contained additional data
Observed result:
The environment variable contains data leaked from the task scheduler's heap
This bug is subject to a 90 day disclosure deadline. If 90 days elapse
without a broadly available patch, then the bug report will automatically
become visible to the public.