Preface
Eternal Blue (eternal blue) is said to be a module for equation organization to attack SMB services in its vulnerability utilization framework. Because of its wide impact and utilization stability involving vulnerabilities, it has been widely used by wanna cry, a devastating blackmail worm, since its publication. 360 Threat Intelligence Center is continuously monitoring wannacry's activities. The trend we see is that wannacry's infection is still increasing, which indicates that there are still a large number of loopholes corresponding to eternalblue, the main transmission means of worms. However, the discussion on the technical analysis of eternalblue is not enough in the open channel. This paper attempts to sort out the relevant details through a more complete analysis to reveal its causes and corresponding use skills.
testing environment
The analysis of eternalblue is carried out in a relatively simple environment. The attack system is a win7 machine, and the target machine is also a win7 32-bit system. No patches related to eternalblue are installed. The version of srv.sys file is 6.1.7601.17514, and the version of srvnet.sys is 6.1.7601.17514. All code screenshots in the debugger in this article correspond to the above versions. Different versions of files may have different code or offset, but the overall execution logic should be similar.
Loophole
According to our analysis, eternalblue achieves its attack purpose by exploiting three independent vulnerabilities: the first is cve-2017-0144, which is used to cause out of bounds memory write; the second is used to bypass the length limit of memory write; and the third is used to attack the memory layout of data. The first two vulnerabilities are highlighted below. The third one will be mentioned in the process of memory layout.
Vulnerability 1
The first is the subject vulnerability used in the eternalblue tool, which is also the core part of eternalblue, No. cve-2017-0144. The vulnerability is triggered by the SMB com transaction2 command of the SMB protocol, which is described as follows:
When the data package contains the corresponding FEA list, the SMB service will convert the FEA list to the corresponding ntfe a list. The corresponding data structure is not disclosed. The following is the corresponding fealist structure analyzed by the trend team.
The entry processing function is srvsmbopen2, and the vulnerability lies in the function srvos2fealisttont:
The following is the corresponding vulnerability function srvos2fealistton, which is used to convert the FEA list to the corresponding ntfe list. The function calls srvos2fealistsizetont to calculate the length of fealist. However, the vulnerability of this function can cause an attacker to forge an extra long size in a specific case, resulting in a pool overflow in the subsequent srvos2featon conversion.
Enter the srvos2fealistsizetont function that causes the vulnerability, which evaluates the corresponding FEA List length and then update the length. The length is DWORD type at the beginning. The size calculated in the subsequent length update code is copied back according to word. At this time, as long as the initial value in the original variable a is greater than ffff, it is 10000 +, the calculation result of this function will increase.
In this assignment, the ESI becomes Si as shown below. At this time, if the data in the high bit of eax is not zero, the super long size will be returned.
As shown in the figure below is the corresponding sent packet. You can see that the length of the request packet is 103d0, and the length of the corresponding fealist is 10000.
As shown in the figure below, eax is the beginning of the list, which points to the total length of FEA list, i.e. 10000. ESI is the end of the list after traversal. Eax ESI = ff5d, which is the actual corresponding length. However, in the MOV instruction with updated length, ESI becomes Si. Because the value in eax is 10000, the eax that should have been assigned ff5d becomes 1ff5d.
Then in the following function srvosfeaton, the overflow is caused by memmove with the wrong length.
The following figure shows the overrun write caused by the replication. The length is A8. You can see that the normal request should be in the SMB buffer of the srv.sys object 86535000. Because the length is too long, the buffer allocated to srvnet.sys is overrun write.
In the internal blue, the srvnet object buffer is stably allocated to the SRV copy object buffer through the memory layout, as shown in the following figure when writing out of bounds.
There are two important domains in srvnet object buffer:
- A pointer to the specified structure (srvnet ﹣ recv) (8834e4c0 in the above figure, which is covered by ffdff020), will be used to address the function address when the SMB (SRNET) connection ends or disconnects.
A pointer to the specified structure (srvnet recv) (i.e. 8834e4c0 in the figure above, covered by ffdff020) will be used to address the function address when the SMB (SRNET) connection ends or is disconnected.
- An MDL for receiving buffer (86546160 in the figure above, covered by ffdfef80)
An MDL for receiving buffer (86546160 in the figure above, covered by ffdfef80)
Therefore, overriding and controlling MDL will cause the subsequent TCP stack to write arbitrary forgery objects. Overriding and controlling the pointer can be used to point it to a forgery object controlled by an attacker. At this time, disconnecting the SMB (srvnet) connection can lead to code execution.
As shown in the figure below, after MDL is copied to ffdfef 80, the shellcode sent by eternal blue will be written to ffdfef 80 + 0x80, that is, ffdff000.
You can see the call stack at this time:
The written address ffdff000 is the address reserved by the system for storing system information, and it can be executed.
Written to the ffdff000 address is a srvnet ﹣ recv structure (the structure is not disclosed) and the shellcode that follows. This structure is used to call srvnetcommonreceivehandler through srvnetwskreceivecomplete when SMB (SRNET) ends or disconnects. Srvnetcommonreceivehandler gets the corresponding function and calls it according to the pointer in SRV? Recv. Here is the POI (ffdff190 (ffdff020 (covered corresponding pointer) + 0x16c) + 4) in the figure below. The address is the address of our forged shellcode (ffdff1f1).
Vulnerability 2
As shown in the above vulnerability, an overrun write can be caused, but the precondition is that the length of FEA list must be greater than 10000. Through analysis, it can be found that FEA list only exists in the subcommand of SMB ﹣ com ﹣ transaction2 command, and the data structure of the command is as follows:
Totaldatacount (total packet length) is of user type, that is, the maximum value can only be ffff. How does ethernalblue send SMB ﹣ com ﹣ transaction2 subcommand requests with length greater than ffff in this place?
Through packet capturing, it can be found that the request packet sent here is not the request packet of SMB ﹣ com ﹣ transaction2 subcommand, but the request packet of SMB ﹣ com ﹣ NT ﹣ transact subcommand:
As shown in the following figure, the type of totaldatacount in the SMB com NT transaction subcommand is ulong, which supports sending packets longer than ffff.
However, SMB com NT transaction itself does not support FEA list. This involves the second vulnerability used in eternalblue.
There is a command named transaction family in the subcommand of SMB:
- SMB com transaction: used to communicate with mail slot and named pipeline
SMB com transaction: used to communicate with mail slot and named pipeline
- SMB? Com? Transaction2: used to open or create a shared file or folder and set their extended properties
SMB? Com? Transaction2: used to open or create a shared file or folder and set their extended properties
- SMB? Com? NT? Transaction: used to open or create a file or folder and apply extended property EA or security descriptor SD
SMB? Com? NT? Transaction: used to open or create a file or folder and apply extended property EA or security descriptor SD
The corresponding SMB com transaction2 command is responsible for the vulnerability.
If the length of the transmission series command is too long, SMB will split the request packet into * * second form to send, as shown below is the corresponding * * second series command:
SMB_COM_TRANSACTION
SMB_COM_TRANSACTION_SECONDARY
SMB_COM_TRANSACTION2
SMB_COM_TRANSACTION2_SECONDARY
SMB_COM_NT_TRANSACT
SMB_COM_NT_TRANSACT_SECONDARY
The server determines which * * second belongs to the corresponding transaction according to the tip, PID, uid and mid of the SMB request header, and the server determines the corresponding transaction type according to the last * * second, that is, if the last * * second is SMB com transaction 2 second, it will be processed according to SMB com transaction 2.
The following figure shows the logic of processing the corresponding * * second. If a transaction is not sent, it will follow the corresponding * * second packet later. The server will not check the corresponding * * second type. As long as the tip, PID, uid and mid match, the server will reassemble the data into a transaction, and the type is determined by the last * * second.
Therefore, in order to send an SMB ﹣ com ﹣ transaction2 with a length of 0x10000, first send an SMB ﹣ com ﹣ NT ﹣ transaction2 with a length of 103d0 (FEA list: 1000), and then send a series of SMB ﹣ com ﹣ transaction2 ﹣ secondary packets. As long as the tip, PID, UID and mid are consistent, the service end will treat it as an SMB ﹣ com ﹣ transaction2, and at this time its length is 103d0.
Since the SMB will wait for the last * * second packet to arrive before generating the final transaction, the eternalblue can contract to deploy the memory of the target device during this period, and then send the last packet to trigger the vulnerability overrun write.
The construction of memory layout
As shown in the above analysis, the exploitation of vulnerability will trigger overflow and lead to cross-border writing. However, the exploitation idea of this vulnerability in eternalblue is consistent with that of most pool cross-border writing:
- In memory spray a series of srvnet object buffers
In memory spray a series of srvnet object buffers
- Free up the space so that the object buffer of SRV can occupy the space
Free up the space so that the object buffer of SRV can occupy the space
- SRV object buffer occupation
SRV object buffer occupation
- Write srvnet's object buffer out of bounds
Write srvnet's object buffer out of bounds
- Trigger code execution
Trigger code execution
Srvnet object spray
But there is a big difference between this and general kernel vulnerability exploitation, that is, our environment is remote. In general, we can choose the kernel object of spray calmly when exploiting the local kernel vulnerability, but for the remote environment, the selection and corresponding control of the kernel object are much smaller.
In eternalblue, the object to be overwritten is srvnet buffer, which contains two important structures:
- A pointer to a specified structure can be overridden to point to a fake structure for subsequent code execution.
A pointer to a specified structure can be overridden to point to a fake structure for subsequent code execution.
- A buffer that accepts MDL. By overwriting it, the forgery structure and shellcode sent later can be guaranteed to be written to the specified area.
A buffer that accepts MDL. By overwriting it, the forgery structure and shellcode sent later can be guaranteed to be written to the specified area.
Microsoft provides the communication mode that SMB 2 directly supports TCP, through which srvnet buffer can be created.
As shown in the figure below, the size of the srvnet object depends on the first four bytes.
SRV object spray
SRV object is the address space obtained through re application after release, but how to apply stably and release a pool of memory remotely in SMB? This involves the third vulnerability used in eternalblue.
The vulnerability lies in the SMB com session setup andx command:
The request of this command depends on the value of wordcount to determine the specific request format. When it is 12, the format is as shown in the figure below. When it is 13, the variables in the red box will be different.
Directly borrow the code after reverse simplification on the Internet, as shown below: if the code sent contains 12 wordcout, including cap ﹣ extended ﹣ security field, but there is no flags2 ﹣ extended ﹣ security field, it will cause the server to process the request package of type 12 in the way of processing type 13 requests, thus entering the wrong function getntsecurityparameters process.
Getntsecurityparameters will check the parameters in the corresponding request. V70 in the function parameter is a size calculated by wordcount and ByteCount.
The calculation in the getntsecurityparameters function is as follows:
This parameter is returned to allocate a pool as a parameter of srvlallocatenonpagedpool.
Therefore, the vulnerability is used to process 12 types of request packets through 13 types. Because the formats of the two types of request packets are inconsistent, the size of the pool created by srvalallocatenonpagedpool can be controlled by controlling the offset data specified by the request packet. The following breakpoints can be used to monitor the process:
bp GetNtSecurityParameters+0x1AC ".printf\"GetNtSecurityParameters1\\n\";r;.echo;?cx-si+bx+1d;g;"
bp SrvAllocateNonPagedPool+0x10 ".printf\"SrvAllocateNonPagedPool NonPageSize:%p\\n\",ecx;g;"
bp SrvAllocateNonPagedPool+0x15C ".printf\"SrvAllocateNonPagedPool alloc Nopage:%p\\n\",eax;g;"
bp BlockingSessionSetupAndX+0x7C0 ".printf\"BlockingSessionSetupAndX double\\n\";g;"
As shown in the figure below, it is the process of illegal size generation monitored by breakpoints. By constructing malformed data packets, including data 87f8, the error offset is identified after the vulnerability is triggered, and a pool with the size of 10fec will be allocated at the end of calculation.
By breaking the corresponding command request, the previously allocated 10fec sized pool can be released, thus generating a hole in the address space, which will be filled by the SRV object buffer.
Now I know how to stably spread a continuous srvnet object buffer in memory, and how to open up and release a specified space. The basic conditions of memory layout have been met. You can see the specific layout process to the final trigger execution process as follows:
- Send a packet with FEA list length of 0x10000 through SMB com NT transaction
Send a packet with FEA list length of 0x10000 through SMB com NT transaction
- Send the subsequent SMB com trans action2 second, which will cause the SMB service to treat the SMB com NT trans action as SMB com trans action2, but the last SMB com trans action2 second is retained.
Send the subsequent SMB com trans action2 second, which will cause the SMB service to treat the SMB com NT trans action as SMB com trans action2, but the last SMB com trans action2 second is retained.
- Spring of srvnet object through SMB 2 protocol
Spring of srvnet object through SMB 2 protocol
- Through SMB com session setup andx vulnerability, a pool memory of almost the same size as the SRV object is allocated after the srvnet object
Through SMB com session setup andx vulnerability, a pool memory of almost the same size as the SRV object is allocated after the srvnet object
- Continue to spread the srvnet object through the SMB 2 protocol to ensure that the srvnet is behind the SRV object
Continue to spread the srvnet object through the SMB 2 protocol to ensure that the srvnet is behind the SRV object
- Disconnection causes the pool memory opened in step 4 to be released and a hole is generated
Disconnection causes the pool memory opened in step 4 to be released and a hole is generated
- Send the last SMB ﹣ com ﹣ transaction2 ﹣ secondary. Because of the same size, the packet will fill the generated hole, and trigger a vulnerability that will cause the MDL and pointer in the srvnet object buffer to be modified. At this time, the subsequent data sent will be copied to the location of ffdff000.
Send the last SMB ﹣ com ﹣ transaction2 ﹣ secondary. Because of the same size, the packet will fill the generated hole, and trigger a vulnerability that will cause the MDL and pointer in the srvnet object buffer to be modified. At this time, the subsequent data sent will be copied to the location of ffdff000.
- Disconnect all connections and trigger shellcode execution pointed by srvnet ﹣ recv
Disconnect all connections and trigger shellcode execution pointed by srvnet ﹣ recv
The release and allocation of memory (mainly SRV, srvnet objects) during utilization can be monitored through the following breakpoints:
bp SrvAllocateNonPagedPool+0x10 ".printf\"SrvAllocateNonPagedPool NonPageSize:%p\\n\",ecx;g;"
bp SrvAllocateNonPagedPool+0x15C ".printf\"SrvAllocateNonPagedPool alloc Nopage:%p\\n\",eax;g;"
bp SrvFreeNonPagedPool+0x3 ".printf\"SrvFreeNonPagedPool free Nopage:%p\\n\",eax;g;"
bp BlockingSessionSetupAndX ".printf\"BlockingSessionSetupAndX\\n\";g;"
bp SrvNetAllocateNonPagedBufferInternal ".printf\"AllocateNonPaged NonPagedBufferSize:%p\\n\",poi(esp+8);g;"
bp SrvNetAllocateNonPagedBufferInternal+0x179 ".printf\"AllocateNonPaged NonPagedBufferAddress:%p\\n\",eax;g;"
bp SrvNetFreeNonPagedBufferInternal ".printf\"SrvNetFreeNonPagedBufferInternal free NonPageBufferAddress:%p\\n\",poi(esp+4);g;"
ba e1 srvnet!SrvNetWskReceiveComplete+0x13 ".if(poi(esi+0x24) == ffdff020) {} .else {gc}"
As shown in the figure below, the layout of the overall monitored packets in the memory, where 867bb000 is the corresponding SRV buffer object, and then the srvnet buffer object on 867cc000 will be overwritten as follows:
The above is an overview of memory layout and corresponding sending packets in the process of using eternalblue, but there are still some details in it for further mining. Due to the limited level of the author, you are welcome to correct any mistakes.
Reference material
http://bobao.360.cn/learning/detail/3738.html
https://github.com/worawit/MS17-010
http://blog.trendmicro.com/trendlabs-security-intelligence/ms17-010-eternalblue/