Duping AV with handles


This story is sort of a continuation of my previous article. Someone messaged me on Slack asking for an additional feature in the code of my pypykatz tool. In this case the request was about making pypykatz able to parse the live LSASS process with the process handle already acquired by some other -neferious- means. Let’s see what it means and why the user was probably interested in this feature, which was added a day after the request.


This time I wanted to test my improvements on McAfee MVISION Endpoint Security but I failed. After spending an hour of setup and activating all lsass protection mechanism the product has to offer, it turned out that even the unmodified script was able to access lsass and dump all credentials from the computer.


Before this commit pypykatz was able to extract the credentials from LSASS in the following ways:

  1. Open the LSASS process and parse the VAS (command: live lsa )
  2. Parse minidump files of an LSASS process offline (command: lsa minidump )
  3. Use Rekall to get credentials stored in full memory dumps and hibernation files (command: lsa rekall )
  4. Use Volatility3 to get credentials stored in full memory dumps and hibernation files
  5. Act as a plugin for MemProcFs to get credentials stored in full memory dumps.


The important feature for our current story is the first one (Open the LSASS process and parse the VAS) but how does it work? In a nutshell the following steps are done before the actual credential parsing part begins:

  1. Acquire debug privilege.
  2. Find the LSASS process’ PID.
  3. Open the LSASS process by invoking the OpenProcess method.
  4. Initialize a virtual reader and pass the process handle to it.
  5. Begin parsing the VAS using this reader and extract credentials.


Looking at the previous section we can understand that the request was to create an interface in pypykatz that bypasses steps 1–3 and initializes the virtual reader with a handle that is somehow already available to the running pypykatz process. Makes one wonder why this was needed? Is it possible to get a handle on the lsass process in a different manner than what was described in the previous section?


As we established, OpenProcess on LSASS directly is a big no-no for some AVs. Yet, we kinda need to have an open handle to get them credz.


This is a summary of how pypykatz does it.

  1. Get debug privileges.
  2. NtQuerySystemInformation will yield all handles opened for all processes. This also includes the PID information of the process for each handle. After this, for each PID/handle:
  3. OpenProcess with PROCESS_DUP_HANDLE privilege. This allows us to duplicate the handle.
  4. NtDuplicateObject will get a copy of the handle of the remote process to our process. Recommended to pass at least PROCESS_VM_READ for DesiredAccess.
  5. NtQueryObject will tell us if this handle is a Process handle or something else. (there are a lot of types, and OMG GUESS WHAT IT’S NOT DOCUMENTED)
  6. If it’s a process handle, QueryFullProcessImageName invoked with the handle will show the process executable path. If it’s lsass.exe then we have found a good match and can begin parsing.
Screenshot of the code because people tend to like articles with pictures in them


Two things have changed since the request.

  1. pypykatz now supports parsing running lsass process via external process handle using the go_live_phandle method.
  2. pypykatz now ships with the function to automatically search for open handles to lsass process in other processes and use them for parsing. This can be invoked either by calling the go_handledup method or from the command line by using the pypykatz live lsa --method handledup command.
Handle duping at work.


Yes. Consider the following cases:



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store