From lazowska@cs.washington.edu Thu Mar 5 14:02:50 PST 1998 Article: 3930 of uw-cs.ugrads Xref: uw-beaver uw-cs.ugrads:3930 Newsgroups: uw-cs.ugrads Path: uw-beaver!news From: lazowska@cs.washington.edu Subject: Windows IP bug -- 1st of 2 messages Sender: news@beaver.cs.washington.edu (USENET News System) Organization: Computer Science & Engineering, U of Washington, Seattle Message-ID: Date: Thu, 5 Mar 1998 20:49:05 GMT This information comes from Jan Sanislo, a member of the lab staff: ======== > The recent network attack that caused the Windows/NT machines to crash > was based on two principles: > > 1) The Internet Protocol (IP) used to transmit network messages > is insecure. That is, anyone connected to the internet can > send any kind of message at any time and make it look like that > message came from anywhere. > > 2) Lack of defensive programming practices in IP implementations > that make the implementation vulnerable to (1). > > This particular attack, generally labelled ``teardrop2'', is of a > general > class that exploits bugs in the IP fragment reassembly routines of > some IP > implementations. Unfortunately we don't have NT source code so we > cannot > know *exactly* the bug the teardrop2 tickled; we just know what the > packets of death looked like and have to interpolate from there. > > For purposes of our discussion, IP can be viewed as low-level protocol > that > is responsible for transmitting ``large'' messages through ``small'' > networks. That is, you may want to send a message that is 3000 bytes > long, > but the physical network (e.g. ethernet) is only capable of sending > 1500 > bytes at a time. The sending IP will fragment the original message in > two > 1500 byte (or less) packets and send each packet separately. The > receiving > IP will coalesce ("reassemble") the two packets into a single 2000 > byte > buffer and then hand the complete message to the next higher protocol > layer > such as UDP or TCP. > > Fragmentation is accomplished in the following manner: Each IP packet > that > constitutes the original message contains 3 things: > 1) An identifier. All fragments of message have the same > identifier. > 2) A length. This is the length of the *current* packet. > 3) An offset. This gives the offset (measured in units of 8 bytes) > in the > *original* message of the current packet. > 4) A fragment flag. A value of 1 indicates that more packets follow > the current packet. A value of 0 means this packet is the last > in > the fragment series. > Thus in our example the 2000 byte message would be fragmented into > two packets: > > Packet 1: Packet 2: > Id: 0xf00 0xf00 > Length: 1496 504 > Offset: 0 187 > FragFlg: 1 0 > > Reassembly is accomplished (to a first approximation) in the following > manner: When a packet arrives check the identifier (actually a > combination > of src and dst addresses and packet id). If we have not seen this > particular > chain before create a datastructure ("reassembly queue") to hold the > fragments until all of them (subject to timeout) arrive. Otherwise > just add the fragment to the existing queue. You can't do much else, > since fragments are not guaranteed to arrive in the order they were > sent; indeed, some implementations transmit the logically last > fragment > first. The queue is usually sorted by the offset field of each > packet. > Note that you don't know the total length of the message until the > last packet (FragFlg = 0) is received; the total length is then > inferred > from the fragment offset + size of that packet. You know you have > a complete message when there are no "holes" in reassembly queue. It > is > important to note that the reassembly queue must be capable of > handling > out of order and retransmitted packets. These latter may in fact be > different from the original packets, with different sizes and offsets. > > One standard way of doing this is to have the reassembly queue > structure > maintain fields that contain offset, size and end information from the > original packets. When a complete message is received, the total size > is calculated, a single large buffer allocated and then the data is > copied into the single buffer via a loop that looks like: > > for each fragment structure fp do > memcpy(bigbuffer_start+fp.offset, fp.data, > fp.length) > release(fragment) > > As this point, we now enter into speculation since we can't see the NT > reassembly code. But we have seen other code (e.g. Linux) which is > similar and, unpatched, is subject to the same style of attack. > > The teardrop2 attack consisted of two specially constructed packets > with > the following format: > > Packet 1: Packet 2: > Id: 0x455 0x455 > Length: 36 3 > Offset: 0 4 > FragFlg: 1 0 > > Now there is something peculiar here right away. Normally you would > not expect to see the last packet overwriting data already sent, which > this packet does since its data offset is in the previous packet. > Secondly, in a proper sequence we should be able to compute the total > message size as (last packet offset*8)+last packet data length. > However > we have already received 36 bytes of data and the last packet > calculation > gives 35 bytes. > > Nevertheless, when packet 2 arrives we compute its starting and ending > position in the original message, which are 4*8 = 32, the offset in > bytes > and 32+3 = 35, the offset plus packet length. This overlaps data in > packet > 1, which has offset 0 and end 36. So we need to realign things so > that > overlaps are eliminated. A bad implementation will do this in such a > manner that the actual data length in the packet will be ignored. > The overlap will be computed as > end of packet 1 data - offset of packet 2 = 36 - 32 = 4 > Then we bump the offset of the reassembly (i.e. the offset that > goes into the secondary fragment structure mentioned above) by 4 to > get > rid of the overlap. Notice two things: > 1) We *assume* that packet contains enough data to ``cover'' the > overlap of 4, even though it only has 3 bytes of data. > 2) We *assume* that the second packet is a retransmission repeating > the original data, which allows us to do the overlap trick in > the first place. > So we set the offset of the reassembly descriptor to be original > offset+overlap = 32+4 = 36. The end location of fragment is just > the original end position calculated above, 35. Finally, we make > the fatal *assumption* that the usable length of the data of the > datagram is end - new offset = 35 -36 = -1. At this point we are > in real trouble since a negative fragment length is considered > a "can't happen" and in such implementations is even defined as > being an unsigned quantity. Depending on the vagaries of the > code we may wind up asking for huge amounts of memory or trying to > copy > huge amounts of non-existant data from one place to another. > > No well-behaved IP implementation would send packets such as those > described above. OTOH, there are no network police that prevent > them from being sent. So the moral of the story is more or less: > the cycles eaten by sanity checking code will never cost you as > much time as the crash that happens because they aren't there. From lazowska@cs.washington.edu Thu Mar 5 14:03:07 PST 1998 Article: 3931 of uw-cs.ugrads Xref: uw-beaver uw-cs.ugrads:3931 Newsgroups: uw-cs.ugrads Path: uw-beaver!news From: lazowska@cs.washington.edu Subject: Windows IP bug -- slight addendum Sender: news@beaver.cs.washington.edu (USENET News System) Organization: Computer Science & Engineering, U of Washington, Seattle Message-ID: Date: Thu, 5 Mar 1998 20:50:12 GMT (Again from Jan Sanislo) ======== > In death packet 1, notice that the data length > was 36 bytes. The IP spec states that "all fragments > except the last shall have a data length that is a multiple > of 8 bytes". So, just looking at the first packet you > can tell that something is amiss on the other end.