Recently there were several new posts (FSecure Post and Peter Ferrie) about updates to the Necurs malicious kit which in essence is based on the malicious driver with sole purpose to protect other malware from security products. The updated version is now shipped as an embedded self-contained launch-and-forget shellcode which will drop the appropriate driver according to the underlingg OS and on successful deployment will start immediate protection. The authors of the kit will supply the client several APIs that could be used to operate the driver.
I knew that for driver loading on OSs above Vista, it was using privilege escalation vulnerability. It was interesting to understand how that exploit was used in Necurs’ case and as I was unable to find a fine explanation, I studied it by myself . Here I present my findings for the matter.
Deployment
As already mentioned, Necurs operates as a 3rd party driver – meaning, the user will need Admin rights to actually use it. Being a 3rd-party product, it needs to operate on verity of Windows OSs:
- Windows XP – by default the logged-in user is already Admin, so no problem here.
- Vista and above – the user will probably have UAC enabled which will prevent driver loading.
So, the solution for Vista and above was based on the CVE-2010-4398 local privilege escalation vulnerability. It’s nicely explained in several places – by the author and Peter Kleissner.
UAC By-Pass
The by-pass is based on changing the security token of the malicious process by the “powerful” one which is borrowed from the system
process in kernel space. This task is achieved in the following way:
- kernel APIs resolution – as the exploit shellcode will actually be executed in the kernel space, it will need the appropriate API addresses to do so. Those APIs are resolved by extracting the APIs’ RVAs from the
ntoskrnl.exe
file and finding the actualntoskrnl.exe
base address (usingZwQuerySystemInformation
). - registry preparation – craft registry data in the way, that will trigger the CVE-2010-4398 exploit as explained in the original article by calling
EnableEUDC
. - exploit shellcode execution – get security token (
PsReferencePrimaryToken
) from the initial system process (PsInitialSystemProcess
) and replace the current process’s (IoGetCurrentProcess
) token with the new one (Fig. 1). Once token replaced, the shellcode will return to the original execution flow.
Multi-OS support was achieved by using predefined offsets to theToken
member in the EPROCESS
structure (Fig 2).
Return from exploit shellcode
Once the exploitation was done, the exploit shellcode will try to return to the original execution flow, as if nothing has happened (in other words, internal_func_b
finished its execution properly – see below). For the explanation, I’ll use arbitrary symbolic addresses to describe the following call stack and 32 bit shellcode version:
To understand the trick, let’s look how ESP changes from the call EnableEUDC
till the stack overflow at RtlQueryRegistryValues
. The overflow will overwrite the return address of internal_func_b
(for explanation please read here). ESP undergoes the following changes and this “picture” of ESP is what exploit shellcode will see (Fig 3):
ESP address | ESP pointed data | Remarks |
---|---|---|
0x0 | 1 | parameter for EnableEUDC |
0x4 | 0xae1efe | return after EnableEUDC |
0x8 | shellcode EBP | EIP is currently in GreEnableEUDC |
0xC | param 1 | |
0x10 | param 2 | |
0x14 | 0xbf81b8d0 | return after call to internal_func_a |
0x18 | internal_func_a EBP | |
0x3C | local vars | local params in internal_func_a |
0x40 | reg value | |
0x44 | reg value | |
0x48 | reg value | this is what exploit shellcode sees |
The token_fix
global variable will fix the ESP in a such way, so it will point to the internal_func_a stack
frame. Finally, the return address of internal_func_a
is used to return to the original flow of execution (Fig 4).
Once found, the flow will land in internal_func_a
(Fig 5).
Now, the running process has Administrator account rights, so the only simple thing left is just to re-launch itself, drop and load the driver. Just to note, that on 64 bit systems, there are similar flow of events occur.