Application Failure Attacks

Application Failure Attacks

DoS attacks that result in application failure are almost always code quality issues. Some of the most well known of these have worked against networking stacks. An early example of this was the User Datagram Protocol (UDP) bomb that would bring down certain SunOS 4.x systems. If you built a UDP packet so that the length specified in the UDP header exceeded the actual packet size, the kernel would cause a memory access violation and panic UNIX systems panic, Windows systems blue screen or bugcheck followed by a reboot.

A more recent example is the Ping of Death, which has an interesting cause that has to do with some problems in how IP headers are constructed. Here's what an IPv4 header looks like:

struct ip_hdr { unsigned char ip_version:4, ip_header_len:4; unsigned char ip_type_of_service; unsigned short ip_len; unsigned short ip_id; unsigned short ip_offset; unsigned char ip_time_to_live; unsigned char ip_protocol; unsigned short ip_checksum; struct in_addr ip_source, ip_destination; };

The ip_len member yields the number of bytes that the whole packet contains. An unsigned short can be at most 65,535, so the whole packet can contain 65,535 bytes at maximum. The ip_offset field is a little strange it uses three bits to specify the fragmentation behavior. One bit is used to determine whether the packet is allowed to be fragmented, and another specifies whether more fragments follow. If none of the bits are set, either the packet is the last of a set of fragmented packets or there isn't any fragmentation. We have 13 bits left over to specify the offset for the fragment. Because the offset is in units of eight bytes, the maximum offset occurs at 65,535 bytes. What's wrong with this? The problem is that the last fragment can be added to the whole packet at the last possible byte that the whole packet should contain. Thus, if you write more bytes at that point, the total length of the reassembled packet will exceed 2^16.

More Info
If you're interested in exactly how the Ping of Death exploit works, one of the original write-ups can be found at http://www.insecure.org/sploits/ping-o-death.html. Although accounts of which systems were vulnerable vary, the issue was discovered when someone found that typing ping -l 65510 your.host.ip.address from a Microsoft Windows 95 or Microsoft Windows NT system would cause a wide variety of UNIX systems, including Linux, and some network devices to crash.

How do you protect yourself from this type of mistake? The first rule is to never ever trust anything that comes across the network. Writing solid code, and thoroughly testing your code is the only way to defeat application crashes. Also remember that many DoS attacks that cause crashes are really cases in which arbitrary code might have been executed if the attacker had spent a little more time. Here's a code snippet that illustrates this problem:

/* Example of a fragment reassembler that can detect packets that are too long */ #include <winsock2.h> #include <list> using namespace std; //Most fragment reassemblers work from a linked list. //Fragments aren't always delivered in order. //Real code that does packet reassembly is even more complicated. struct ip_hdr { unsigned char ip_version:4, ip_header_len:4; unsigned char ip_type_of_service; unsigned short ip_len; unsigned short ip_id; unsigned short ip_offset; unsigned char ip_time_to_live; unsigned char ip_protocol; unsigned short ip_checksum; struct in_addr ip_source, ip_destination; }; typedef list<ip_hdr> FragList; bool ReassemblePacket(FragList& frags, char** outbuf) { //Assume our reassembler has passed us a list ordered by offset. //First thing to do is find out how much to allocate //for the whole packet. unsigned long packetlen = 0; //Check for evil packets and find out maximum size. unsigned short last_offset; unsigned short datalen; ip_hdr Packet; //I'm also going to ignore byte-ordering issues - this is //just an example. //Get the last packet. Packet = frags.back(); //Remember offset is in 32-bit multiples. //Be sure and mask out the flags. last_offset = (Packet.ip_offset & 0x1FFF) * 8; //We should really check to be sure the packet claims to be longer //than the header! datalen = Packet.ip_len - Packet.ip_header_len * 4; //Casting everything to an unsigned long prevents an overflow. packetlen = (unsigned long)last_offset + (unsigned long)datalen; //If packetlen were defined as an unsigned short, we could be //faced with a calculation like this: //offset = 0xfff0; //datalen = 0x0020; //total = 0x10010 //which then gets shortened to make total = 0x0010 //and the following check always returns true, as an unsigned //short can never be > 0xffff. if(packetlen > 0xffff) { //Yech! Bad packet! return false; } //Allocate the memory and start reassembling the packet. //... return true; }

Following is another code snippet that illustrates another type of problem: inconsistencies between what your structure tells you to expect and what you've really been handed. I've seen this particular bug cause lots of mayhem in everything from Microsoft Office applications to the core operating system.

/*Second example*/ struct UNICODE_STRING { WCHAR* buf; unsigned short len; unsigned short max_len; }; void CopyString(UNICODE_STRING* pStr) { WCHAR buf[20]; //What's wrong with THIS picture? if(pStr->len < 20) { memcpy(buf, pStr->buf, pStr- >len * sizeof(WCHAR)); } //Do more stuff. }

The most obvious bug you might notice is that the function isn't checking for a null pointer. The second is that the function just believes what the structure is telling it. If you're writing secure code, you need to validate everything you can. If this string were passing in by a remote procedure call (RPC), the RPC unmarshalling code should check to see that the length that was declared for the string is consistent with the size of the buffer. This function should at least verify that pStr->buf isn't null. Never assume that you have a well-behaved client.



Writing Secure Code
Writing Secure Code, Second Edition
ISBN: 0735617228
EAN: 2147483647
Year: 2001
Pages: 286

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net