h3xduck@blog:~#

EternalBlue Series Part 6: Additional bugs

by h3xduck

22 August 2021

4. Additional bugs for EternalBlue to work.

In order to exploit the buffer overflow we found previously and launch what is called the EternalBlue exploit, we will be making use of another two bugs which exist in Windows and which will take us from the overflow in the heap to the remote code execution.

4.1 Secondary bug A - Error parsing transaction types

This bug has a lot to do with the SMB transactions we introduced in part4 of this series. Right now, we have a problem, and it is that, although we wish to pass 0x10000 (or other valid DWORD value) as a parameter to the server in the FEAlist in order to trigger the buffer overflow, the conventional SMB transactions do not let us do that, limiting the parameters to be of type WORD, making it impossible for us to send such a value in the first place.

However, there is a workaround for this. Let’s now introduce another type of transactions in SMB (which also can be fragmented into primary and secondary transactions as we have already explained in part4). We will have then:

It should be noted that in principle the protocol does not allow for combining transaction types: the connection cannot start with a SMB_COM_NT_TRANSACT and continue sending SMB_COM_TRANSACTION2_SECONDARY, and the same with starting with a trans2. However, this is the bug, the server does no checking on whether the transaction types are consistent across the communication. We could start the connection with a SMB_COM_NT_TRANSACT (which very conveniently allows us to give a DWORD as a parameter, exactly what we wanted but couldn’t do with trans2s) and then continue the connection sending SMB_COM_TRANSACTION2_SECONDARYs. This bug exists because the server will only look at the type of the last transaction and assume all the previous ones were of the same type as the last transaction. This lets us trick the server into thinking that the transmission was a full SMB_COM_TRANSACTION2 while in reality it included a SMB_COM_NT_TRANSACT at the start.

Let’s now show what we have been talking about here in the network itself. We will run the exploit and analyze the network traffic to see this effect in action. For this, we will be using Wireshark, one of the most popular network sniffing tools. Launch the metasploit exploit against our vulnerable windows machine and filter by the SMB protocol:

As it can be seen, the first transaction is a SMB_COM_NT_TRANSACT while the secondary ones being sent are trans2. And as we mentioned, the SMB server will understand it as if all the transactions were trans2, but this has allowed us to pass some DWORD parameters.


4.2 Secondary bug B - Session Setup Allocation Error & The Heap Grooming technique

If we go back to the main vulnerability in the FEA size calculation, we mentioned that we do not really know what we will be overwriting when overflowing the buffer in the heap, and in fact until this point we do not have any control on where we are allocating the NT Buffer. When overflowing the stack we can overwrite the return address, but what valuable data could we overwrite in our favour in the heap? Once we have the answers to those questions, we will be very near to exploiting the system.

Let’s first understand how we could possibly control the place where the NT Buffer is placed within the heap. If we look at the pseudocode we showed in part5, we observe that at instruction 5 the NT buffer is allocated in a Non Paged Pool within the heap. Non Paged pools are virtual addresses which are guaranteed to reside in physical memory for as long as the corresponding kernel objects are allocated. In contrast to Paged Pools, these allocations are constant in physical memory, so if we allocate a series of buffers they will be kept in the heap for as long as we wish.

Another important fact is that memory allocations tend to reuse chunks of memory. For instance, if we malloc() twenty consecutive buffers of X bytes and later we free() one of them which is in the middle, it is quite likely that the next malloc of X bytes will reuse the freed memory of that previous allocation. This technique is called heap grooming, and we will be using it to select the memory addresses which the NT buffer will occupy:

As it can be seen, we can predict the location of our NT buffer in the heap if we manage to groom the heap first. And not only that, we also know what kind of data will be stored after the NT buffer, which will be whatever is stored in Groom 4.

However, it is easier said than done. The client cannot remotely allocate memory in the server at will, it is the server which should be willingly creating the grooms, and not only that, the client must be able to free() a groom at will. There is a way to do this, fortunately, which actually is this secondary bug B we are introducing now. This bug will let us groom the heap with specific-sized grooms.

(As a curious note, this bug is currently still active in Windows versions because it is not considered a vulnerability itself!).

The bug occurs in the SMB authentication. A client, in order to authenticate itself and configure a SMB session, would send to the server a SMB_COM_SESSION_SETUP_ANDX request containing one of the following parameters, depending on whether it is using LM/NTLM authentication or NTLMv2. We can see these structures in the official documentation from Microsoft:

LM/NTLM NTLMv2

The UCHAR WordCount holds in both cases the total length of the whole SMB_parameters in words. In x86 systems, UCHAR is half a word, USHORT is a word, and ULONG is a double word. Therefore if we calculate the value contained in WordCount in both structures:

Of course, the server, when receiving a new SMB_COM_SESSION_SETUP_ANDX, does some checks and ensures all parameters are correctly set, and then proceeds to read the SMB_Data part of the struct. This is done in the function BlockingSessionSetupAndX, which has the following form (you may decompile it with ghidra to obtain this pseudocode):

What we have inside the function is, first, some checking to see if the wordCount is 13 (LM/NTLM auth) or 12 (NTLMv2), but that second option must also come with the capability to use extended security, defined as CAP_EXTENDED_SECURITY. If any of those conditions is not met, the server rejects the request for the SMB session.

Then we see at instruction 8 that the server will verify again that the request is of Extended Security, checking both the capability at the request and the flags of the SMB header. If all is set, it proceeds to interpret it as an extended security (NTLMv2) request. Otherwise the server assumes that if it is not of the second type then it must be of the first one (LM/NTLM) and proceeds to interpret the structure as such.

However, if we look closely at the function and the if clauses, we can see that we can force a wrong interpretation of the structure we send if:

What will happen in this case is that the request will pass the error checking, will not go into the If clause of Instruction 8 because the flag of the header is not set, and will then be interpreted as an structure for LM/NTLM authentication (first type) while actually being of the second one. Therefore, the server will assume that the number of words in the structure is 13 when they actually are 12, which means that the USHORT ByteCount is parsed along with the words, and interprets that the ByteCount now is what is contained in the SMB_STRING NativeOS[] and NativeLanMan[]. As ByteCount specifies in the struct the amount of bytes to allocate for the data of SMB, now that the server thinks ByteCount is NativeOS and NativeLanMan, it proceeds to allocate as many bytes as those strings indicate. And given that all the fields are controlled by the attacker, we have now achieved remote heap allocation. These allocated bytes will be the grooms we have been talking about previously.

Also, the other issue we had, which is to free the grooms in order for our predicted heap allocation to take place, is as easy as ending the SMB session, in which the server deallocates the data allocated for the structure in SMB_COM_SESSION_SETUP_ANDX, and thus frees the bytes incorrectly allocated before because of this bug, effectively freeing the groom.


We are almost there! We now know how to achieve remote heap allocation and deallocation, so we are only left to know how Eternalblue takes advantage of this to execute remote code. If you have reached this point without skipping any of the parts, congratulations! The fireworks are almost about to start.

See you on the next part!

tags: stack - overflow - buffer - exploitation - shellcode - RCE - eternalblue - vulnerability - windows - eternalblue - SMB

Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.