24/7 Emergency – (888)-287-5858

Support            Contact Us            Blog      

The story of a privileged handle…



As virtualization technology continues to become the corporate standard, the popularity of Virtual Desktop Infrastructure (VDI) in large enterprises has been increasing. These automated environments can provision desktops and applications from the internal and external network on top of virtualization technology without an IT administrator’s input. There are many components involved in a VDI infrastructure, but one specifically caught our attention on a customer mandate back in September 2017: the Windows “vmwagent.exe”.

On this particular mandate, we had to escape the VDI environment with developer access and without local administrative access. The customer had done a great job at image hardening; services, applications and operating systems were well configured and patched, with up-to-date antivirus software, behavior monitoring, and strong passwords. Faced with this situation, we decided to perform a quick look around with the popular Process Explorer from the SysInternals Suite. One of the many notable features we like about this tool is the ability to display opened handles per process.

We were shocked to find a privileged process handle in an unprivileged process! We had stumbled onto a potential vulnerability in the VMWare Horizon solution. Ultimately, we were able to exploit this to grant us local administrative privileges on all Windows desktops in the VDI. These results were then reported to VMWare.

Our goal therefore for this blog post is to deliver some background as well as technical details on the CVE-2017-4946 vulnerability. Hopefully we will be able to draw the attention of as many VMWare customers as possible and better explain the urgency of applying the new update, as stated in the VMSA-2018-0003 advisory.

We also want to urge as many security professionals to not overlook such vulnerabilities. They are easy to identify.


Handles in Windows

Each process in Windows has its own memory space in which various code, data, and metadata sections are mapped. A process cannot directly access resources outside of its memory space boundary. It needs a handle authorized by the kernel first.

The handle allocation protocol is simple, you declare what resource you want and what you intend to do with it. The kernel will then decide, based on the originating security token and other parameters, if it is authorized to allocate a handle. Once authorized, the process needs to use the handle for each action on the associated resource. The kernel will only allow actions that were previously authorized when the handle was given. Basically, this works like a session token in the web application world.

Here is a list of objects from which we can retrieve a handle:

Access token Change notification
Console input Console screen buffer
Desktop Event
File File mapping
Job Mailslot
Mutex Pipe
Process Registry Key
Semaphore Thread
Timer Transaction
Window Station

Details on MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724251(v=vs.85).aspx


Identifying the vulnerability

In a VDI session, using Process Explorer, it took no time to realize that the “vmwagent.exe” process (6912) had an opened process handle to its parent process “v4pa_agent.exe” (1540). The lower pane of Process Explorer window, as displayed below, reveals the opened handle. The upper pane of the window shows that we were not authorized to access the “v4pa_agent.exe” process since it was running under the NT AUTHORITY\SYSTEM privileged account.

Our plan became obvious, if we could use that handle from our user-owned “vmwagent.exe” process and interact with the privileged parent process “v4pa_agent.exe”, we could leverage code execution under the security context of the system.

Image 1 – Process Explorer capture showing the privileged handle in an unprivileged process.


The only missing element was to confirm the handle’s granted access rights. Since we were the owner of the “vmwagent.exe” process which holds the handle we wanted, we could dump the process memory using the minidump feature of Process Explorer and analyze it with the Windows Debugger as shown below.

Image 2 – WinDBG capture showing the privileged handle granted access rights. 


At this point, we confirmed that the vulnerability was exploitable. Due to time constraints, we decided to develop the exploit directly on the vulnerable system. Visual Studio was available, we were given developer access, remember?!


Exploiting a privileged process handle

There are multiple ways to exploit a privileged process handle, but first we needed to acquire the handle! We could either inject a library in the “vmwagent.exe” process itself and use the handle from there or use the DuplicateHandle call. Once acquired, we could use the privileged process handle to hijack the execution flow of the parent process: allocating memory and altering protections was possible with VirtualAllocEx and VirtualProtectEx calls. This done, we examined possible exploits.

Below is a partial list of our options:

  • Perform a DLL injection technique targeting the parent process with CreateRemoteThread call.
  • Overwrite a callback in the parent process. (Note that this technique could fail under the Control Flow Guard (CFG) security mechanism. Unless you attack CFG directly.)
  • Hook a common library function with raw instruction patching.

In this environment, CreateRemoteThread was not possible and a callback overwrite would be too risky, so we chose to go with a good old hook.

The function we decided to hook was RtlInitUnicodeString in NtDll.dll library. This function initializes UNICODE_STRING structure from a wide char string (PCWSTR). It is often called and was our best bet on hijacking execution flow. We could find its address easily in our own process with the GetProcAddress function. NtDll.dll shares the same base address across all processes until reboot.

Image 3 – Attack scenario representation. Processes from left to right are vmwagent.exe, exploit.exe and v4pa_agent.exe.


Finally, the exploit shellcode used for the payload was straightforward. We only wanted to allow a simple LoadLibraryA call to load a malicious library into the process. At this point, we could have used any type of shellcode, but we found it useful to have a “static” shellcode. It would allow us to only have to rebuild a library to execute our desired code. Time not being on our side, we quickly allocated a full memory page for the shellcode and used the following offsets for dynamic values:

  • +0x0      => shellcode base
  • +0xF00 => Name of the library to load
  • +0xFF0 => Toggle bit (used to avoid calling LoadLibraryA on every call)
  • +0xFF8 => Address of LoadLibrary
;; (Poorly written) Exploit shellcode to call a function with 1 argument within process memory.
;; by Martin Lemay, GoSecure Inc. 2017

push rcx
push rdx
push rax
push rbx
call bridge
and rbx, 0xfffffffffffff000
mov rcx, qword ptr [rsp+0x20]
add rcx, 8
mov qword ptr [rsp+0x20], rcx
mov rax, qword ptr [rbx+0xff0]
cmp al, 1
je repair
mov qword ptr [rbx+0xff0], 1
mov rcx, rbx
add rcx, 0xf00
call qword ptr [rbx+0xff8]

  pop rbx
  pop rax
  pop rdx
  pop rcx
  xor eax,eax
  mov QWORD PTR [rcx+0x8],rdx
  mov WORD PTR [rcx+0x2],ax
  mov WORD PTR [rcx],ax
  jmp qword ptr [rsp]

  pop rbx
  push rbx

After execution, we recovered the execution state and returned to the original RtlInitUnicodeString function. Improvements could be made at this point for a more stable and re-usable exploit across various Windows flavors and architectures, but in our case, it was more than enough for our needs: we had successfully exploited the vulnerability.



In the past months, we’ve encountered multiple instances of bad handle usage that went undetected by vendors. Note that we are not only seeing bad process handle usage, but also bad device handle usage that led to kernel privilege escalations.

Interestingly, this old attack vector is still not well understood in the industry. We hope this blog serves as an incentive for security professionals to not overlook this crucial attack vector on engagements and for developers to properly manage handles on Windows applications.


GoSecure Titan® Managed Extended Detection & Response (MXDR)​

GoSecure Titan® Managed Extended Detection & Response (MXDR)​ Foundation

GoSecure Titan® Vulnerability Management as a Service (VMaaS)

GoSecure Titan® Managed Security Information & Event Monitoring (SIEM)

GoSecure Titan® Managed Perimeter Defense​ (MPD)

GoSecure Titan® Inbox Detection and Response (IDR)

GoSecure Titan® Platform

GoSecure Professional Security Services

Incident Response Services

Security Maturity Assessment

Privacy Services

PCI DSS Services

Penetration Testing Services​

Security Operations


GoSecure MXDR for Microsoft

Comprehensive visibility and response within your Microsoft security environment


Cyber Risks

Risk-Based Security Measures

Sensitive Data Security

Safeguard sensitive information

Private Equity Firms

Make informed decisions

Cybersecurity Compliance

Fulfill regulatory obligations

Cyber Insurance

A valuable risk management strategy


Combat ransomware with innovative security

Zero-Day Attacks

Halt zero-day exploits with advanced protection

Consolidate, Evolve & Thrive

Get ahead and win the race with the GoSecure Titan® Platform


GoSecure Titan® Endpoint Detection and Response (EDR)

GoSecure Titan® Next Generation Antivirus (NGAV)

GoSecure Titan® Network Detection and Response (NDR)

GoSecure Titan® Inbox Detection and Reponse (IDR)

GoSecure Titan® Intelligence


GoSecure is a recognized cybersecurity leader and innovator, pioneering the integration of endpoint, network, and email threat detection into a single Managed Extended Detection and Response (MXDR) service. For over 20 years, GoSecure has been helping customers better understand their security gaps and improve their organizational risk and security maturity through MXDR and Professional Services solutions delivered by one of the most trusted and skilled teams in the industry.




24/7 Emergency – (888)-287-5858