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 = %x\n", sizeof(david));
printf("sizeof
david’s copper.field1 = %x\n", 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 despite any 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.
No comments:
Post a Comment