Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Stick-U235/c4c98fa6386b9ee27145de0513a98890 to your computer and use it in GitHub Desktop.
Save Stick-U235/c4c98fa6386b9ee27145de0513a98890 to your computer and use it in GitHub Desktop.
Exploiting the Integer Overflow Present Within HTTP.sys (MS15-034)

HTTP.sys is Microsoft’s device driver utilized to handle HTTP requests to a hosted web application, commonly IIS-based. HTTP.sys implemented feature enhancements that were introduced with IIS 6. Among these feature enhancements includes Kernel Caching, which allows for a more seamless experience for the user. HTTP.sys in IIS 6+ now caches responses within the kernel, which allows for the kernel to return cached data to the user in a faster manner than the previous implementation which relied on the kernel to pass the request to the worker process for the response. With less process hops, returning the response directly from the kernel cache increases speed. It should be noted that other services outside of IIS utilize HTTP.sys as well, such as netsh and servicestate.

To better understand the vulnerability we must first gain an understanding of how HTTP.sys works, and why it is vulnerable in the first place. HTTP.sys handles all requests to a web application. If the response is already cached, HTTP.sys will reach into the Response Cache and return the requested data. If the HTTP engine within HTTP.sys does not find relevant information in the Response Cache, it forwards the request to a Request Queue, to be processed by the worker process (w3wp.exe) once it is ready to handle the request.

Image1

As mentioned above, HTTP.sys released an update with IIS 6 that allows for faster responses to user requests due to its configuration of handling requests and responses on the kernel level. Shown below is the addition of 5 functions within the HTTP.sys source code that was added with the IIS 6 release. These 5 functions handle kernel caching and parsing. The comparison chart was generated by performing a differential analysis of the two binaries (IIS 5 and IIS 6).

Image

We see that each new function is built to handle the Range header introduced within HTTP 1.1. The Range header was introduced to handle requests for only a partial chunk of a representation. When a browser attempts to access page 774 of a 10,000 page document, it is unnecessary to request all 10,000 pages. This is where the Range header comes into play. We can request only a specified region of a representation from the kernel’s cache.

The features focused upon kernel caching and Range header handling introduced in IIS 6 come with catch: An unsigned integer overflow now present within the HTTP.sys source code.

When an unsigned integer is utilized to hold a value, proper bounds checking is typically performed in order to prevent potential storage of a larger integer than the variable can hold. In a function within HTTP.sys: ULongLongAdd, proper bounds-checking precautions were not performed. The function allows for user-input to modify the value of the variable without performing the appropriate checks to ensure that the variable type’s integrity will be upheld. This variable’s value can be modified by modifying the Range Header within the request header. Since the variable in charge of storing the range header is assigned an unsigned integer 64 type, it can only have a maximum value of 18446744073709551615 (hex: 0xFFFFFFFFFFFFFFFF). Different programming languages and compilers handle the overflowing of an integer in various manners. A common and typical outcome is a “wrap around” affect, where adding the value of 1 to the maximum value of the variable will equal 0.

Regarding the initial vulnerability introduced within the Range Header, the warning to implement proper bounds-checking is heavily mentioned. According to the RFC of Range Headers: “Since there is no predefined limit to the length of a payload, recipients must anticipate potentially large decimal numerals and prevent parsing errors due to integer conversion overflows.” (RFC 7233).

Writing a small program in C can allow an attacker to gain a better understanding of how an integer overflow works. The program will add two variable’s values, store the sum in a third variable and print the result.

#include<stdio.h>
int main()
{
   int a, b, c;
   printf("Enter two numbers to add\n");
   scanf("%d%d",&a,&b);
   c = a + b;
   printf("Sum of entered numbers = %d\n",c);
   return 0;
}

We run this program, assign one variable the value of 4294967295 (the largest value an integer can hold) and the second variable is assigned a value of 1. Notice how when we add one extra positive value to the maximum integer value, there is a “wrap around” affect, causing the result to be 0 rather than 4294967296.

Image

There are more conditions that must be met to trigger the integer overflow and exploit this vulnerability. The requested file must exist, and the Range header must meet the following range conditions:

GET / HTTP/1.1
Host: stick.lol
Range: bytes=x-18446744073709551615

X, symbolizing the lower-end of the Range, must be less than the size of the file requested from the web server, as well as have a value greater than 0. The following conditions must be true: x < fileSize && x > 0

The basic flow chart of the HTTP requests’ Range header conditions is a helpful visual aid in observing the requirements of the web server to process the Range header:

Image

Exploiting this behavior in the HTTP.sys device driver via the Range Header within a request to a web service can have various affects including kernel cache dumps, user-land memory dumps, DoS conditions, BSoD’s and memory corruption. With the above request, the expected result is for the response to return a large area of kernel memory. Among testing, results have shown that if the webserver is utilizing SSL a larger amount of data will be returned.

It is possible to return memory from the user-land worker process as well. Adding another header to the request, Translate, will prevent HTTP.sys from caching the response. This will in turn cause HTTP.sys to reach into the worker processes memory within user-land and return the requested range. Another benefit of utilizing the Translate header within an initial request is that it calls upon the UlSendHttpResponse() within HTTP.sys. This function does not cause IIS to crash during exploitation. This avenue allows the attacker to focus more on abusing the integer overflow to recover memory cache from the operating system rather than just causing a DoS condition or causing a BSoD.

There are two other functions within HTTP.sys that can be utilized to leak user-land data, UlSendCachedResponse() and UlCacheAndSendResponse(). To cater the exploit to use these code paths, If-Range header should be added to the GET request. It shall be noted that utilizing these other two code paths to exploit the vulnerability has led to BSoD’s, which are not beneficial to an attacker trying to gain remote code execution.

To better observe the memory leak in action, a Windows 8.1 operating system was installed with IIS 7. Within the C:\Inetpub\wwwroot subdirectory, three files were created. The first file was around 300KB of “A”’s. The second and third files were accordingly a collection of the same amount of B’s and C’s. Making a request first for the list of A’s returned the expected result: a file containing all A’s. Making the second request, specifically for B’s, returned the entire of file of B’s. Now both the response for a file containing all A’s and a file containing all B’s is in the kernel cache. Again, a request was made for the file of A’s, but this time the integer overflow is exploited. We set the Range header to “Range: bytes=18-18446744073709551615”.

As well, we include a new header, Translate. When we include the Translate header, HTTP.sys calls upon its UlSendHTTPResponse() function, which tells the worker process to handle the response instead of HTTP.sys handling it. Also, when the Translate header is present, HTTP.sys will not cache the worker processes response. In turn, when the worker process responds, it responds with user-space memory rather than kernel-space. This Translate header also prevents the DoS / BSoD condition, and is therefore ideal for further exploitation.

We make another request for the file of A’s, this time with our malicious Range and Translate header implemented. We receive our initial file of A’s, the integer handling the response is overflowed, and the entire cache dumps. We see trailing B’s behind our request for A’s. This confirms the memory-leak vulnerability. In the request for the B’s which accordingly returned cached data as well, there were interesting aspects observed. Separating the original file of A’s and the cached B’s is what appears to be a heap address.

Image

It was observed and confirmed that approximately 65,193 bytes of kernel cache have been returned. The amount of data leaked will vary greatly and grow larger in accordance with the size of the file server and the amount of requests it receives and hence caches. An important note in exploiting this memory leak vulnerability: HTTP.sys will fetch and respond with user-land memory in buffers of approximately 64KB. If invalid memory space is found and accessed before the response buffer is full, the response buffer will empty. When designing an exploit for this vulnerability, it is best to apply appropriate precautions when gathering data to prevent an empty response.

With the data gathered, we are able to successfully cause a DoS / BSoD condition, as well as leak user-land and kernel-land memory. This will allow us to leak ASP source code, determine the targets architecture and leak valid heap addresses remotely within the worker process: w3wp.exe. Gaining remote code execution off of the current setup and knowledge is rather difficult. It is possible to retrieve credentials from the memory leak, though the consistency of this is not solid. Other avenues shall be observed in order to gain a path for reliable exploitation. That fact aside, memory corruption within HTTP.sys is possible as well.

Now that we can cause a DoS condition or leak memory from the victim, the next step in this assessment is to gain an overview of the possibility of remote code execution. When the worker process, w3wp.exe, sends a response to the kernel to be cached then sent to the requesting client, HTTP.sys uses its function UlpCreateCacheRangeSliceTracker() to handle caching the response from w3wp.exe. If the response is larger than 256KB, then the content is sliced into 64KB chunks to be stored. A while loop is utilized to store the 64KB chunks into an array, one-by-one.

Pseudo-code of the UlpCreateCacheRangeSliceTracker() function within HTTP.sys:

DOWRD i = 0;
DWORD sliceNumber = rangeStart / 65536; // 65,536B = 64KB.
DWORD sliceEnd = (rangeEnd - 1) / 65536;
while (sliceNumber <= sliceEnd)
useSlice[i++] = sliceNo++; // The useSlice array is allocated on the stack.

As shown, the above code has a simple methodology for slicing the data segment into 64KB chunks and storing the results in an array to be fetched at a later time. When we specify a Range containing a high-end value of an unsigned integer, 18446744073709551615, the slice-end will equal the same value. When the request is sent, the user process is called upon for a response. The corrupted range will return a response containing the memory dump.

Next, UlpCreateCacheRangeSliceTracker() will attempt to slice-up the response into chunks and cache it. Slicing up a value this large and attempting to store it in an array on the stack causes the stack to be corrupted. Utilizing this information to incorporate the Range header to overflow the integer variable, as well as requesting more than 256KB of data, the memory corruption was successful. Upon running the exploit, Windows 8 experienced a kernel panic. (Note: I’m utilizing VMWare Workstation to run Windows 8.1 as a virtual machine and capture the crash logs for this exercise).

Image

The crash log states a chunk of data containing an invalid PA/length attempted to write to the stack:

Image

To summarize: Influencing the Range header to exploit the integer overflow will cause the array to attempt to write higher-than-expected values in each index, and hence to memory, causing a kernel panic due to trying to access unpaged memory. We now know that we can influence the amount of data returned, as well as write to general memory regions.

The goal now is to attempt to both read data within specified memory regions as well as control exactly what data is written. We have kernel pointers which will heavily aid in bypassing ASLR as well as aid in actual exploitation. Return-oriented programming will heavily aid in bypassing multiple Windows OS exploitation preventions. The next step in exploitation is to discover a write-primitive to influence exactly what data is used to cause the overflow conditions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment