In the previous section, we covered fixed length strings. However, many protocols do not want to limit the maximum length of certain data items a priori. These protocols commonly represent variable length data such as arrays or strings as an integer (the length of the array or string), followed by the data proper.
Here's a typical code snippet that looks innocent but is badly broken:
char *
read_string(int fd)
{
char buffer[1024];
int count;
/* read string length */
if (read(fd, &count, 4) != 4) {
log("read failed");
return NULL;
}
/* convert to host byte order */
count = ntohl(count);
/* here's the bug */
if (count > sizeof(buffer)-1) {
log("string too large");
return NULL;
}
/* read string data */
if (read(fd, buffer, count) != count) {
log("read failed");
return NULL;
}
buffer[count-1] = '\0';
return strdup(buffer);
}
What's wrong with this code? The first problem is of course the
implicit assumption that a variable of type int is 32 bits wide,
which is not true for all hardware platforms. However, that is hardly
security relevant; this simply means that the application will not
work correctly on these platforms.
The real problem is that count is a signed variable. Assume an
attacker sends a length of 0xFFFFFFFF. When this value is assigned
to the count variable, which is a signed integer, it effectively
``becomes'' -1. This will of course pass the string length test because
-1 is definitely less than sizeof(buffer)-1. However, the
read call will interpret count as an unsigned number,
4294967295. Now all the attacker needs to do is send 2048 worth of data
(containing an off-the-shelf buffer overflow exploit), and close the
connection. read will dutifully copy as much data as it can get,
i.e. 2048 bytes, which will overflow buffer and clobber
the return address. The subsequent return from the function will jump
straight into the exploit. Strike!
An even more bizarre problem was discovered by the Bindview RAZOR team in Windows NT's NTLM Security Service Provider, which provides much of the security infrastructure for NT's LanManager authentication service. Clients send requests to this service, specifying the function they want to invoke as a 32bit word inside the packet. The server uses this value as an index into an array of function pointers, and invokes the specified function. Unfortunately, it does not check for negative indices, which allows attackers (with the aid of some other bizarre features of this service) to run arbitrary shell commands on the victim's host.7.6
This type of bug (which is only recently being given due attention) has
a very simple fix. Simply make sure that the count variable is an
unsigned quantity - in this case, you would use the type u_int32_t
on Linux.
Because these bugs are caused by mixing up signed and unsigned integer handling, they are frequently referred to as signedness bugs.