While browsing the Rotor sources yesterday, I noticed something that looked like a potential security issue. After writing some test code I confirmed that it was indeed a problem. Like last time, it's a bug that allows you to compromise type safety.
Previously I promised to write more about the issue after the fix was released, but I never got around to it, partly because security issues aren't very exciting anymore after they've been fixed.
BTW, my "no Microsoft bug filing policy" doesn't apply to security issues, so I've notified the Microsoft Security Response Center of the issue.
Anyway, I thought this would be a good opportunity to look at the previously fixed issue and demonstrate how a type safety hole leads to arbitrary code execution and makes it trivial to bypass both DEP and ASLR.
Discovering the Bug
This is the hard part. Contrary to popular belief, Microsoft writes pretty secure code nowadays. I found the issue because an IKVM user reported a problem with some code that worked with JIT optimizations disabled, but mysteriously failed when JIT optimizations were on. Debugging this issue led to misbehaving code similar to this:
if (arr1[index * 3 + 5] != null)
Union1 u1 = arr1[index * 3 + 5];
Due to a JIT bug the second array indexing expression was incorrectly applied, resulting in the ability to read a value outside of the array bounds.
Due to the predictability of memory allocation in managed code, it is easy to allocate two arrays of different types and then use the above bug to access an element from one array through a reference to the other array. This gives you the ability to perform a cast that otherwise wouldn't be allowed.
Once you have this ability, it can be easily abused. For example, you could create a class like this:
public int arrayLength;
public int stringLength;
public char ch1;
public char ch2;
If you now obtain a reference typed as
StringHack to a real string object, you have the ability to alter the contents of the string (well, the first two characters in this example).
However, it's not just the .NET access restrictions that can be bypassed, you can also use this trick to execute arbitrary machine code.
Next time we'll look at a PoC that, given a type safety hole, will allow you to call WinExec to start any application from partially trusted .NET code.