What is DoublePulsar Backdoor?
Behind a series of SMB exploitation vulnerabilities by NSA, ultimately lies the use of the DoublePulsar backdoor. DoublePulsar is a fileless kernel-level SMB backdoor.
How to Install DoublePulsar Backdoor
The DoublePulsar backdoor is installed using kernel-level shellcode (on x86 systems, shellcode typically starts from address 0xffdff1f1). Letâs take a closer look at this shellcode.
The beginning of the shellcode contains an interesting piece of code:
At first glance, this code appears redundant, as the jz jump will never be executed. In fact, this code is used to test whether the system is a 64-bit system.
On a 64-bit system, the bytes above disassemble as follows:
In this case, the jz will always jump.
This code segment tests whether the CPU is 32-bit or 64-bit, and based on the result, jumps to different shellcode.
This article analyzes the example using a 32-bit system.
Following this, the code proceeds as follows:
What does this code do? Its purpose is to hook SYSENTER, as detailed in [1]. The next time the SYSENTER instruction is executed, control will transfer to the code at ebx+17, which is address FFDFF221.
Following the code at FFDFF221:
This code dismantles the SYSENTER hook above. Subsequently, it executes the function at FFDFF39A, which attempts to install the DoublePulsar backdoor. This function is somewhat lengthy but very clear in its objective: it replaces the function pointer at offset 0x38 of srv.sys!SrvTracation2DispatchTable (item 0xe in the table). The replaced code serves as the SMB backdoor function.
By setting a breakpoint at ffdff51e, it becomes very clear to observe:
Here, ebx is the address of srv!SrvTransaction2DispatchTable, ebx+0x38 points to the function pointer of item 0xe. Eax represents the address of the replaced backdoor function.
Features of DoublePulsar Backdoor
According to the analysis above, the DoublePulsar backdoor has been successfully installed into item 0xe of srv!SrvTransaction2DispatchTable. So, how is this backdoor exploited? Attackers simply need to construct an SMB_COM_TRANSACTION2 request (0x32) and specify Subcommand as SESSION_SETUP (0xe), which is an invalid value. This action triggers the dispatch function at item 0xe of SrvTransaction2DispatchTable, invoking the replaced backdoor function. The SMB request packet appears as shown in the following figure:
Next, letâs analyze the functionality of the backdoor function.
This function can be considered the essence of the entire DoublePulsar backdoor, employing a series of dazzling techniques.
The function starts by calling three different functions:
- sub_FFDFF930
- sub_FFDFF89F
- sub_FFDF8E0
Letâs analyze these three functions one by one.
1. sub_FFDFF930
First, consider the function sub_FFDFF930.
Although this functionâs code is very simple, deeply understanding it is not easy, primarily due to some data structures used that are not publicly documented, requiring extensive reverse engineering for speculation. The functionâs parameter is passed to the Dispatch functionâs parameter in SrvTransaction2DispatchTable, with the parameter type not publicly disclosed and tentatively referred to as CONTEXT. After extensive reverse engineering, the conclusion is as follows:
Somewhere at an offset in CONTEXT, a pointer Ptr is stored (further in-depth analysis reveals that this pointer actually points to IRP, which should be the IRP pointer of the SMB request). The value of this pointer is exactly the starting address of CONTEXT + Size. Therefore, by incrementally searching from the starting address of CONTEXT in steps of 4 bytes, this pointer can be located.
As shown in the figure below:
Once this address is found, it becomes possible to deduce the addresses of the SMB response packet and where Parameter is stored in the SMB request packet.
While this may seem convoluted, its primary purpose is to avoid hard-coding offsets for Parameter in the SMB response and SMB request packetsâ structure across different platforms.
The starting address of the SMB response packet is stored at ebp+44, while Parameter in the SMB request packet is stored at ebp+38.
2. sub_FFDFF89F
Next, consider the second function sub_FFDFF89F:
This function is simpler, used for initializing cryptographic operations for session keys.
3. sub_FFDF8E0
Lastly, consider the third function, which performs simple validity checks on the SMB backdoor request packet. It also employs techniques to find kernel-mode pointers to verify their validity. Additionally, it checks ParameterCount and TotalParameterCount values, both of which must be 0xc. The presence of this function demonstrates that this shellcode has taken thorough consideration of stability.
4. FFDFF88B
After these three functions execute, they call the function FFDFF88B, which, based on its calculation results, jumps to different branches.
The pseudo-code of this function is as follows:
This function simply sums up the four bytes of dword, converting the result into a byte.
So, what is its input? After debugging analysis, the input is the Timeout field of the SMB request.
In other words, the backdoor function decodes the Timeout request field of SMB, comparing the decoded result with 0x23, 0x77, 0xc8 as commands for the backdoor.
When the result is 0x23, the backdoor function checks whether the backdoor is already installed, known as the PING command.
When the result is 0x77, the backdoor function further executes the injected shellcode.
When the result is 0xc8, the backdoor function uninstalls the backdoor.
Consider a specific example:
In this request, the value of Timeout is 93 89 07 00: 0x93 + 0x89 + 0x07 + 00 = 0x123, taking the last byte, resulting in 0x23, as analyzed above, which corresponds to the PING command.
When handling the PING command, we see that the calculated key is placed in the Signature of the SMB response packet, ensuring encryption for subsequent shellcode transmissions.
In the final segment of PING command processing:
Here, based on different states, the MID (Multiplex ID) of the response packet is incremented by a value (normally, the MID of the response must match the MID value of the request). The specific meaning of the MID increment value is detailed in the table below:
The following figure illustrates a successful example of the PING command:
Request,
As shown, the MID in the request is 66 (0x0042).
Response:
The MID value in the response is 82 (0x0052). The difference in MID values is 0x0052 â 0x0042 = 0x10.
After completing these steps, control returns to the original dispatch function to respond to the SMB request.
Conclusion
This article provides a detailed analysis of DoublePulsar, which serves as the NSAâs seventh weapon. The code is ingeniously designed with strong concealment. The author demonstrates a deep familiarity with Windows code, especially the SMB section. The entire shellcode is crafted with considerations for generality and stability, akin to a finely crafted weapon. Such a kernel-level backdoor undoubtedly opens a wide gateway for intruders, allowing almost unrestricted access.