The problems in C are not confined to just the language. Some routines in the standard library have
unsafe semantics. This was dramatically demonstrated in November 1988 by the worm program that
wriggled through thousands of machines on the Internet network. When the smoke had cleared and the
investigations were complete, it was determined that one way the worm had propagated was through a
weakness in the finger daemon, which accepts queries over the network about who is currently logged
in. The finger daemon, in.fingerd, used the standard I/O routine gets().
The nominal task of gets() is to read in a string from a stream. The caller tells it where to put the
incoming characters. But gets() does not check the buffer space; in fact, it can't check the buffer
space. If the caller provides a pointer to the stack, and more input than buffer space, gets() will
happily overwrite the stack. The finger daemon contained the code:
main(argc, argv)
char *argv[];
{
char line[512];
...
gets(line);
Here, line is a 512-byte array allocated automatically on the stack. When a user provides more
input than that to the finger daemon, the gets() routine will keep putting it on the stack. Most
architectures are vulnerable to overwriting an existing entry in the middle of the stack with something
bigger, that also overwrites neighboring entries. The cost of checking each stack access for size and
permission would be prohibitive in software. A knowledgeable malefactor can amend the return
address in the procedure activation record on the stack by stashing the right binary patterns in the
argument string. This will divert the flow of execution not back to where it came from, but to a special
instruction sequence (also carefully deposited on the stack) that calls execv() to replace the
running image with a shell. VoilĂ , you are now talking to a shell on a remote machine instead of the
finger daemon, and you can issue commands to drag across a copy of the virus to another machine.
0 comments: