Bitdefender Hypervisor Memory Introspection
|
This file handles token steal detection and token privilege protection. More...
#include "wintoken.h"
#include "winprocess.h"
#include "winprocesshp.h"
#include "decoder.h"
#include "hook.h"
#include "drivers.h"
#include "alerts.h"
#include "winpool.h"
#include "gpacache.h"
Go to the source code of this file.
Functions | |
static INTSTATUS | IntWinTokenPrivsHandleWrite (void *Context, void *Hook, QWORD Address, INTRO_ACTION *Action) |
EPT callback triggered when a write occurs over the Privileges bitfields in a nt!_TOKEN structure protected through EPT. More... | |
static INTSTATUS | IntWinTokenPrivsHandleSwap (void *Context, QWORD VirtualAddress, QWORD OldEntry, QWORD NewEntry, QWORD OldPageSize, QWORD NewPageSize) |
Handles a token swap-in or swap-out, re-applying protection if the token is not assigned anymore to a process. When a token is de-allocated and the whole page becomes free, as we increased on ExAllocatePoolWithTag the size of token allocations to always be one page, for performance purposes, the kernel may use the already freed page for different purposes, sometimes even mapping new physical memory into it. Since we have already hooked the page against writes, we will have, in such cases, translation violations, as the new mapping will not have the same contents as the TOKEN allocation which was freed before. Therefore we have to verify during the translation modifications of tokens if the current token is still assigned to the process and it was just swapped out, or if it has been freed, in which case we should re-establish the hook on the newly assigned token, while removing the old hook, which will solve the possibility of translation violations appearing in this case. More... | |
static void | IntWinTokenPrivsSendEptAlert (EXCEPTION_KM_ORIGINATOR *Originator, EXCEPTION_VICTIM_ZONE *Victim, INTRO_ACTION Action, INTRO_ACTION_REASON Reason) |
Sends an EVENT_EPT_VIOLATION for a token privileges violation. More... | |
static void | IntWinTokenPrivsSendIntegrityAlert (EXCEPTION_VICTIM_ZONE *Victim, INTRO_ACTION Action, INTRO_ACTION_REASON Reason) |
Sends an EVENT_INTEGRITY_VIOLATION when checks over the token privileges have failed. More... | |
static BOOLEAN | IntWinTokenPrivsShouldHook (WIN_PROCESS_OBJECT *Process, QWORD NewTokenPtr) |
Decides if the given token address should be hooked through EPT or not. More... | |
static INTSTATUS | IntWinTokenProtectPrivsInternal (WIN_PROCESS_OBJECT *Process, QWORD NewTokenPtr) |
If needed, this function will establish an EPT hook on the given token pointer for privileges protection. Note that, this function might get called in the case where Process->OriginalTokenPtr != NewTokenPtr, in order to re-establish the hook in the case where the token pointer has changed between timer ticks. More... | |
static INTSTATUS | IntWinTokenFetchTokenAddress (WIN_PROCESS_OBJECT *Process, QWORD *OldValue, QWORD *NewValue) |
Fetches the token pointer from inside the EPROCESS and returns the old token pointer and the new token pointer which may have changed at the time of the read. More... | |
BOOLEAN | IntWinTokenPtrIsStolen (WIN_PROCESS_OBJECT *Process, BOOLEAN Check, WIN_PROCESS_OBJECT **FromProcess, QWORD *OldValue, QWORD *NewValue) |
This function checks if the security token of a given process has been stone from another process. More... | |
INTSTATUS | IntWinTokenPtrCheckIntegrityOnProcess (WIN_PROCESS_OBJECT *Process) |
This function checks if the security token of a given process has been stone from another process. More... | |
INTSTATUS | IntWinTokenCheckCurrentPrivileges (WIN_PROCESS_OBJECT *Process, QWORD TokenPtr, BOOLEAN IntegrityCheck, BOOLEAN *PresentIncreased, BOOLEAN *EnabledIncreased, QWORD *Present, QWORD *Enabled) |
Verifies the current token if the current Privileges.Present and Privileges.Enabled fields were not altered in a malicious way. More... | |
INTSTATUS | IntWinTokenPrivsCheckIntegrityOnProcess (WIN_PROCESS_OBJECT *Process) |
This function checks if the privileges bitfields for the given process have been changed in a malicious manner, sending an alert if needed. More... | |
TIMER_FRIENDLY INTSTATUS | IntWinTokenCheckIntegrity (void) |
This function checks the integrity of the security token for all the processes inside gWinProcesses. The checks include both verifying if there are token pointers belonging to multiple processes, indicating a stolen token, and verifying if the Token Privileges have not changed in a malicious way, indicating a privilege escalation. More... | |
INTSTATUS | IntWinTokenPrivsProtectOnProcess (WIN_PROCESS_OBJECT *Process) |
Updates the stored original Privileges bitfields (Present and Enabled) and hooks through EPT the Privileges inside the assigned token of the given process, if needed. More... | |
INTSTATUS | IntWinTokenPrivsUnprotectOnProcess (WIN_PROCESS_OBJECT *Process) |
INTSTATUS | IntWinTokenProtectPrivs (void) |
Protects all the currently unprotected tokens belonging to processes against privileges manipulation. More... | |
INTSTATUS | IntWinTokenUnprotectPrivs (void) |
Unprotects all the currently protected tokens belonging to processes against privileges manipulation. More... | |
Variables | |
LIST_HEAD | gWinProcesses |
The list of all the processes inside the guest. More... | |
This file handles token steal detection and token privilege protection.
This module will assure, through integrity protection, once every second, that there are no tokens which are assigned to two processes simultaneously. If two processes share the same token, it means that either some rootkit or some kernel exploit leveraging some arbitrary writes has stolen a token (most probably a more privileged one) and assigned it to a malicious process. In this case, the introspection engine will raise an alert. Moreover, the privileges field in the token structures are protected against modifications. Note that, due to some privilege escalation, a process might exploit some vulnerability such that the assigned privileges are increased, for example CVE-2020-0796 leverages such a vulnerability in the srv2.sys driver so that it gains SYSTEM privileges. The privileges protection is available, due to performance reasons, only on dynamically detected processes (e.g. processes that start after introspection engine initializes), or on all processes on systems which support sub page protection (SPP). The performance improvement in this case is provided by the fact that, once introspection engine initializes, all the token allocations are forced to one page, thus there will not be extra exits outside the protected structure. It is also worth mentioning that dynamic processes can have tokens which are allocated before introspection engine initialization. For this purpose we will check that those allocations have the size of a 4kb page. Note that, on static detected processes introspection engine will verify the assigned privileges to every process once every second, through the integrity mechanism, but due to the fact that, on detection, the LPE has already been executed and the vulnerability leveraged, the introspection engine cannot enforce protection in these case, meaning that rewriting the privileges would be futile, since the process had very high privileges for around 1 second, which is more than enough to provoke damage and enforce persistence on the system. The checks performed on integrity on privileges are:
Definition in file wintoken.c.
INTSTATUS IntWinTokenCheckCurrentPrivileges | ( | WIN_PROCESS_OBJECT * | Process, |
QWORD | TokenPtr, | ||
BOOLEAN | IntegrityCheck, | ||
BOOLEAN * | PresentIncreased, | ||
BOOLEAN * | EnabledIncreased, | ||
QWORD * | Present, | ||
QWORD * | Enabled | ||
) |
Verifies the current token if the current Privileges.Present and Privileges.Enabled fields were not altered in a malicious way.
The checks performed on integrity on privileges are:
[in] | Process | The process for which the checks are done. |
[in] | TokenPtr | The GVA which points to the assigned token, may be different from Process->OriginalTokenPtr. |
[in] | IntegrityCheck | This should be set by the caller if this function is called during an integrity check on timer. If this parameter is set, the function will take into account the corner case in which there is a one bit difference between Enabled and Present privileges, due to a race condition between our checks and the privilege removal from the guest. |
[out] | PresentIncreased | It will store a boolean representing whether the current privileges violate the first check. |
[out] | EnabledIncreased | It will store a boolean representing whether the current privileges violate the second check. |
[out] | Present | The current value in the Privileges.Present field. |
[out] | Enabled | The current value in the Privileges.Enabled field. |
INT_STATUS_SUCCESS | On success. |
INT_STATUS_INVALID_PARAMETER_1 | If a NULL Process has been given. |
INT_STATUS_INVALID_PARAMETER_3 | If a NULL PresentIncreased has been given. |
INT_STATUS_INVALID_PARAMETER_4 | If a NULL EnabledIncreased has been given. |
Definition at line 811 of file wintoken.c.
Referenced by IntWinDpiValidateTokenPrivs(), and IntWinTokenPrivsCheckIntegrityOnProcess().
TIMER_FRIENDLY INTSTATUS IntWinTokenCheckIntegrity | ( | void | ) |
This function checks the integrity of the security token for all the processes inside gWinProcesses. The checks include both verifying if there are token pointers belonging to multiple processes, indicating a stolen token, and verifying if the Token Privileges have not changed in a malicious way, indicating a privilege escalation.
INT_STATUS_SUCCESS | On success. |
Definition at line 1097 of file wintoken.c.
Referenced by IntHandleTimer().
|
static |
Fetches the token pointer from inside the EPROCESS and returns the old token pointer and the new token pointer which may have changed at the time of the read.
Note: the returned old value and new value can be the same, one should check those value after calling this function in order to ensure that the token pointer has changed. If the change is considered alright, one should update Process->OriginalTokenPtr after calling this function.
[in] | Process | The WIN_PROCESS_OBJECT for which new token should be fetched. |
[out] | OldValue | The old token pointer, stored internally in OriginalTokenPtr field of WIN_PROCESS_OBJECT. |
[out] | NewValue | The value fetched from the Token field of the given EPROCESS inside the guest. |
INT_STATUS_SUCCESS | On success. |
INT_STATUS_PAGE_NOT_PRESENT | If the eprocess is not present in memory and the token cannot be read. |
Definition at line 564 of file wintoken.c.
Referenced by IntWinTokenPrivsCheckIntegrityOnProcess(), and IntWinTokenPtrIsStolen().
INTSTATUS IntWinTokenPrivsCheckIntegrityOnProcess | ( | WIN_PROCESS_OBJECT * | Process | ) |
This function checks if the privileges bitfields for the given process have been changed in a malicious manner, sending an alert if needed.
[in] | Process | The WIN_PROCESS_OBJECT for which the privileges are checked in the assigned token. |
INT_STATUS_SUCCESS | On success. |
Definition at line 958 of file wintoken.c.
Referenced by IntWinProcCreateProcessObject(), IntWinProcDeleteProcessObject(), and IntWinTokenCheckIntegrity().
|
static |
Handles a token swap-in or swap-out, re-applying protection if the token is not assigned anymore to a process. When a token is de-allocated and the whole page becomes free, as we increased on ExAllocatePoolWithTag the size of token allocations to always be one page, for performance purposes, the kernel may use the already freed page for different purposes, sometimes even mapping new physical memory into it. Since we have already hooked the page against writes, we will have, in such cases, translation violations, as the new mapping will not have the same contents as the TOKEN allocation which was freed before. Therefore we have to verify during the translation modifications of tokens if the current token is still assigned to the process and it was just swapped out, or if it has been freed, in which case we should re-establish the hook on the newly assigned token, while removing the old hook, which will solve the possibility of translation violations appearing in this case.
[in] | Context | The page for which this handler is invoked. |
[in] | VirtualAddress | The guest virtual address for which this handler is invoked. |
[in] | OldEntry | The old page table entry used to translate VirtualAddress. |
[in] | NewEntry | The new page table entry used to translate VirtualAddress. |
[in] | OldPageSize | The old page size. |
[in] | NewPageSize | The new page size. |
Definition at line 356 of file wintoken.c.
Referenced by IntWinTokenProtectPrivsInternal().
|
static |
EPT callback triggered when a write occurs over the Privileges bitfields in a nt!_TOKEN structure protected through EPT.
[in] | Context | The process for which the given nt!_TOKEN structure has been associated with. |
[in] | Hook | The GPA_HOOK structure which was set on the given token. |
[in] | Address | The guest physical address on which the write took place. |
[out] | Action | The action decided by the engine for the current violation. |
INT_STATUS_SUCCESS | On success. |
INT_STATUS_NOT_FOUND | If a process is not given as Context. |
Definition at line 429 of file wintoken.c.
Referenced by IntWinTokenProtectPrivsInternal().
INTSTATUS IntWinTokenPrivsProtectOnProcess | ( | WIN_PROCESS_OBJECT * | Process | ) |
Updates the stored original Privileges bitfields (Present and Enabled) and hooks through EPT the Privileges inside the assigned token of the given process, if needed.
[in,out] | Process | The WIN_PROCESS_OBJECT for which the privileges information is stored, and for which a hook would be established on the assigned token, if needed. |
INT_STATUS_SUCCESS | On success. |
INT_STATUS_INVALID_PARAMETER_1 | If the given process is NULL. |
Definition at line 1163 of file wintoken.c.
Referenced by IntWinProcCreateProcessObject(), and IntWinTokenProtectPrivs().
|
static |
Sends an EVENT_EPT_VIOLATION for a token privileges violation.
[in] | Originator | The originator driver which has written over the privileges. |
[in] | Victim | Describes the victim of the given violation. |
[in] | Action | The action which was decided to be taken by the exceptions engine. |
[in] | Reason | The reason why the given action was given. |
Definition at line 73 of file wintoken.c.
Referenced by IntWinTokenPrivsHandleWrite().
|
static |
Sends an EVENT_INTEGRITY_VIOLATION when checks over the token privileges have failed.
[in] | Victim | The victim, which is denoted by the process for which the privileges are increased. |
[in] | Action | The action taken by the exceptions engine. |
[in] | Reason | The reason for which the given action has been taken. |
Definition at line 125 of file wintoken.c.
Referenced by IntWinTokenPrivsCheckIntegrityOnProcess().
|
static |
Decides if the given token address should be hooked through EPT or not.
The decision is taken based on various considerations. If we have hardware support for sub page protection, we will always hook the tokens through EPT, as the impact will be much lower, even lower that with the "force allocation to page size" trick. On static detected processes, most likely the token was allocated before. On dynamically detected processes we will check if the token has been allocated with one page size, in which case we will hook the given token through EPT.
[in] | Process | The WIN_PROCESS_OBJECT for which the token would be decided if it should be protected or not. |
[in] | NewTokenPtr | The given token pointer. |
TRUE | If the given token pointer should be hooked through EPT for the privileges protection. |
FALSE | If protecting the given token through EPT would induce a high performance impact. |
Definition at line 180 of file wintoken.c.
Referenced by IntWinTokenProtectPrivsInternal().
INTSTATUS IntWinTokenPrivsUnprotectOnProcess | ( | WIN_PROCESS_OBJECT * | Process | ) |
Definition at line 1224 of file wintoken.c.
Referenced by IntWinProcRemoveProcess().
INTSTATUS IntWinTokenProtectPrivs | ( | void | ) |
Protects all the currently unprotected tokens belonging to processes against privileges manipulation.
INT_STATUS_SUCCESS | On success. |
INT_STATUS_NOT_NEEDED_HINT | If the option INTRO_OPT_PROT_KM_TOKEN_PRIVS is not activated. |
Definition at line 1258 of file wintoken.c.
Referenced by IntGuestUpdateCoreOptions().
|
static |
If needed, this function will establish an EPT hook on the given token pointer for privileges protection. Note that, this function might get called in the case where Process->OriginalTokenPtr != NewTokenPtr, in order to re-establish the hook in the case where the token pointer has changed between timer ticks.
[in] | Process | The process for which the token should be protected or not. |
[in] | NewTokenPtr | The address of the token for which the privileges would be protected. |
INT_STATUS_SUCCESS | On success. |
Definition at line 276 of file wintoken.c.
Referenced by IntWinTokenPrivsCheckIntegrityOnProcess(), IntWinTokenPrivsHandleSwap(), IntWinTokenPrivsHandleWrite(), and IntWinTokenPrivsProtectOnProcess().
INTSTATUS IntWinTokenPtrCheckIntegrityOnProcess | ( | WIN_PROCESS_OBJECT * | Process | ) |
This function checks if the security token of a given process has been stone from another process.
[in] | Process | The process whose token has to be verified. |
INT_STATUS_SUCCESS | On success. |
Definition at line 719 of file wintoken.c.
Referenced by IntWinProcCreateProcessObject(), IntWinProcDeleteProcessObject(), IntWinTokenCheckIntegrity(), IntWinTokenPrivsHandleSwap(), and IntWinTokenPrivsHandleWrite().
BOOLEAN IntWinTokenPtrIsStolen | ( | WIN_PROCESS_OBJECT * | Process, |
BOOLEAN | Check, | ||
WIN_PROCESS_OBJECT ** | FromProcess, | ||
QWORD * | OldValue, | ||
QWORD * | NewValue | ||
) |
This function checks if the security token of a given process has been stone from another process.
[in] | Process | The process who`s token has to be verified. |
[in] | Check | If TRUE, gWinProcesses will be iterated to see if the token value is the same for another process (same thing happens if the original token pointer has been modified). |
[out] | FromProcess | The process where the token has been stolen from. |
[out] | OldValue | The old token. |
[out] | NewValue | The new token. |
TRUE | The given process has a stolen token. |
FALSE | The given process has its original token. |
Definition at line 625 of file wintoken.c.
Referenced by IntWinDpiValidateParentProcessToken(), and IntWinTokenPtrCheckIntegrityOnProcess().
INTSTATUS IntWinTokenUnprotectPrivs | ( | void | ) |
Unprotects all the currently protected tokens belonging to processes against privileges manipulation.
INT_STATUS_SUCCESS | On success. |
INT_STATUS_NOT_NEEDED_HINT | If the option INTRO_OPT_PROT_KM_TOKEN_PRIVS is in fact activated. |
Definition at line 1299 of file wintoken.c.
Referenced by IntGuestUpdateCoreOptions().
LIST_HEAD gWinProcesses |
The list of all the processes inside the guest.
Definition at line 11 of file winprocesshp.c.