BROKERS IN THE SHADOWS – Part 2: Analyzing Petya’s DoublePulsarV2.0 Backdoor
In the wake of WannaCry, a new cyber threat has emerged from the NSA leak. Making use of previously exposed tools, Petya once again is engaged in another large scale attack. Important distinctions in this case, however, are that the attacks targeted mainly a specific country, and are used solely for destruction. While Petya may look like ransomware, it appears that despite a victim paying the ransom, there is no way to decrypt the files afterward.
Petya was first seen in 2016, as a very different attack. Compared to its first appearance, in 2017 it targeted fewer file types in order to proliferate more quickly. Another difference is that the current Petya sample, which would be highly irregular for Ransomware, focuses on lateral movement over the same domain. It seeks to infect computers on the same internal network. Security services worldwide are now shifting focus, giving priority to blocking attacks from within. The initial attack vector is still unclear.
In the most recent iteration, Petya has adapted the tools used by the WannaCry ransomware as mentioned in our previous blog post. The creators of this new version of Petya reverse engineered the DoublePulsar backdoor used by WannaCry, probably in order to avoid detection. Petya is now using its own modified version of this backdoor that we call DoublePulsarV2.0.
To protect against the new Petya threat, a patch for Ms17-10 continues to be the most essential patch since Conficker in 2008, and it seems this will be relevant for years to come. Check Points IPS offers similar protection on DoublePulsar, SMB Touch, CVE-2017-0144 (EternalBlue), and others.
In Part 2 of Brokers in the Shadows, we will explore this new backdoor.
The DoublePulsar implant found in Petya behaves much like its predecessor, which appeared in WannaCry (and was originally in the Shadow Broker’s dump). Both have a similar flow of execution, with the former having minor implementation differences. The installation of DoublePulsar consists of several stages. Each stage is responsible for obtaining vital information for the subsequent level, until the backdoor is finally set up and ready to accept further commands. The backdoor comes in 2 flavors, 32 and 64 bit, and is able to determine at run-time which version should execute according to the system architecture.
This is an outline of the installation process:
Stage 1 – Obtaining Kernel Export Functions: Before any action can take place, the backdoor must get a basic set of tools to navigate in kernel memory. These come in the form of several functions exported by ntoskrnl.exe, the Windows NT kernel image. The functions used in this case are:
- ZwQuerySystemInformation– Obtains various system information details, divided into classes of information.
- ExAllocatePool– Allocates blocks of memory in memory pools, which are the kernel space equivalent of the user mode heap.
- ExFreePool– Frees any blocks allocated in the aforementioned pools.
To retrieve pointers for these functions, DoublePulsar’s payload begins by getting a pointer to the Processor Control Region or KPCR struct, which is an undocumented data structure in the kernel containing various helpful fields. One example is a field located at offset 4 of the struct, which points to the Interrupt Dispatch Table (IDT). This field is of particular interest for the sole reason that the table it points to resides in the memory of the ntoskrnl.exe image. Furthermore, this table is aligned to the beginning of a memory page, so that once this is reached, the code is able to navigate the memory backwards in page size hops, checking each page to see if it begins with MZ (the PE magic number). Once such a page is found, it is safe to say that the beginning of the ntoskrnl.exe is reached.
Figure 1: Code that locates the ntoskrnl.exe image in memory
After getting a pointer to the ntoskrnl image, the backdoor’s code follows the PE structure to get to the image’s Export Address Table. This table holds the entry points of exported functions within the image, which can be leveraged for execution by any other code. The existing function names are scanned by following the AddressOfNames array of the export directory, hashing each name with a particular algorithm (described later), and comparing the result with an argument hash. The latter represents the requested function name. Once a function name is found, the address of its entry point can be obtained by taking the element with the name’s index from the AddressOfFunctions array of the export directory.
Stage 2 – Obtaining the SMB Driver: At this point, it is possible to use the retrieved functions as primitives for retrieving further helpful information. Namely, ExAllocatePool is used to reserve memory, in which information obtained by ZwQuerySystemInformation will be written. The latter function is invoked with the SystemQueryModuleInformation information class argument, which causes it to return a list of all drivers loaded in the system. This list is scanned by the backdoor’s code, hashing the full path of each driver (with the same aforementioned algorithm) and comparing it to a predefined hash, corresponding to the SMB driver (which is srv.sys).
Stage 3 – Installing a Driver Function Hook: The pointer to the srv.sys driver is used in the final stage of the backdoor installation. The code looks for the .data section of the driver’s image, where a table named SrvTransaction2DispatchTable resides. This table contains pointers for functions that handle a particular type of incoming SMB packets, and has one function of interest named SrvTransactionNotImplemented. This function deals with any malformed packets in which reserved or otherwise unexpected fields (e.g. Timeout , Reserved etc.) are used. This makes these fields particularly useful for passing commands to the backdoor and issuing responses back to the sender, which can be achieved by replacing the function with a designated handler.
However, instead of fully omitting the SrvTransactionNotImplemented function, the backdoor’s code ‘remembers’ its entry in the dispatch table, and replaces it with a pointer to its own function that handles commands passed on the aforementioned packet fields. This handler is written to an allocated memory region, and will be invoked each time a malformed packet is received. When the handler is done running, it calls the original function with any required modifications in the passed arguments, to enable sending customized command responses to the sender.
At this point, it is worth noting that handler found installed in the current instance of Petya varies slightly from the one observed in WannaCry. Particularly, there is a change in the values of the commands it inspects (in Petya’s case: 0xf0, 0xf1 and 0xf2), as well as values passed as responses (0x11 and 0x21). The check for these values in code is depicted in the following figures, and explained in further detail later on.
Figure 2: New command codes found in Petya’s version of DoublePulsar
Figure 3: New response codes found in Petya’s version of DoublePulsar.
The string hash function used throughout the whole backdoor execution is outlined in the following Python code:
This table shows the strings corresponding to the hash values observed in the backdoor’s code:
Finally, this figure summarizes the whole flow of execution described in the above analysis:
Figure 5: Summary of the DoublePulsar backdoor installation stages
Once installed, the DoublePulsarV2.0 backdoor provides a basic communication interface based on the SMB_COM_TRANSACTION2 (0x32) command using the TRANS2_SESSION_SETUP (0x000E) subcommand.
In contrast to the previous version which was used by Wannacry and was part of the Shadow Brokers leak, the DoublePulsarV2.0 backdoor sends the messages over the “Timeout” and “Reserved” fields.
In this case, it allows the backdoor communication to bypass rules and scanners such as the Metasploit and Doublepulsar detection high profile open- sources tools.
The “Timeout” field is used to deliver the command itself in a hardcoded manner (without using any encoding or XOR method) while the “Reserved” field is used to get a positive or negative answer from the backdoor for the requested command.
As written at the “Common Internet File System (CIFS) Protocol”:
“The ‘Reserved’ field is reserved and SHOULD be set to 0x0000.”
This confirms it has indeed been abused.
This leads us to the 3 basic commands as detailed in Figure 2:
- 0xf0 (0.240 sec) – Checks if a backdoor is installed.
- 0xf1 (0.241 sec) – Uninstalls the backdoor.
- 0xf2 (0.242 sec) – Loads DLL or Executes shell code.
And 3 options for answer as seen in Figure 3:
- 0x0000– Negative answer.
- 0x1100– Positive answer.
- 0x2100 – Error message, for illegal command.
Here we demonstrate the backdoor communication flow seen in Petya.
The first step uses the 0xf0 command to check if the backdoor is installed:
It receives the negative answer that indicates that the backdoor isn’t installed.
After it’s installed by leveraging the EternalBlue exploit, the following check using the same 0xf0 command ends with receiving acknowledgement of the installation.
Now, the payload can be sent to the next targeted machine in the same network by using the 0xf2 command:
The response codes also contain error messages for illegal commands, as in this case when we sent the backdoor the illegal command 0xf7demonstrated below:
The illegal request which raises the error message:
Check Point IPS Coverage
The list below outlines the IPS protections released by Check Point and cover the relevant Petya attack vectors.
Check Point IPS Blades provides full protection against all Petya exploits.
- Microsoft Windows SMBTouch Scanner
- Microsoft Windows SMB Remote Code Execution (MS17-010: CVE-2017-0144)
- Microsoft Windows SMB Information Disclosure (MS17-010: CVE-2017-0147)
- Microsoft Windows EternalBlue SMB Remote Code Execution
- Petya Ransomware Lateral Movement Remote Code Execution
- Microsoft Windows DoublePulsar SMB Remote Code Execution