In hacking, a shellcode is a small piece of code used as the payload in the exploitation. It is called “shellcode” because it typically starts a command shell from which an attacker can control the compromised machine.
An exploit will commonly inject a shellcode into the target process before or at the same time as it exploits a vulnerability to gain control over the program counter. Once the program counter is adjusted to point to the shellcode, the shellcode gets executed and performs its task.
A famous one of this kind of exploits is EternalBlue. Many people may still feel chilly when hearing EternalBlue, as it is the very exploit to be used in WannaCry, a ransomware impacting 200,000 computers across 150 countries in 2017.
EternalBlue exploits a vulnerability in Microsoft’s implementation of the Server Message Block (SMB) protocol, as known as MS17-010. The Windows SMBv1 implementation is vulnerable to buffer overflow in Large Non-Paged kernel Pool memory through the processing of File Extended Attributes (FEAs) in the kernel function, srv!SrvOs2FeaListToNt.The EternalBlue has been used not only in WannaCry ransomware, but also in EternalRocks, a Server Message Block (SMB) worm , and Adylkuzz, cryptocurrency mining malware in 2017.
Here is the example how we exploit MS17-010 in RidgeBot. With this built-in exploit, RidgeBot customers can test whether their system is immune to WannaCry kind of events.
The mechanism of the EternalBlue exploitation:
The function srv!SrvOs2FeaListToNt will call srv!SrvOs2FeaListSizeToNt to calculate the received FEA LIST size before converting it to NTFEA (Windows NT FEA) list. The following sequence of operations happens:
srv!SrvOs2FeaListSizeToNt will calculate the FEA List size and update the received FEA List size
The resulting FEA size is greater than the original value because a wrong WORD cast.
When the FEA List is iterated to be converted to NTFEA LIST, there will be an overflow in the non-page pool because the original total size of list is miscalculated
Shell code Implemetation:
7 steps shellcode used here to finally achieve the RCE:
- Hook sysenter entry function kifastercallentry:
Sysenter uses three MSR (Model Specific Register) registers to specify the jump target address and stack location. The operating system sets these three registers through the rdmsr / wrmsr privileged instruction in kernel mode, which of course must be completed during system initialization (before the first system call occurs). If we read the address of the kifastercallentry function through the rdmsr instruction, and replace the kifastcallentry function address with the shellcode address, then we successfully hiject the system.
- Find ntkrpamp.exe module start address
With above asm code execution, we can see when the address of the KiFastCallentry function is 805416e0, and the ntkrpamp.exe module is located at [804d7000, 806e5000].
- According to the address of ntkrpamp.exe module, dynamically calculate the addresses of some key functions.
- Obtain the eprocess object of the lsass.exe process
- Obtain the thread chain for eprocess object
- Try to inject APC call into thread chain of eprocess
When successfully performed KinsertQueue function injection, UserApcPending flag would be set as 1.
- When thread in alterable state, the injected APC call would be executed.