This post is a modified version of a post that originally appeared on Jimmy Wylie’s blog here.
Early last week, Tavis Ormandy released a new DoS vulnerability affecting the SymCrypt library. While it provided some details, I decided to look at it a little further to understand where this vulnerability occurs. We start by describing the vulnerability, then verifying that the vulnerability exists using the provided POC, and finish by looking at some other concerns for those attempting to patch the vulnerability. This is intended to be an educational article to illustrate the impact of API vulnerabilities in Windows.
Bug information can be found on Project Zero’s website here. The vulnerability affects the function
SymCryptFdefModInvGeneric in bcryptprimitives.dll, which is presumably based on the SymCrypt library found here. Given malformed input, this function will enter an infinite loop that results in a DoS in some Windows applications, possibly requiring a reboot. In some third-party applications, it may cause the application to deadlock.
Quick Aside: Mathematics
You can verify this vulnerability without knowing much about what these functions are supposed to implement, but you won’t necessarily understand why the function loops infinitely, or even perhaps how to fix it. Especially when it comes to cryptography, that’s a pretty reasonable situation to find yourself in. If you are curious about the purpose of this function and others in this library, this writeup by Conrado Gouvêa on Montgomery Reductions is a good start. If you don’t have time to read that post but are still faintly curious, the TL;DR is that calculating modulo is expensive if you use division. Thus, a real smart mathematician created a different way, called Montgomery Reductions, to calculate modulo using only multiplies and adds which are more easily optimized.
Testing the Vuln
(Note: the following was done on a Windows 10 64 bit VM).
From the vulnerability disclosure, we know both the library (bcryptprimitives.dll), and also how to test the vuln (certutil.exe). Executing the given test case by running certutil on the modinv.cer file from the vuln disclosure results in the following output:
Figure 1: Testing the Vuln with CertUtil
It tries to display part of a signature, and then hangs indefinitely. At this point, I tested the vuln inside of IDA Pro, to make sure the vulnerability is indeed inside of bcryptprimitives.dll. For more information on that see here.
API bugs are difficult
We verified that there’s a user-mode infinite loop in the bcryptprimitives.dll, and this is a big problem, to say the least. When developers write software, they rely on these DLLs and exported functions (APIs), like bcryptprimitives.dll and its parent bcrypt.dll, so that they don’t have to re-write every single piece of code needed to interact with the operating system. A Windows API is a “contract guarantee” between Microsoft and the developer community: “Trust us. We know how to do this operation. If you pass us these inputs, we promise to provide these outputs”.
Developers choose to rely on these functions, and trust that when their code executes on a Windows system, Microsoft’s implementation of an exported function to behave as promised. A library like bcrypt that implements common cryptographic operations such as signature verification is a dependency of a wide variety of software beyond just Microsoft developed code. Tavis refers to this fact in the vuln disclosure:
“Obviously, lots of software that processes untrusted content (like antivirus) call these routines on untrusted data, and this will cause them to deadlock.”
This bug isn’t just a Windows Denial of Service; it’s also potentially a denial of service against any piece of software that relies on that particular API. On the bright side, an API bug means that Microsoft only needs to fix and patch one bug, and as a result, all the dependent software should work as intended. However, in this case, there’s an extra wrinkle.
Common Source code means common problems
Microsoft Windows doesn’t just implement these cryptographic algorithms in user-mode. The kernel also exports these cryptographic operations for drivers to use. This library is documented on MSDN here. As an example API, look at BCryptVerifySignature. In the remarks section, it says:
“To call this function in kernel mode, use Cng.lib, which is part of the Driver Development Kit (DDK).”
If you look in, System32\drivers, you’ll find a corresponding cng.sys. Here are some of its functions.
SymCryptFdefModInvGeneric is exported by cng.sys. So it appears that both the user-mode and kernel-mode crypto libraries are dependent on a version of SymCrypt source (not necessarily the same version though).
I compared the kernel level implementation of
SymCryptFdefModInvGeneric with the user-mode one, and they appear to be very similar in structure. Obvious things were different such as calling KeBugCheck on failure, and not so obvious things. For example, the kernel version only uses general purpose registers, while the user-mode version uses SSE registers in certain places. It is possible that this change in implementation means that cng .sys’ version is unaffected. For lack of time, I didn’t test to see if the kernel version was explicitly vulnerable.
Microsoft, on the other hand, will almost certainly make time (at least I hope) to test for a corresponding kernel level DoS bug, as such a bug could have cascading effects on the operating system. I wouldn’t be surprised if the patch to fix this bug changes both cng.sys and bcryptprimitives.sys.
We’ve walked through how to verify the SymCrypt DoS vulnerability using the test case from the disclosure, and looked into the potential impact of such a bug. Since the bug affects a core Microsoft DLL, it has the potential to DoS not only Microsoft written software but also any third-party supplied software that relies on
bcryptprimitives!SymCryptFdefModInvGeneric. Since cng.sys also relies on a version of SymCrypt source code, there exists the potential for a kernel-mode vulnerability in
cng!SymCryptFdefModInvGeneric. More than likely, Microsoft is testing this possibility as well, and if no public reports are released testing this possibility, we’ll be able to tell when the July patch gets released.