the lea edi,[eax+0x18]). The return value is the pointer of the newly allo-
cated block. Clearly, the idea is that an object is being allocated with a 24-bytes-
long buffer. The buffer is being zero initialized, except for the first member at
offset +0, which is set to the total size of the buffer allocated. The user-supplied
buffer is then placed after the header in the newly allocated block.
At first glance, this code appears to be perfectly safe because the function
only writes as many bytes to the allocated buffer as it managed to allocate. The
problem is that, as usual, we’re dealing with values coming in from the outside
world; there’s no way of knowing what we’re going to get. In this particular
case, the problem is caused by the arithmetic operation performed on the
buffer length parameter.
The lea esi,[edi+0x18]at address 00401027 seems innocent, but what
happens if EDIcontains a very high value that’s close to 0xffffffff? In such
a case, the addition would overflow and the result would be a low positive num-
ber, possibly lower than the length of the buffer itself! Suppose, for example, that
you feed the function with 0xfffffff8as the buffer length. 0xfffffff8 +
0x18= 0x100000010, but that number is larger than 32 bits. The processor is
truncating the result, and you end up with 0x00000010.
Keeping in mind that the buffer length copied by the function is the original
supplied length (before the header length was added to it), you can now see how
this function would definitely crash. The malloccall will allocate a buffer of
0x10bytes long, but the function will try to copy 0xfffffff8bytes to the
newly allocated buffer, thus crashing the program.
The solution to this problem is to take a limited-sized input and make sure
that the target variable can contain the largest possible result. For example,
assuming that 16 bits are enough to represent the user buffer length; simply
changing the preceding program to use an unsigned shortfor the user
buffer length would solve the problem. Here is what the corrected version of
this function looks like:
allocate_object:
00401024 push esi
00401025 movzx esi,word ptr [esp+0xc]
0040102a push edi
0040102b lea edi,[esi+0x18]
0040102e push edi
0040102f call Chapter7!malloc (004010dc)
00401034 pop ecx
00401035 xor ecx,ecx
00401037 cmp eax,ecx
00401039 jnz Chapter7!allocate_object+0x1b (0040103f)
0040103b xor eax,eax
0040103d jmp Chapter7!allocate_object+0x43 (00401067)
0040103f mov [eax+0x4],ecx
00401042 mov [eax+0x8],ecx
00401045 mov [eax+0xc],ecx
Auditing Program Binaries 259