New issue
Advanced search Search tips
Starred by 3 users
Status: Fixed
Owner:
Closed: Mar 2015
Cc:



Sign in to add a comment
NVidia Windows Display Driver: Admin Impersonation Check Bypass
Project Member Reported by forshaw@google.com, Dec 8 2014 Back to list
NVidia Windows Display Driver: Admin Impersonation Check Bypass
Platform: Windows 7 32/64 bit
Class: Security Bypass

The Windows display driver (nvlddmkm.sys, version 9.8.13.4416 although earlier versions have been inspected and found to contain the dangerous code pattern) has a potential security issue which might allow a local user to elevate privileges and perform actions in the driver which are reserved for administrators. This only affects Windows 7 and derived platforms, Windows 8+ is not vulnerable due to changes in the implementation of a kernel function which the driver is relying on for the security check. 

The driver contains the following code:

BOOLEAN IsCurrentUserAdmin() {
	BOOLEAN CopyOnOpen;
	BOOLEAN EffectiveOnly;
	SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;

	PACCESS_TOKEN token = PsReferenceImpersonationToken(
 		KeGetCurrentThread(),
		&CopyOnOpen,
		&EffectiveOnly,
		&ImpersonationLevel);

        if (token == NULL) {
            token = PsReferencePrimaryToken(IoGetCurrentProcess());
        }
	
	return SeTokenIsAdmin(token);
}

This is a common pattern I’ve found for some drivers to determine if the current user is in the administrators group. It checks whether the current thread is impersonating an admin if not it uses the process’s primary token. The issue here is that SeTokenIsAdmin on Windows 7 and below does not check whether the token impersonation level is sufficient to be actually impersonating that user. Windows defines 4 impersonation security levels, Anonymous, Identification, Impersonation and Delegation. When making an access check in the kernel (with SeAccessCheck) anything below Impersonation level is rejected immediately. So if a thread was impersonating at Identification then trying to open say a file will fail with STATUS_ACCESS_DENIED. Therefore the issue is that this check can be made to return TRUE on Windows 7 by getting hold of an Identification level impersonation token for an administrator (such as Local System) which is typically allowed by the OS. Even an Anonymous level token would work because of the way Windows impersonation works. 

Now due to the size of the driver and its use of v-tables or function pointers for handling a lot of it’s dispatch I’ve not had the time to be able to work out a) whether this check is being used for any serious security purpose or b) whether it can even be exercised in normal operation. Therefore I can only provide basic details. For example on the version specified for the 32 bit driver the vulnerable function is at RVA 88180 and it has at least 11 references, some direct calls others being used to pass it as a function pointer. 

To test this the simplest way is to use the following code while running as a UAC administrator user on Windows 7 (but while running at the normal user level). This will grab the linked administrator token at Identification level which can be used with the ImpersonateLoggedOnUser API call to impersonate. 

TOKEN_LINKED_TOKEN linked_token;
HANDLE hToken;
DWORD dwRetLen;

OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
	
if (GetTokenInformation(hToken, TokenLinkedToken, &linked_token, 
            sizeof(linked_token), &dwRetLen))
{
	printf("Got Token: %p\n", linked_token.LinkedToken);
	if (ImpersonateLoggedOnUser(linked_token.LinkedToken))
	{
		printf("Done\n");
	}
	else
	{
		printf("Error impersonate: %d\n", GetLastError());
	}
}
else
{
	printf("Error: %d\n", GetLastError());
}

Note when testing if there’s an access check higher up the stack, for example when opening a device object this might end up as a TOCTOU issue, however another thread can run concurrently which sets the token on the calling thread so it’s possible to exploit (assuming the kernel isn’t running with a higher IRQL). 

If it’s really a security issue (I do believe it has the potential) then the fix is pretty simple, ensure the returned value of ImpersonationLevel in the function is greater or equal to SecurityImpersonation and if that fails fall back to the primary token.

e.g.

PACCESS_TOKEN token = PsReferenceImpersonationToken(
 		KeGetCurrentThread(),
		&CopyOnOpen,
		&EffectiveOnly,
		&ImpersonationLevel);

if((token != NULL) && (ImpersonationLevel < SecurityImpersonation)) {
	PsDereferenceImpersonationToken(token);
	token = NULL;
}

The actual impact of this issue will depend what you’re using it for. I’ve noticed there are a few different tools which interact with the NVAdminDevice device which at least have system stability impact which required you to be an administrator but it wasn't clear the described code path was being being used in those situations. If the check is being used to guard any sort of privileged operation such an memory writes, file access, device port access etc it could have a direct impact through elevation of privilege. 

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.
 
Project Member Comment 1 by forshaw@google.com, Dec 9 2014
Labels: Id-1587922
Project Member Comment 2 by forshaw@google.com, Jan 22 2015
Labels: CVE-2015-1170
Project Member Comment 3 by forshaw@google.com, Mar 2 2015
Project Member Comment 4 by forshaw@google.com, Mar 10 2015
Labels: -Restrict-View-Commit
Removing view restriction.
Sign in to add a comment