How Windows 10 Explore DHCP Critical Vulnerability Detects Two More Security Errors
As described in a previous article about CVE-2019-0726 , sometimes searching for details about an already known vulnerability leads to the discovery of a new vulnerability. And in some cases there are more than one such new vulnerability.
Two functions of the dhcpcore.dll library were considered in the article: the casually mentioned UpdateDomainSearchOption and the more detailed analysis of the DecodeDomainSearchListData it calls. As always happens when searching for vulnerabilities, even if important conclusions at the end come down to one or two functions, in the process of parsing you have to look at a much larger amount of code. And sometimes the eye clings to trifles that are not related to the current task, but may have independent significance or come in handy later. Suppose that at the moment there is no time to pay attention to them, but such trifles are postponed in the subcortex and, if after a certain period of time there is an opportunity to return to them and check their guesses, they again pop up in consciousness.
And so it happened this time. When studying the DhcpExtractFullOptions function, which is responsible for processing all the options specified in the DHCP response from the server, in particular, calling UpdateDomainSearchOption, two arrays on the stack of 256 elements each attract attention immediately:
In this case, there are no noticeable checks that limit the values of data iterators arrays. Since at that moment we were analyzing another vulnerability, this information was not relevant. Therefore, it only remained to remember this place in the code in order to return to it later.
A couple of weeks pass, and we again recall the DhcpExtractFullOptions function that had attracted attention earlier. We turn to it in the disassembler, comb the previously incompletely framed pieces of code and try to understand why we are using two static arrays that intrigued us.
At the very beginning of the function execution, arrays and their iterators are zeroed:
The function parses all the options in the packet received from the DHCP server, collects information from them and then processes it. In addition, according to the results of the analysis, she also writes the corresponding event to the ETW (Event Tracing for Windows) service. It is in the event logging that the buffers of interest to us take part. Together with a lot of other data, they are transferred to the EtwEventWriteTransfer procedure. The work of preparing all the data for logging is quite voluminous and does not matter much for the vulnerability we are considering, so we can do without illustrations.
It is more important to determine how these buffers are filled. Filling occurs in the options parsing loop. First, a function with the speaking name ParseDhcpv4Option is called for the current option received for processing, which either fills the fields of the dhcp_pointers object based on the received data, or makes a note about an unfamiliar option when it encounters an option identifier with a value for which there is no handler.
Upon returning from ParseDhcpv4Option, the identifier value of the current option_tag is written to the next element of the all_tags array, the first of the arrays of interest to us. If the function met an unfamiliar option and, accordingly, did not set the is_known_option flag, then the identifier value is also written to the next element of the second array - unknown_tags. Naturally, the meaningful names of the variables in this article were already obtained by analyzing the code.
Thus, the all_tags array stores tags of all options from the received message, and the unknown_tags array stores tags only for options unfamiliar to the parser. At the same time, checking the values of the indices of these arrays is completely absent. Consequently, the values of such indices can exceed 256 and lead to writing beyond the limits allocated on the stack for memory arrays. To overfill the first array, it is enough to send a packet with the number of options exceeding 256 from the DHCP server. The same is true for the second array with the only difference being that options that the client cannot process should be sent.
Let us now try using a practical example to make sure that our conclusions are correct. To begin with, we pay attention to the fact that option tags occupy one byte, while array elements are of type int, that is, they are four-byte. Thus, we have an overflow in which we control every fourth byte, and the rest are reset to zero when overwriting.
To test our assumption, the easiest way is to overwrite the security cookie on the function in question, which will raise an exception related to the security check. Let us simulate a situation in which the DHCP server sends a sufficient number of options for rewriting. Let it be 0x1a0 (416) options with identifier 0xaa and zero size. Thus, each option takes up two bytes, and the total packet size with all headers is 1100-1200 bytes. This value is within the MTU for Ethernet, therefore, there is reason to believe that the message will not be fragmented, which will allow us to avoid possible adverse effects.
We send the packet formed in the described way in response to a request from the DHCP client and catch the exception in the corresponding svchost.exe process on the client machine:
As can be seen from the stack trace, the stack cookies and the return address from the function were rewritten by the identifiers of the options from our package.
Of course, creating a working exploit for this error will require quite a lot of effort from the attacker. On modern systems, buffer overflow on the stack is a rather complex and labor-intensive vulnerability due to all the existing defense mechanisms. On the other hand, one should not forget that all these mechanisms either protect against rewriting the return address and exception handlers, or prohibit code execution in memory regions not intended for this, or interfere with address prediction. For example, they cannot help in any way against overwriting the ones stored on the stack between the overflowed buffer and the return address of local variables. And the DhcpExtractFullOptions function in question contains several potentially dangerous variables in this interval.
Again, we write to Microsoft about the detected error. After a short correspondence and an analysis of the application, which took about a week, we get the answer that a CVE identifier is being prepared for the described vulnerability, the correction is planned for release in March, and information about the vulnerability is already available at Microsoft, was reported by someone earlier. The fact, generally speaking, is not surprising, because the error lies literally on the surface, and buffers that do not contain boundary checks of indices always attract attention first and can often be detected by automatic analysis tools.
In March, as was announced, an update is being released that fixes the described error, which received the identifier CVE-2019-0697. The previously reported researcher turned out to be Mitch Adair, the same Microsoft employee who discovered the DHCP vulnerability CVE-2019-0547 fixed in January .
Posted by Mikhail Tsvetkov, Positive Technologies Application Analysis Specialist.