For instance, one way to represent strings (escpecially if the string isn't supposed to exceed a certain length) is to specify in the protocol that the string is alway encoded as a fixed number of bytes, and if it's shorter, the rest is padded out with zero bytes.
Which brings us to our first example, a bug that was present in early versions of the BOOTP server. The BOOTP protocol allows clients to automatically discover configuration information at boot time.7.4 The simplest case is for clients to broadcast a packet containing just their hardware address, and the server looking up all the client's information based on this address. But there are more complex scenarios in which the client provides the server with some hints about what type of configuration they need. One such hint is the name of the boot file that tells diskless clients7.5 what kernel image to download.
XXX: BOOTP is a good example of a network service that is useful but trusts the local network layer
The BOOTP packet header looks roughly like like this:
struct bootp {
/* various fields */
...
char bp_file[64];
char bp_vend[128];
};
The server code that dealt with incoming BOOTP requests looked something like this:
struct bootp *bp;
char buffer[4196];
char path[1024];
int n;
if (read(socket, buffer, sizeof(buffer)) < 0)
return;
bp = (struct bootp *) buffer;
... lots of exciting stuff ...
if (bp->bp_file[0]) {
strcpy(path, bp->bp_file);
... more exciting stuff ...
}
This code looks harmless enough; after all, according to the definition
of the BOOTP header, the bp_file field can be 64 characters at
most, so there should be plenty of room in the 1024 byte buffer, right?
The problem though is that all the header declaration says is that at
offset so-and-so, there are 64 bytes reserved where a string can go.
In the C programming language, strings must be terminated by the
NUL character. There's nothing in the world that keeps an attacker
from sending a packet that looks like a sane BOOTP request up to
the bp_file offset, and starting at the offset contains 2000
consecutive A's without a single NUL byte inside. Or, rather than 2000 A's,
include a standard stack smashing buffer overflow exploit.
There are several ways to fix this problem. One is to check the contents
of bp_file and drop the packet if there's no NUL byte within
the 64 byte array. Another is to just make sure we don't copy more
bytes to the path buffer than it's able to hold, as shown here:
strncpy(path, bp->bp_file, sizeof(path));
path[sizeof(path)-1] = '\0';
The second statement is required because contrary to common belief, strncpy does not add a NUL byte to the resulting string if the source string is longer than the indicated maximum.
The second solution is the one that most people implement when dealing with potential buffer overflow problems, and it's practical in most cases. I tend to prefer the first, however, because you may not catch all uses of data from packet. For instance, there's the RWHO protocol, used to broadcast the list of active users to machines on the local network. The rwhod daemon processes incoming packets and stores the raw packet in a file that can be displayed by users using the rwho utility. In this case, it is clearly preferable for the server process to drop bogus packets right from the start rather than having to deal with these issues inside the client utilities as well.