Nmap Development mailing list archives
[NSE] socket:receive_buf(...) broken
From: Patrick Donnelly <batrick () batbytes com>
Date: Tue, 14 Sep 2010 02:24:27 -0400
Hi all, I'm in process of a major maintenance fix of the NSE nsock binding. In the process of doing so I have found numerous bugs and problems but one has been extra glaring that warrants a post here, now. The socket:receive_buf [1] function has numerous problems: When *any* socket method is called, excluding receive_buf, the buffer is cleared (especially send!) [1]. This is "intentional behavior" but has resulted in buggy usage of the function. For example, the ssh2.lua library throws away the last 4 bytes of a payload because it does not offset the initial integer giving the length. These extra four bytes are kept in the buffer but discarded on a later send. These are good bytes that were part of the payload! I assume these bytes were some form of padding and that is the only reason any ssh operation succeeds. I'll try to show what I mean in the code snippet (from ssh2.lua) below: --- Retrieve the size of the packet that is being received -- and checks if it is fully received -- -- This function is very similar to the function generated -- with match.numbytes(num) function, except that this one -- will check for the number of bytes on-the-fly, based on -- the written on the SSH packet. -- -- @param buffer The receive buffer -- @return packet_length, packet_length or nil -- the return is similar to the lua function string:find() check_packet_length = function( buffer ) local packet_length, offset offset, packet_length = bin.unpack( ">I", buffer ) assert(packet_length) if packet_length + 4 > buffer:len() then return nil end return packet_length, packet_length end --- Receives a complete SSH packet, even if fragmented -- this function is an abstraction layer to deal with -- checking the packet size to know if there is any more -- data to receive. -- -- @param socket The socket used to receive the data -- @return status True or false -- @return packet The packet received transport.receive_packet = function( socket ) local status, packet = socket:receive_buf(check_packet_length) return status, packet end The transport.receive_packet function attempts to read from the socket a binary integer (4 bytes) that describes the length of the payload. The check_packet_length function will keep reading from the socket until the length reaches packet_length+4 (the extra 4 accounting for the binary integer). You can see a bug here in that the check_packet_length function returns packet_length which will include the binary integer but exclude the last 4 bytes of the payload! The function should return packet_length+4, packet_length+4. The only reason ssh2.lua gets away with this (on accident of course) is because later sends clear the buffer. Another error in the above code is the assumption that the buffer is at least of size 4. If the initial call to the function is passed an empty buffer string, the call will fail the assertion. Another unspecified implementation detail that has saved us is that receive_buf will automatically keep the pattern if the two integers returned by check_packet_length (or any such function) are equal. This means that the last byte is not discarded when it should be. I argue this is also a bug. The correct way to capture the entire string is to specify keeppattern as true when calling receive_buf. (It just so happens that receive_packet would have properly failed, in a later assertion not shown, if the last byte had been discarded as it should be.) These are the design problems I'm so far aware of with this function. I'm fixing all of the above in the new implementation. To rehash the changes to receive_buf: o The buffer will no longer be cleared on a call to other socket methods. Only receive_buf reads the buffer. Other read methods will ignore the contents of the buffer. Do not interleave calls without finishing the buffer. o A "priming" call to the pattern function may pass a buffer of length 0. The function/pattern should handle this appropriately. (This needs to be the case anyway; consider in the above example if for some reason we only had 2 bytes in the buffer. The read of a 4 byte integer would still fail!) o receive_buf does not automatically set keep to true if the two integers returned by the pattern function are equal. [1] http://nmap.org/nsedoc/lib/nmap.html#receive_buf -- - Patrick Donnelly _______________________________________________ Sent through the nmap-dev mailing list http://cgi.insecure.org/mailman/listinfo/nmap-dev Archived at http://seclists.org/nmap-dev/
Current thread:
- [NSE] socket:receive_buf(...) broken Patrick Donnelly (Sep 13)
- Re: [NSE] socket:receive_buf(...) broken David Fifield (Sep 15)
- Re: [NSE] socket:receive_buf(...) broken Kris Katterjohn (Sep 15)
- Re: [NSE] socket:receive_buf(...) broken Luis MartinGarcia. (Sep 17)
- Re: [NSE] socket:receive_buf(...) broken Patrick Donnelly (Sep 15)
- Re: [NSE] socket:receive_buf(...) broken Patrik Karlsson (Sep 16)
- Re: [NSE] socket:receive_buf(...) broken Fyodor (Sep 16)
- Re: [NSE] socket:receive_buf(...) broken Kris Katterjohn (Sep 15)
- Re: [NSE] socket:receive_buf(...) broken David Fifield (Sep 15)
