Another Local Privilege Escalation Vulnerability Using Process Creation Impersonation
Introduction
Over the past few months, FortiGuard Labs has been working closely with the Microsoft Security Response Centre (MSRC) to address multiple local privilege escalation (LPE) vulnerabilities that we discovered on the Windows platform. One of the most notable LPE vulnerabilities we reported to MSRC was found on the Windows Network Connectivity Assistant (NCA), which is only enabled on the Enterprise or Education versions of Microsoft Windows 10. The nature of this LPE vulnerability had previously been reported to MSRC as proof-of-concept (POC) by James Forshaw, an author and security researcher in Google’s Project Zero. However, MSRC decided not to fix it at the time as they believed Windows was not vulnerable to such an attack.
In this blog post, I will discuss how to use simple vulnerability modeling to discover this vulnerable LPE code pattern.
Process Creation Impersonation Vulnerability Modeling
Since the concept of this logical issue is well documented elsewhere, we do not need to reinvent the wheel here. But we do need to identify the common vulnerability pattern. Fortunately, James provided a handy POC which we were able to use as our starting point.
Basically, the POC includes simple Remote Procedure Call (RPC) client and server applications that are used to demonstrate how process creation impersonation can lead to privilege escalation. In a nutshell, privilege escalation can happen when the RPC server attempts to impersonate the client and spawns a process at the same time without using an explicit token. If you found it difficult to visualize that problem, you are not alone. Reviewing the following code snippet might help:
extern “C” void TestCreateProcess(handle_t hBinding, int level)
{
if (RpcImpersonateClient(hBinding) == 0)
{
STARTUPINFO startInfo = { 0 };
PROCESS_INFORMATION procInfo = { 0 };
HANDLE hToken = nullptr;
HANDLE hDuplicateToken = nullptr;
startInfo.cb = sizeof(startInfo);
WCHAR cmdline[] = L”c:\windows\notepad.exe”;
if (level == Medium) // — (1)
{
// Open and create client impersonated token
if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE | TOKEN_QUERY, true, &hToken))
{
printf(“Error OpenThreadToken: 0x%xn”, GetLastError());
}
else
{
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenPrimary, &hDuplicateToken))
{
printf(“Error duplicate token (0x%x)n”, GetLastError());
}
else
{
LPVOID lpEnv;
// Create interactive process
startInfo.lpDesktop = _T(“winsta0\default”);
if (!CreateEnvironmentBlock(&lpEnv, hDuplicateToken, false))
{
printf(“Error create env block (0x%x)n”, GetLastError());
}
else
{
if (!CreateProcessAsUser(hDuplicateToken, cmdline, cmdline, nullptr, nullptr, false, 0, nullptr, nullptr, &startInfo, &procInfo))
{
printf(“Error CreateProcessAsUser (0x%x)n”, GetLastError());
}
else
{
printf(“[+] CreateProcessAsUser: %dn”, procInfo.dwProcessId);
}
DestroyEnvironmentBlock(lpEnv);
}
CloseHandle(hDuplicateToken);
CloseHandle(hToken);
}
}
}
else // — (2)
{
if (CreateProcess(cmdline, cmdline, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startInfo, &procInfo))
{
printf(“[+] CreateProcess: %dn”, procInfo.dwProcessId);
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
}
else
{
printf(“Error creating process: %dn”, GetLastError());
}
}
RpcRevertToSelf();
}
else
{
printf(“Error impersonating usern”);
}
}
Listing 1: Typical RPC server with a vulnerable process creation routine due to a client being impersonated by a server
As you can see in the prologue of the TestCreateProcess() function, the server first impersonates the caller process – typically the client application with lower privilege will trigger this RPC API given that the remaining operation will be executed under the caller context.
The code labelled at (1) indicates that the caller would like to run notepad.exe at a medium integrity level. In fact, this branch is used to demonstrate the proper way for a server to create an external process when the client is being impersonated.
The interesting part is at label (2), which shows you the wrong way for process creation to take place under impersonation. The code starting from label (2) does nothing more than call the CreateProcess() API. However the problem here is that since the process creation API was called by the server, the new process inherits its token from the server and not the client, regardless of its impersonation state.
In conjunction with the infamous symbolic link attacks, one can then hijack user-drive mapping, which is one of the variants of the symlink attacks that could redirect the default root drive (e.g. C: ), to an arbitrary location that is controlled by the attacker. As a result, a fake target executable – in this example notepad.exe – will be spawned with elevated privilege by the server process through the CreateProcess() API.
With this information in mind, we are now going to look for similar vulnerable code patterns within Windows. Generally, the approach is very similar to what we had discussed in our RPC bug hunting blog series. In this case, we are going to look for any Windows components that spawn an external process via CreateProcess or ShellExecute API under impersonation. This should be quite straight forward if you have experience in writing static PE parser script using Python. Otherwise, you can write an IDA script to do the same job. I will leave this as exercise for the readers.
As a result, we were able to collect a shortlist of Windows components that could be potentially vulnerable to process creation hijacking. Among those shortlisted Windows components, we found two additional Windows services that contain the vulnerable code pattern – namely NcaSvc.dll and rascustom.dll.
CVE-2019-1287: Network Connectivity Assistant Service
The Network Connectivity Assistant is used to view the current connection status and to gather detailed information that is helpful for troubleshooting failed DirectAccess connections. The NCA was first integrated with the client operating system beginning with Windows 8.
DirectAccess is a feature available on the Enterprise or Education versions of Windows 10. This feature can be understood as a VPN-like technology that provides intranet connectivity to client computers when they are connected to the Internet. DirectAccess can be enabled only when the client computers are connected to the Domain Controller from the Windows Server. In other words, DirectAccess connections are designed to connect automatically as soon as the computer connects to the Internet.
Under the hood, NCA exposes some RPC interfaces to client applications. During our investigation, we realized that one of its RPC APIs, Rpc_NcaExecuteAndCaptureLogs, is vulnerable to a process creation hijack due to the impersonated client calling the external powershell.exe executable when gathering network diagnostics information on the client machine. In fact, there are multiple PowerShell executables being spawned to generate this network diagnostic information. One of these calls CreateProcessAsUser with an explicitly impersonated token, and it also calls CreateProcess at the same time.
This bug was spotted during the latter’s process creation within the NcaScriptLogsStartProcess function. We are not exactly sure why both process creations use the same command line parameters; perhaps the engineer chose to execute administrative PowerShell commands without the impersonation token in order to generate more comprehensive diagnostic information.
This vulnerability is now fixed and has been assigned with CVE-2019-1287.
Remote Access Connection Manager Custom Protocol Engine
The Remote Access Connection Manager is a Windows service that is responsible for managing dial-up and VPN connections from the client computer to the Internet or other remote networks, while rascustom.dll is the core service component. This service is running by default on Windows 10.
Interestingly we found the same code pattern there that we saw from NcaSvc.dll in rascustom.dll as well. In other words, if we could exploit the same LPE vulnerability, the effect would be greater as all of Windows 10 would be affected. However, the RPC API exposed by the rascustom.dll, Rpc_VpnProEngExecuteAndCaptureLogs, does not allow a non-administrative user to execute the function. Typically, the RPC server will perform caller access check at the entry point of the RPC API functions before it allows the caller to access the main operation of the API. We did not attempt to dive deeper to find ways to bypass the access check, and we believe this to be very unlikely, but the service is considered to not be vulnerable to a process creation hijack as long as the access check is in place and immutable.
Conclusion
FortiGuard Labs has released the IPS signature MS.RPC.NcaSvc.Privilege.Escalation to protect customers in advance from this vulnerability. You can refer to Microsoft’s September 2019 Bulletin for more information about the issue.
-= FortiGuard Lion Team =-
Learn more about FortiGuard Labs and the FortiGuard Security Services portfolio. Sign up for our weekly FortiGuard Threat Brief.
Read about the FortiGuard Security Rating Service, which provides security audits and best practices.