February 17, 2010 at 1:16 pm #4675
Guys, I have a question about the following example from “19 Deadly Sins of Software Security”. This is the only example I’m having trouble with, I just want to make sure I fully understand.
Quoted from page 7 of the book;
The following overrun is a little more interesting:
bool CopyStructs(InputFile* pInFile, unsigned long count)
unsigned long i;
m_pStructs = new Structs[count];
for(i = 0; i < count; i++)
How can this fail? Consider that when you call the C++ new operator, it is similar to the following code:
ptr = malloc(sizeof(type) * count);
If the user supplies the count, it isn’t hard to specify a value that overflows the multiplication operation internally. You’ll then allocate a buffer much smaller than you need, and the attacker is able to write over your buffer. The upcoming C++ compiler in Microsoft Visual Studio 2005 contains an internal check to prevent this problem.
Is it because the count value that will be used to calculate the buffer’s size can be too long? Will the size of the actual buffer will be truncated to something smaller while count remains the same?
So I wrote something to test a few theories.
const unsigned long COUNT = 18446744073709551615; // Biggest possible unsigned long on my system.
char* buf = new char[COUNT];
printf("Count: %un", COUNT);
printf("Strlen: %in", strlen(buf));
unsigned long i;
for(i = 0; i < COUNT; i++)
buf = 'A';
Debugging this gives me the following error;
“error C2148: total size of array must not exceed 0x7fffffff“. The hex value of 0x7fffffffequals 2147483647 in decimal, which is the biggest possible value of a signed int.
When I change the count from 18446744073709551615 to 99999999999999999 I get the following output;
What, no errors? So obviously the new operator doesn’t like to get unsigned long values 😛 And does indeed create a smaller buffer, but ALSO adjust the value of count, so that overflows don’t occur in the for loop. Even when count was declared as constant. Is this because of the internal check the compiler performed? Will it just throw the C2148 error? Is this what the author was talking about?
Maybe I’m making things too complicated for myself :-X
Edit: Ugh, so I found out the Visual C++ compiler treats unsigned longs as 4 bytes. So they’re like unsigned ints. I can imagine the risk when you think you’re dealing with 8 bytes instead of 4.
February 25, 2010 at 2:22 am #29085partekParticipant
You may not be able to overflow a buffer in code before it is compiled. There’s a good chance that the compiler will try and fix that for you.
Most buffer overflows are triggered by input that comes from an external source such as a prompt on a commandline or commands via a network socket.
When you’re taking input from an external source, if proper bounds checking is not in place the buffer which the input is being placed into can overflow and overwrite memory near the location where the buffer was in memory.
In some cases this allows an attacker to overwrite memory in a way that can give them control over the execution path of a program.
February 25, 2010 at 11:20 am #29086
Yea it took me a while to notice that the compiler does lots of stuff I didn’t expect. I perfectly understand how buffer overflows work and how to exploit them. But there are lots of examples that don’t work anymore, because the compiler fixes them. So I usually turn off the stack protection mechanisms and other runtime checks.
My problem was that I expected the unsigned longs to be 8 bytes, as my C++ books explains. But MSDN tells me the Visual C++ compiler treats them as 4 bytes. And I didn’t really understand how the new operator allocates the memory.
Th author of the book talks about overflowing the multiplication operation of the new operator internally. So the allocated buffer will be too small. The actual size of count is still the same while the buffer is smaller than count. In this case, I understand that the loop will overwrite memory. I noticed that the new operator doesn’t allow something bigger than a 4 byte unsigned value.
Thanks for replying. So you think I understand it now?
February 25, 2010 at 3:51 pm #29087partekParticipant
I think what’s being missed here is that you’re not taking input from an external source such as a prompt, a file, or a socket.
The compiler appears to be fixing a logic error for you, which compilers are sometimes good at doing. What a compiler can’t do is protect the program from user input that it knows nothing about if the input isn’t being handled in a ‘secure’ manner.
Check out Smashing the Stack for Fun and Profit. It’s one of the best written descriptions of buffer overflows. It’s a bit linux centric, but the concepts are all the same:
February 25, 2010 at 4:15 pm #29088
Ah sweet! You were right, I had the stuff hard coded. I get different results when I feed it input from the command line. Ugh, now it feels so obvious!
I’ve already read smash the stack. I still can’t believe I missed something like this. Thanks a lot for clearing it up ;D
- You must be logged in to reply to this topic.