Hi, Michael here.
No doubt you are aware of the out-of-band security bulletin issued by the Microsoft Security Response Center today, and like all security vulnerabilities, this is a vulnerability we can learn from and, if necessary, can use to shape future versions of the Security Development Lifecycle (SDL).
Before I get into some of the details, it’s important to understand that the SDL is designed as a multi-pronged security process to help systemically reduce security vulnerabilities. In theory, if one facet of the SDL process fails to prevent or catch a bug, then some other facet should prevent or catch the bug. The SDL also mandates the use of security defenses, because we know full well that the SDL process will never catch all security bugs. As we have said many times, the goal of the SDL is to “Reduce vulnerabilities, and reduce the severity of what’s missed.”
In this post, I want to focus on the SDL-required code analysis, code review, fuzzing and compiler and operating system defenses and how they fared.
Code Analysis and Review
I want to start by analyzing the code to understand why we did not find this bug through manual code review nor through the use of our static analysis tools. First, the code in question is reasonably complex code to canonicalize path names; for example, strip out ‘..’ characters and such to arrive at the simplest possible directory name. The bug is a stack-based buffer overflow inside a loop; finding buffer overruns in loops, especially complex loops, is difficult to detect with a high degree of probability without producing many false positives. At a later date I will publish more of the source code for the function.
The loop inside the function walks along an incoming string to determine if a character in the path might be a dot, dot-dot, slash or backslash and if it is then applies canonicalization algorithms.
The irony of the bug is it occurs while calling a bounded function call:
_tcscpy_s(previousLastSlash, pBufferEnd – previousLastSlash, ptr + 2);
This function is a macro that expands to wcscpy_s(dest, len, source); technically, the bug is not in the call to wcscpy_s, but it’s in the way the arguments are calculated. As I alluded to, all three arguments are highly dynamic and constantly updated within the while() loop. There is a great deal of pointer arithmetic in this loop. Without going into all the gory attack details, given a specific path, and after the while() loop has been passed through a few times, the pointer, previousLastSlash, gets clobbered.
In my opinion, hand reviewing this code and successfully finding this bug would require a great deal of skill and luck. So what about tools? It’s very difficult to design an algorithm which can analyze C or C++ code for these sorts of errors. The possible variable states grows very, very quickly. It’s even more difficult to take such algorithms and scale them to non-trivial code bases. This is made more complex as the function accepts a highly variable argument, it’s not like the argument is the value 1, 2 or 3! Our present toolset does not catch this bug.
Ok, now I’m really going out on a limb with this next section.
Over the last year or so I’ve noticed that the security vulnerabilities across Microsoft, but most noticeably in Windows have become bugs of a class I call “onesey – twosies” in other words, one-off bugs. There is a good side and a bad side to this. First the good news; I think perhaps we have removed a good number of the low-hanging security vulnerabilities from many of our products, especially the newer code. The bad news is, we’ll continue to have vulnerabilities because you cannot train a developer to hunt for unique bugs, and creating tools to find such bugs is also hard to do without incurring an incredible volume of false positives. With all that said, I will add detail about one-off bugs to our internal education; I think it’s important to make people aware that even with great tools and great security-savvy engineers, there are still bugs that are very hard to find.
I’ll be blunt; our fuzz tests did not catch this and they should have. So we are going back to our fuzzing algorithms and libraries to update them accordingly. For what it’s worth, we constantly update our fuzz testing heuristics and rules, so this bug is not unique.
If you want the full details of the defenses, and how they come into play on Windows Vista and Windows Server 2008, I urge you to read the SVRD team’s in-depth analysis once it is posted.
A big focus of the SDL is to define and require defenses because we have no illusions about finding or preventing all security vulnerabilities by attempting to get the code right all the time, because no-one can do that. No one. See my comment above about one-off bugs!
Let’s look at each SDL mandated requirement and how they fared in light of this vulnerability.
The -GS story is not so simple. A lot of code is executed before a cookie check is made and the attacker can control the overflow because the overflow starts at an offset before the stack buffer, rather than at the stack buffer itself. So the attacker can overwrite other frames on the call stack, corresponding to functions that return before a cookie check is made. That’s a long way of saying that -GS was not meant to prevent this type of scenario.
ASLR and NX
The code fully complies with the SDL, and is linked with /DYNAMICBASE and /NXCOMPAT on Windows Vista and Windows Server 2008. There are great defenses when used together, and reduce the chance of a successful attack substantially. Also, the stack offset is randomized too, making a deterministic attack even more unlikely.
Service Restart Policy
By default the affected service is marked to restart only twice after a crash on Windows Vista and Windows Server 2008, which means the attacker has only two attempts to get the attack right. Prior to Windows Vista, the attacker has unlimited attempts because the service restarts indefinitely.
Thanks to mandatory integrity control (MIC) settings (which comes courtesy of UAC) the networking endpoint that leads to the vulnerable code requires authentication on Windows Vista and Windows Server 2008 by default. Prior to Windows Vista, the end point is always anonymous, so anyone can attack it, so long as the attacker can traverse the firewall. This is a great example of SDL’s focus on attack surface reduction; requiring authentication means the number of attackers that can access the entry point is dramatically reduced.
We enabled the firewall by default in Windows XP SP2 and later, this was a direct learning from the Blaster worm. By default, ports 139 and 445 are not opened to the Internet on Windows XP SP2, Windows Vista and Windows Server 2008.
The $64,000 question we ask ourselves when we issue any bulletin is “did SDL fail?” and the answer in this case is categorically “No!” No because as I said earlier the goal of the SDL is “Reduce vulnerabilities, and reduce the severity of what you miss.” Windows Vista and Windows Server 2008 customers are protected by the defenses in the operating system that have been crafted in part by the SDL. The development team who built the affected component compiled and linked with the appropriate settings as described in “Windows Vista ISV Security” and Writing Secure Code for Windows Vista so that their service is protected by the operating system.
The team did not poke holes through the firewall unnecessarily, in accordance with the SDL.
The team reduced their attack surface, in accordance with the SDL, by requiring authenticated connections rather than anonymous connections by default.
We know that the SDL-mandated -GS has very strict heuristics so some functions are not protected by a stack cookie, but in this case, there is no buffer on the stack, so there will be no cookie. We know this. There are no plans to remedy this in the short term.
Fuzzing missed the bug, so we will update our fuzz testing heuristics, but we continually update our fuzzing heuristics anyway.
In short, based on what we know right now, Windows Vista and Windows Server 2008 customers are protected because of the SDL-mandated defenses in the operating system, and because the development team adhered to the letter of the SDL to take advantage of those defenses.
Chalk one up for Windows Vista and later and the SDL!
As usual, questions and comments are very welcome.