staticanalysis / google-security-research

Automatically exported from code.google.com/p/google-security-research
0 stars 0 forks source link

NVidia Windows Display Driver: Admin Impersonation Check Bypass #212

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
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.

Original issue reported on code.google.com by fors...@google.com on 8 Dec 2014 at 10:32

GoogleCodeExporter commented 9 years ago

Original comment by fors...@google.com on 9 Dec 2014 at 9:15

GoogleCodeExporter commented 9 years ago

Original comment by fors...@google.com on 22 Jan 2015 at 2:03

GoogleCodeExporter commented 9 years ago
Fixed in http://nvidia.custhelp.com/app/answers/detail/a_id/3634

Original comment by fors...@google.com on 2 Mar 2015 at 8:20

GoogleCodeExporter commented 9 years ago
Removing view restriction.

Original comment by fors...@google.com on 10 Mar 2015 at 8:20