INSIGHTS | May 3, 2012

Enter the Dragon(Book), Pt 2

Nobody has been able to find this backdoor to date (one reason I’m talking about it).

While the C specification defines many requirements, it also permits a considerable amount of implementation-defined behavior (even though it later struck me as odd that many compilers could be coerced into generating this backdoor in an identical way).

 

From the C specification; Environmental Considerations, Section 5.2—in particular section 5.2.4.1 (Translation limits)—seems to offer the most relevant discussion on the topic.

 

Here’s a concise/complete example:
typedef struct _copper
{
  char field1[0x7fffffff];
  char field2[0x7fffffff];
  char pad0;
  char pad1;
} copper, *pcopper;
int main(int argc, char **argv)
{
    copper david;
    printf(“sizeof david = %xn”, sizeof(david));
    printf(“sizeof david’s copper.field1 = %xn”, sizeof(david.field1));
       if(argc > 1 && strlen(argv[argc-1]) < sizeof(david.field1))
              strncpy_s(david.field1, argv[argc-1], sizeof(david.field1));
    return 0;
}
What is the expected size of david?
What is the expected size of the copper.field?
Here’s the compiled output:
sizeof david = 1
sizeof david.copper.field1 = 7fffffff
W0W!! The sum of the parts is GREATER than the whole!
It would seem that a (somewhat) correct check for length (let’s forget about the NULL and strncpy_s for portability/readability) is always going to pass since field1’s length is VERY large; however, the storage for this type is allocated with sizeof(copper) (statically or dynamically). This means that we can arbitrarily write into memory despiteany amount of bounds checking!
So, what we have is the sizeof operator failing due to the arrangement of this struct, which violates the environmental limits of C.
This struct actually contains numerous variations and interesting vectors. For instance, I’ve found _MANY_ type’s defined in the SDK of both operating systems and compilers—if you surreptitiously #define (actually redefine) an existing constant, you can exploit existing code.
The situation here is that it’s virtually impossible to detect this backdoor.
I’ve attempted to detect the flaw with all sorts of code checking tools, all of which are blind to this attack.  It seems that this overflow occurs statically, which is why sizeof is failing. I’ve been calling this a “static overflow,” which may or may not be a good name, but it seems to fit given that the overflow happens during compilation (AST formulation).
Possible attack vectors include: (1) untrusted .c/.h files in your compiler’s path, (2) environment (set CARGS=/DMAXPATH=0x7fffffff), (3) arguments, and (4) flags.
This may seem a relatively small surface area, but in any modestly-complex application, hundreds/thousands of header files are included from untrusted sources.
I’ve had many crashes in cc/ld. I anyone finds a way to exploit the actual compilation (take control of the cc/ld process) that would be pretty neat. Some of the more aggressive faults tend to occur when the compiler looks up instructions to address the oversized region, or when this type is used in more elaborate loop/indexed [array].foo[bar] arrangements.
I hope you all enjoyed this magic trick.