An Analysis Of MS15-034

Written on:April 18, 2015
Comments are closed


By now you’ve undoubtedly heard about MS15-034. The following is a collection of my cursory research and thoughts on this vulnerability.

In addition, here is a small list of related resources, some of which I also reference in the sections that follow:

UPDATE (4/20/2015): I was able to consistently achieve information disclosure via some additional testing with submitting multiple range values (see added section “Information Disclosure”). I’ve also added some additional information on another updated HTTP.sys function. (Updated content is annotated accordingly)

A brief overview of the vulnerability

According to the published Microsoft Security Bulletin, MS15-034 is a remote code execution vulnerability caused by HTTP.sys improperly parsing specially crafted HTTP requests. Specifically this exploit can be triggered using the Range header of an HTTP request, causing an Integer overflow. While the bulletin indicates Remote Code Execution is possible (and as such rates this vulnerability as Critical), so far I have only been able to generate a Denial of Service condition and have not yet seen any RCE code in the wild (though I’m sure someone is working hard at it). Before I get into the details of the vulnerability, let’s take a quick look at HTTP.sys and its purpose.

What is HTTP.sys?

HTTP.sys (aka the HTTP Protocol Stack) is a Windows kernel-mode device driver which, as stated in Microsoft’s Introduction to IIS Architectures, “listens for HTTP requests from the network, passes the requests onto IIS for processing, and then returns processed responses to client browsers”. It is responsible for providing Kernel-mode caching, Kernel-mode request queuing, and request pre-processing and security filtering.

Kernel-mode caching which improves performance by serving all cached content requests directly from kernel-mode, appears to be the culprit in this vulnerability. Although Microsoft does cite disabling IIS Kernel caching as a possible workaround, it may cause too much performance degradation on production web servers to be very practical.

While it’s clear that MS web servers running modern IIS versions are the primary affected group, keep in mind the the HTTP Protocol Stack implemented by Http.sys is used by more than just IIS so other applications may be affected as well.

What is the Range Header?

I mentioned earlier that this vulnerability can be triggered via the Range header. The Range header is used in an HTTP request to return a portion (or range of bytes) of a given resource. Take for example, the following requests that illustrate how to obtain a portion of Google’s humans.txt file using the Range header.


The RFC does not require clients or servers to honor/support Range headers, though most do. It also allows for various formats of byte range specifications including a single range or set of ranges such as:

  • bytes=0-499,
  • bytes=500-999,
  • bytes=-500,
  • bytes=9500-,
  • bytes=0-0,-1,
  • bytes=500-600,601-999

I mention this because as Didier Stevens pointed out in his post, if you’re building IDS/IPS rulesets for detection/prevention of this exploit you need to take into account the various allowable methods of formatting this header.

A Deeper Dive

When I was researching this vulnerability, the first resource I came across was this nice write-up from the team at BeyondTrust. I encourage you to read it, but I’ll also replicate some of their findings here.

They start by analyzing this vulnerability in much the same way that I (and probably many others) did … by diffing the old and new HTTP.sys files. Here’s a shot of mine:


This is a view of the UlpParseRange function (unpatched HTTP.sys shown on the left, patched shown on the right), which after the update, has one key difference…an additional call to RtlULongLongAdd(). Although this is a slightly modified, 5 parameter version (vs. the 3 parameter version specified on MSDN), the intent of the added function is the same…to provide a validity check to prevent against Integer overflow conditions. The absence of this check in the unpatched version of HTTP.sys is what allows for the overflow condition at the heart of MS15-034.

So you can get a little better idea how this comes into play, take a look at the following:


You can see UlpParseRange is called by HlContentRangeHeaderHandler, when it needs to process the values passed via the Range header.

Now, taking a look at the UlpParseRange, you can see the location where the missing Integer Overflow condition check should be in the vulnerable version of HTTP.sys (top) and how it was addressed in the updated version via the addition of RtlULongLongAdd:


Unpatched HTTP.sys



Patched HTTP.sys w/ RtlULongLongAdd

If we step back even further, before getting to this point, UlpParseRange makes a call to UlpParseRangeNumber, which as its name implies, parses the numbers passed via the Range Header. This latter function actually makes its own calls to RtlULongLongAdd, though they don’t serve to provide any protection against the core Integer Overflow problem.

To give you an idea of what this looks like, I set breakpoints on each of the three functions and passed a Range header value of bytes=14-156. What will happen is the breakpoint for UlpParseRange will hit once (for the single range passed via the header). Following that breakpoint, UlpParseRangeNumber will hit twice (once for each number in the range — 14 and 156). After each UlpParseRangeNumber breakpoint, RtlULongLongAdd will trigger for each digit in the respective range number. That means it will hit twice for the number 14 and three times for the number 156.

Here is what that would look like:

0: kd> g
Breakpoint 0 hit

0: kd> g
Breakpoint 1 hit

0: kd> g
Breakpoint 2 hit

0: kd> r
eax=00000001 ebx=8e2b0b08 ecx=0000000a edx=00000000 esi=00000031 edi=00000017
eip=8b176ebf esp=8e2b0aa8 ebp=8e2b0ad8 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246

0: kd> g
Breakpoint 2 hit

0: kd> r
eax=00000004 ebx=8e2b0b08 ecx=0000000a edx=00000000 esi=00000038 edi=00000016
eip=8b176ebf esp=8e2b0aa8 ebp=8e2b0ad8 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246

0: kd> g
Breakpoint 1 hit

0: kd> g
Breakpoint 2 hit

0: kd> r
eax=00000001 ebx=8e2b0b08 ecx=0000000a edx=00000000 esi=00000031 edi=00000014
eip=8b176ebf esp=8e2b0aa8 ebp=8e2b0ad8 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246

0: kd> g
Breakpoint 2 hit

0: kd> r
eax=00000005 ebx=8e2b0b08 ecx=0000000a edx=00000000 esi=00000038 edi=00000013
eip=8b176ebf esp=8e2b0aa8 ebp=8e2b0ad8 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246

0: kd> g
Breakpoint 2 hit

0: kd> r
eax=00000006 ebx=8e2b0b08 ecx=0000000a edx=00000000 esi=00000034 edi=00000012
eip=8b176ebf esp=8e2b0aa8 ebp=8e2b0ad8 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246


Once the numbers passed via the Range header are parsed by UlpParseRangeNumber, it returns to UlpParseRange, and the vulnerable portion of the function follows.


Let’s take a closer look at the above location 6EEF9 (highlighted) by passing a crafted Range header value. To demonstrate the simple mechanics of the vulnerable code, I’ll use an example range of 21845 – 18446744073709551615. The latter number is the largest possible value of a 64-bit unsigned Integer.


I chose the former simply for ease of demonstration as it equates to 5555h.

After parsing the provided Range header value, the code at loc_6EEF9 subtracts the lower range value (stored in EDI) from the upper range value (stored in EAX) and then adds 1.



By not properly checking the bounds of these values following the addition/subtraction, the codes leaves an opportunity for an overflow condition (if you’re unfamiliar with the concept of Integer Overflows you might refer to the following Wikipedia article:

UPDATE (4/20/2015)

When I posted this yesterday I neglected to mention some of the other changes made to HTTP.sys via the Microsoft update, namely UlAdjustRangesToContentSize, which as you can see below also has an additional RtlULongLongAdd validity check (right).


I noticed this come into play when I was testing the various methods of submitting the Range header (see the previous section “What is the Range Header”) and crashed the server with an input as follows:



Note the multiple byte range values passed, the first starting with 0, the second with 1, the third with 2, the fourth with 3, the fifth with 4 and the remaining all starting with 5.

Now take a look at the crash which occurs during a call to memcpy from AdjustRangesToContentSize:




Note the values pointed to by esi and edi are the lower and upper ranges provided in each of our header byte range values. This function might be a bit more interesting to explore than UlpParseRange for the purposes of exploiting information disclosure via arbitrary memory reads (see added section “Information Disclosure”)

So What?

Why is this bad? Well, according to Microsoft, this particular vulnerability can lead to remote code execution though Integer Overflows can be difficult to translate to robust/reliable RCE, I have certainly not yet been able to do so in my test environment, nor have I seen any such exploits circulating in the wild (yet). Even so, I (like many others) have been able to easily and reliably generate a denial of service condition by exploiting this vulnerability. The fact that it’s so easy to cause a DoS means you should remediate it as soon as possible.

UPDATE (4/20/2015)

With additional testing I believe I’ve achieved fairly reliable Information Disclosure, at least via a default IIS installation on a Windows 7 SP 1 machine (see below).

Identifying Vulnerable Assets

You can perform a basic check on your server(s) to determine their vulnerability to this exploit. Simply make a request for a static resource (such as an image) and pass a range header with the values bytes=0-18446744073709551615. An unpatched, vulnerable server should respond with “Requested Range Not Satisfiable”


You can use an intercepting proxy (such as Burp), curl, or a custom script to generate such a request (I’ve provided one below). Note: modifying the lower number of the range to anything other than 0 risks causing a DoS condition on the target so use with caution! Which brings me to my next topic…

Causing a DoS

You can cause a Denial of Service condition by changing the lower value of the range. For example, the common example I’ve seen floating around the interwebs is Range: bytes=18-18446744073709551615. The number 18 is somewhat arbitrary and many other values will work, though it will also depend on the size of the resource your requesting.

I wrote a simple python script to both check for the presence of the vulnerability as well as exploit the DoS condition. For demonstration purposes it simply requests the /welcome.png file common to the Standard IIS installation on Windows 7 SP 1 (though this configurable).

I wrote this days ago to test/demo the exploit so it’s completely function over form. To use it in order to check if an asset is vulnerable simply specify the target url as follows:


I’ve also included a simple flag (-e) to automatically trigger the DoS condition — using a default range of 18-18446744073709551615 and assuming the presence of /welcome.png (both of which are configurable via CL options).


As stated earlier, modifying the range header by itself can trigger a DoS so use with caution!!!


Information Disclosure

UPDATE (4/20/2015)

With additional testing I was able to consistently replicate Information Disclosure without DoS’ing the target machine (nearly all of the time) by submitting multiple byte range values and adjusting them slightly. Recall earlier that I stated the RFC allowed for multiple formats of byte ranges to be submitted. As I was testing these various formats, I noticed that by submitting multiple byte ranges I was able force the target system to consistently return its memory contents. Let me give some examples.

First, the most consistently successful method was requesting the default welcome.png file (note the submitted byte ranges):


In all cases, the requested image file is returned, but it is appended with additional data in memory. I was able to repeatedly make changes to the byte range values and resubmit without causing any service crash or disruption except for a few instances in which the server stopped responding to remote requests but remained up (I couldn’t pinpoint a cause and there were only about 3 occurrences among hundreds of requests).

I was also able to obtain similar results (though less often) by requesting html content:


In the above case, the content index.htm file was returned (“This is a test”) along with the content of other resources still loaded in memory that were accessed by different test machines to simulate multi-user traffic. One more example is below:


Again, there was much more content returned than the request resource (the root index) including a PHP page accessed by a different user and an image file loaded into memory. Requesting non-image files was hit-or-miss with the server often responding with a connection reset (this also happened from time to time with image files, though a re-request would always do the trick).

What About Internal Servers?

Whenever there’s a web-based vulnerability, the first thing that most people think to patch are their public-facing/DMZ systems…and rightly so. Naturally, it’s recommended to patch all servers but priority absolutely lies with those that are public facing. They are going to have the most exposure and therefore the greatest risk for targeting. But what about your internal servers?

You may recall a post I did in October of last year called “Phishing for Shellshock” in which I demonstrated that it was trivial to target an organization’s internal servers using a phishing attack to exploit what many were considering an “external” vulnerability. While it’s technically possible to exploit MS15-034 in a similar manner, there are some key differences that made Shellshock more susceptible.

First, several of the header(s) available for use to exploit Shellshock (Accept, Accept-Language, etc) were not protected by CORS, meaning they could be used in a CSRF-style phishing attack in which a targeted user unknowingly executes malicious JavaScript in their browser that then delivers the exploit to the servers on their internal network. In the case of MS15-034, the Range header is in fact protected by CORS so the likelihood of a successful exploit is lower.

Second, Shellshock was a bit more attractive to exploit in that manner because it resulted in remote code execution.The beauty of the Shellshock phishing exploit was the targeted user didn’t need to do any parsing of the response from the exploited server since it would call directly back out to the attacker. So far, MS15-034, only results in DoS and apparently some limited and inconsistent Information Disclosure (though I have not been able to replicate).

So does that mean internal systems are completely safe from a phishing attack? Not necessarily. It’s not uncommon to see some relaxed CORS rules on the internal network (for both clients and servers) so keep that in mind when considering your own environment. Also, although there is no published RCE yet, it doesn’t mean one doesn’t exist (though it’s not necessarily easy given this is an integer overflow).

To demonstrate how this could work, I configured a test IIS server with some insecure, unrestricted response headers:


I then created this simple web page (a modified version of my Shellshock exploit):

Should the victim visit this page (via a phishing email perhaps), they will be treated to a trailer of the new Star Wars movie. At the same time, the embedded JavaScript will kick off a series of requests to their internal network (as dictated by the provided GET parameters) attempting to exploit this Range header vulnerability.

The corresponding phishing link would be formatted as follows:

In this case, s is IP address at which to start exploiting and e3 and e4 are the values at which to stop exploiting for the third and fourth octet respectively. In the case of the above URL, the phishing target will attempt to exploit every asset from to


So how likely is this to actually be exploited on internal servers? Probably not very likely, especially if you’ve restricted CORS internally and have a trained user population that isn’t very susceptible to phishing. Either way, I think it’s important to always think of how exploits such as these could find their way into the internal network and ensure you’ve got a plan to patch all of your assets.


I hope this post provided some useful details and ideas regarding the MS15-034 vulnerability. As with any Critical, remotely executable vulnerability, I recommend you patch/mitigate as soon as possible.

Until next time,


12 Comments add one

  1. winger says:

    Very COol with Last Script!!!
    nice job man.

  2. zeroratchet says:

    File “/root/”, line 22
    res = “”

    please help??!!!…

    • Mike Czumak says:

      You didn’t provide the error so I can’t be much help. That said, if you copy/pasted you might be dealing with space/break characters.

  3. Rich says:

    Nice work, not sure why this post isn’t getting more attention.

    I’m having trouble NOT dos’ing my 2012 server though. I’m able to get a few bytes of memory out, but a subsequent request with the same ranges results in BSOD.

    When you say you can consistently NOT crash the host and dump memory, are you replaying the same request with the same byte ranges?


    • Mike Czumak says:

      Both…I’ve replayed the same request as well as changed up the byte range values, both without crashing my target machine. Again, my preliminary testing was only against a Windows 7 SP 1 machine running IIS so I haven’t had a chance to test on Server 2008 or 2012.

  4. Great published work. I would really love to see a blog post on patch diffing. There are just not many good ones around. A step by step post on discovering the lack of the integer overflow check in unpatched HTTP.sys and confirming the presence of it in HTTP.sys patched would be awesome.

    Great work!

  5. Matthew Hall says:

    Ive been looking at this on Windows 2012 – the byte ranges are evaluated in a lifo, so a request to ‘/’ will evaluate the range requested on the “first” file in that directory. I had a similar instance to Rich where I could grab (pretty much) the entirety of web.config but subsequent requests just DoS the box. /me looks intensely at this function….

  6. Matthew Hall says:

    Also, in my testing it seems the memcpy is into a 64k buffer; I’ve been getting results with 6553*6* bytes in my responses.

  7. Innokentiy says:

    Does darungrim4 work on 64bit drivers? Tried http.sys. Matched one function on 21%