MS15-034/CVE-2015-1635HTTP Remote Code Execution Vulnerability Analysis
Preface
On patch day April, Microsoft fixed a remote code vulnerability MS15-034 in HTTP. SYS by marking a "high-risk" CVE-2015-1635 patch. According to Microsoft's announcement, when an HTTP server with this vulnerability receives a specially crafted HTTP request, remote code may be triggered to be executed in the target system with system privileges.
This is a security vulnerability that has no small impact on the server system, any Windows Server 6.0 R2/Server 2008/Server 2012 R2 and Windows 2012/7/8 operating systems installed with Microsoft IIS 8.1 or later are affected by this vulnerability.
According to Microsoft's announcement, this vulnerability was discovered by Citrix Security Response Team (Security Response Team of Citrix). According to the information published on the internet, citrix is a high-tech enterprise engaged in cloud computing virtualization, virtual desktop and remote access technology. This also raises a lot of questions on Twitter about whether the vulnerability was identified by APT attacks against Citrix, less than 12 hours after Microsoft released the patch, an anonymous user posted the concept verification attack code available for this vulnerability on the Pastebin website, which seems to prove this.
After obtaining this information, the author and my friends of 360Vulcan began to conduct in-depth analysis on it, and initially analyzed the vulnerability principles and utilization of relevant information within 12 hours, the following are some of our analysis results to help the security community better understand and defend against this high-risk security vulnerability.
Vulnerability Reproduction
Combined with the information posted on the Pastebin website and Microsoft announcement, we know that this is an HTTP. the integer overflow vulnerability in SYS. According to the python code on the Pastebin website, we know that the vulnerability can be triggered (detected) by sending an HTTP request in this format to the IIS server:
GET/HTTP/1.1 Host: stuffRange: bytes = 0-18446744073709551615
We can directly use the wget or curl tool to test this vulnerability. For example, we can use the following command line:
wget 127.0.0.1 –debug –header=”Range: bytes=0-18446744073709551615″
In this example, the hexadecimal value of 18446744073709551615 is 0 xffffffffffffff (16 F), which is the maximum integer that can be expressed by a 64-bit unsigned integer, so we can easily think of it, this "integer overflow" must be related to the super-large integer of the exception.
The detection tool Code provided by the POC author on Pastebin believes that if the IIS server returns "Requested Range Not Satisfiable" in the above request package, the vulnerability exists, otherwise, if "The request has an invalid header name" is returned, The vulnerability is fixed.
In actual tests, many people may find that this is not the case. For different servers, this test program may cause the server to directly initiate BSOD or even directly cause the VM process Crash (for virtual hosts ), why? What exactly is the cause of this integer? We will further discuss this in the following section.
Vulnerability Principle Analysis
HTTP. SYS is a kernel-mode driver introduced by Microsoft from IIS6.0 to optimize IIS server performance on Windows platforms. It provides functions such as receiving and responding to HTTP requests, caching quickly, improving performance, and logging for IIS and other Microsoft servers that require the use of HTTP.
HTTP. SYS provides two most important functions: Kernel-mode caching and Kernel mode request queuing. This security vulnerability lies in the Kernel mode caching (Kernel mode cache.
Here, I will take IIS 8.1 installed on Windows 8.5 X86 platform as an example for analysis and explanation. Here we analyze the vulnerable HTTP. SYS version 6.3.9600.16520, And the patched http. sys version is 6.3.9600.17712.
The anonymous author of The POC code on Pastebin mentioned that the patch fixes http! The UlpParseRange function implements repair/interception through the RtlUlonglongAdd function.
From the test code and function name, we can see that this vulnerability has a direct relationship with the "Range" field in the HTTP header, A Range request is a request domain used by the HTTP client in the HTTP protocol to retrieve only a part of the data of files on the server.
Here is a simple introduction to the concept of slow http.sysstorage. After iisingw3wp.exe receives an HTTP request, it caches the data to the kernel and integrates the HTTP Response Header. Finally, the http. sys organizes data packets to be sent through the network kernel component. The request contains the specified range of the Ranges object, while the cache contains the http file and size information.
Let's take a look at this UlpParseRange function to see if it is the root cause of this vulnerability.
The entire UlpParseRange code is long, so it is not all pasted here. The function logic is very simple, that is, from the Range bytes = lower-upper (or the lower-or-upper form, parse the lower (that is, the start offset of the read range) and upper (that is, the end offset of the read range), and then calculate the length of the read. Normally, the upper is greater than the lower, therefore, the length = upper-lower + 1
For example in the test code, lower = 0, upper = 0 xFFFFFFFFFFFFFFFF
Let's take a look at how the code was written before being patched.
PAGE:0009AD2C sub eax, edxPAGE:0009AD2E sbb ecx, ediPAGE:0009AD30 add eax, 1PAGE:0009AD33 mov [esi], eaxPAGE:0009AD35 adc ecx, ebxPAGE:0009AD37 mov [esi+4], ecx
Through the assembly code, we can see that here we subtract the lower from the upper and Add 1 to get the length gap between the two (for example, bytes = 20-50, then 50-20 + 1, there are 31 bytes between the two)
In this example, the code is 0 xffffffffffffffffff-0 + 1. An integer overflow occurs, and a 64-bit unsigned integer overflow is 0.
Let's take a look at the modified version:
PAGE:0009B501 push ebxPAGE:0009B502 sub eax, edxPAGE:0009B504 push 1PAGE:0009B506 sbb ecx, ediPAGE:0009B508 push ecxPAGE:0009B509 push eaxPAGE:0009B50A mov ecx, esiPAGE:0009B50C call _RtlULongLongAdd@20
The code here is to subtract the lower from the upper, and then use RtlUlonglongAdd to add the result with 1. RtlUlonglongAdd will perform a security check. If the sum result overflows, STATUS_INTEGER_OVERFLOW will be returned.
The lower passed in the test code is 0, so here there is also overflow, captured, blocked, but if lower! = 0. The integer overflow is not captured here. What is the problem? Where is the problem?
In fact, this may be because the POC writer intentionally hides some key details: UlpParseRange can cause Integer Overflow by manipulating the Range parameter, and is indeed repaired, but it is not where the Range data is actually faulty.
We further speculate and analyze the vulnerability and find that the vulnerability is actually used, but UlAdjustRangesToContentSize. This function is used to final correct the validity of StartingOffset and Length specified in Ranges.
UrlpParseRange first parses the Range parameter and obtains StartingOffset and Length, and stores it in the http request object. After parsing it to the corresponding cache, compare the size of Offset + Length, whether the Data length of the cached file to be requested is exceeded. If the data length is exceeded, you must crop the length to a proper length to prevent reading data that exceeds the length. See the following code:
PAGE: 0007FD09 mov eax, [ebp + length_low] PAGE: 0007FD0C add eax, dword ptr [ebp + offset_low] PAGE: 0007FD0F mov dword ptr [ebp + offset_low], eaxPAGE: 0007FD12 mov eax, [ebp + length_high] PAGE: 0007FD15 adc eax, dword ptr [ebp + offset_high]; set Length + OffsetPAGE: 0007FD18 cmp eax, esi; esi = content length, compare the actual cached data length. PAGE: 0007FD1A jb short loc_7FD30PAGE: 0007FD1C ja short partition: 0007FD1E cmp dword ptr [ebp + offset_low], ecx PAGE: 0007FD21 jb short partition: 0007FD23PAGE: 0007FD23 loc_7FD23: PAGE: 0007FD23 PAGE: 0007FD23 sub ecx, [ebp + length_low]; length = contentlength-offset PAGE: 0007FD26 mov eax, esiPAGE: 0007FD28 sbb eax, [ebp + length_high] PAGE: 0007FD2B mov [edx + 4], eax
Here we can see that there is a usable integer overflow. If Length + offset overflows, it will be smaller than contentsize. Here we will skip this "adjust" process, the Length is not processed or corrected. We have successfully controlled the Length.
Taking the value in the example as an example, length + offset = (0 xffffffffffffff + 1) + 0 (This + 1 is added in the previous UlpParseRange) = 0, less than contentsize
If the value of the lower parameter is not 0, the result is equal to the value of the lower parameter. If the value of the result is smaller than the contentsize value, the result is not adjust.
That is to say, an integer overflow occurs at UlpParseRange, which leads to Security Check Bypass. At the same time, if lower! = 0, UlpParseRange is not triggered by integer overflow, but should be triggered here.
Here we have figured out the trigger process and principle of this vulnerability:
1. When upper (offset ended with range) = 0xffffffffffffff, UlpParseRange or UlAdjustRangesToContentSize triggers integer overflow, leading to the Length check of UlAdjustRangesToContentSize
2. controllable Length, but Length = 0 xFFFFFFFFFFFFFFFF-lower (offset starting from range), and the lower must be smaller than the Data Length of the target file to be obtained contentlength.
Reproduction and principles of BSOD
We can see that many researchers testing attack programs cannot stably reproduce BSOD. According to the discussion on Github, by adjusting the lower value, some people can play blue Server 2012 R2, and some others won't, or change the file.
In fact, after analyzing the principles of this vulnerability, we can clearly understand the rule. The first principle is that the lower mentioned above cannot be greater than the requested content length, for example, setting a request for iisstart.htm (648 Bytes ), the lower must be smaller than 647.
At the same time, the processing of HTTP requests is to first parse the http request packet in the context of the process initiated by w3wp and combine it into a compact HTTP response packet, use UlSendData-> UxTpTransmitPacket-> UxpTpEnqueueTransmitPacket to queue and then UlSendCacheEntryWorker to send it out. In this process, if the data starting offset specified by range is smaller than the total length of the compact packet header, it will not trigger the subsequent hit cache processing. (Range can only be specified for the data file memory, but cannot be specified in the Response Header)
Here I use wget to add a header for testing. The response packet length should be 8.1 bytes (for Windows 310X86), that is, the lower must be greater than or equal to 310 bytes, you need to adjust this value for other messages.
In this case, iisstart.htm, lower> = 310, and <647 can stably trigger BSOD.
Further exploitation
Does this vulnerability only support BSOD? What about remote code execution? Looking at the vulnerability trigger details, it seems that code execution cannot be performed remotely, but it is possible to remotely read the server kernel memory data.
In UlpSendCacheEntry-> UlBuildFastRangeCacheMdlChain, http. sys will create MDL for the HTTP response header and cache source buffer/length (which we can control), so we will create a huge mdl for our ultra-long length, next, put it into the data packet object of UxTpTransmitPacket, use tcpip-> netio, parse MDL, and finally send the data.
In this case, the cache space can be exceeded and data from the cache memory can be read. If the cache memory is followed by 0 xffffffffffffffff-lower (4 GB ?) The left and right kernel memory (usually X64) may cause information leakage.
But first, it is difficult to have a continuous 4G memory. At the same time, it is difficult to obtain so much data through IIS, so we can only try to reduce this memory requirement: length = 0 xFFFFFFFFFFFFFFFF-lower, and lower <contetnlength, we can try to increase the content length to Reduce the Length, for example, find a file close to 4 GB on the server :)