A story of win32k!cCapString, or unicode strings gone bad

In the most recent blog post (“Fun facts: Windows kernel and guard pages”), we have learned how the code coverage of kernel routines referencing user-mode memory can be determined by taking advantage of the fact that kernel-mode code triggers guard page exceptions in the same way as user-mode does. Today, I will present how the trick can be used in a practical attack against an actual 0-day vulnerability in the Windows kernel. Don’t get too excited though – the bug is a very peculiar type of an information disclosure class, not particularly useful in any sort of real-life attack. Despite being of minimal severity due to the extent of information the bug makes it possible to leak, it makes a great example of how the misuse of the UNICODE_STRING structure and related kernel routines can lead to opening up a security loophole in the operating system.

Microsoft has been aware of the issue for over 4 months, but due to its low severity, I believe it is rather unlikely it will be fixed any time soon.

Unicode string security

All of the most recent versions of the Windows operating system (starting with Windows 2000) internally handle textual strings using the UTF-16 encoding and a UNICODE_STRING structure defined as follows:

typedef struct _LSA_UNICODE_STRING {
  USHORT Length;
  USHORT MaximumLength;
  PWSTR  Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;

Together with the structure definition, both user- and kernel-mode API interfaces provide a set of functions designed to initialize, examine, compare and otherwise operate on unicode strings, e.g. RtlInitUnicodeString, RtlAnsiStringToUnicodeString or RtlAppendUnicodeStringToString. In the above listing, the Length field represents the current length of the string in bytes (it must be a multiplicity of two due to the encoding used), MaximumLength indicates the total capacity of the buffer in bytes and Buffer points to the actual data. Note that both Length and MaximumLength fields are only 16-bits long, which is by far enough to accommodate the size of any string used during normal operation of the system. Perhaps contrary to intuition, the relatively limited ranges of the integers (making it possible to easily craft a string of a maximum size) do not pose any serious threat with regards to integer arithmetic, because overflowing either fields doesn’t give the attacker much edge. If you think about it, getting Length to be smaller than the actual length of the string can only lead to overwriting the existing buffer contents, but will never result in writing past the pool allocation. Similarly, setting MaximumLength to an overly small number only puts a more strict limitation on how many bytes can be stored in the corresponding buffer, or causes all subsequent calls to fail due to an invalid Length > MaximumLength condition. As a consequence, integer overflows are not of much interest in this context.

Read more

Fun facts: Windows kernel and guard pages

It has been a while since I last posted here, so I guess it’s high time to get back to work and share some more interesting Windows kernel internals goodies. Before we get to that, however, let’s start with a few announcements. First of all, there is a number of great infosec conferences coming up and it just so happens that I have been accepted to speak at a few of them. The list includes:

  • SyScan in Singapore (22-26th of April) with Gynvael Coldwind: “Bochspwn: Exploiting Kernel Race Conditions Found via Memory Access Patterns”. We will be discussing a personal project of ours that led to the discovery of around 50 local vulnerabilities in the Windows kernel, some of them already patched in the February and April security bulletins (ms13-016, ms13-017, ms13-031, ms13-036).
  • NoSuchCon in Paris (15-17th of May): “Abusing the Windows Kernel: How to Crash an Operating System With Two Instructions”
  • SEConference in Cracow (24-25th of May): “Bezpieczeństwo jądra Windows, lub jak zabić system dwoma instrukcjami”
  • CONFidence in Cracow (28-29th of May) with Gynvael Coldwind: “Beyond MOV ADD XOR – the unusual and unexpected in x86”
  • BlackHat US in Las Vegas (27th of July – 1st of August) with Gynvael Coldwind: TBA (not yet accepted, but going there either way)

If you are planning to attend any of the above, feel free to ping me so that we can have a beer or two. Otherwise, I will be posting the slides / whitepapers on the blog directly following my presentation at each event, in case you’re interested in the materials.

In other news, last Tuesday Microsoft unexpectedly patched the NTFS.sys device driver vulnerability that we described several months ago in the “Introducing the USB Stick of Death” blog post, despite making a prior claim that vulnerabilities which require Administrative and/or physical access to the victim machine are out of scope and are not considered security issues. While this is surprising in itself, the ms13-036 bulletin containing the fix has apparently broken a lot of Windows-driven platforms, to the point where the vendor removed the specific 2823324 update from the download center entirely. Such situations are uncommon to see, and this particular one is most likely evidence of how third-party software can rely on unsupported or internal OS behavior. I would be really interested to read a post-mortem, but I guess a binary diff of the old and new (if one comes out) patch will have to do. Stay tuned for the analysis.

Now, let’s get to the merit of this post.

Read more