smb bugs

While messing with the protocol, I found 2 bugs in smb client implementations, nothing fancy, but I really appreciate buggy implementations of this protocol, therefore …

nmap

The first one is within nmap's msrpc nse library:

local function call_function(smbstate, opnum, arguments)
        local i
        local status, result
        local parameters, data
        local pos, align
        local result
        local first = true
        local is_first, is_last
 
        data = bin.pack("<CCCC>I<SSIISSA",
                                0x05,        -- Version (major)
                                0x00,        -- Version (minor)
                                0x00,        -- Packet type (0x00 = request)
                                0x03,        -- Packet flags (0x03 = first frag + last frag)
                                0x10000000,  -- Data representation (big endian)
                                0x18 + string.len(arguments), -- Frag length (0x18 = the size of this data)
                                0x0000,      -- Auth length
                                0x41414141,  -- Call ID (I use 'AAAA' because it's easy to recognize)
                                0x00000038,  -- Alloc hint
                                0x0000,      -- Context ID
                                opnum,       -- Opnum
                                arguments
                        )

The Alloc hint is wrong, as it is static, it has to be

                                string.length(arguments),  -- Alloc hint

instead.
Seems to work with Windows though.

The effect is … nothing, besides dionaea fails to parse the request if alloc hint is set wrong.

Using a static Call ID makes it pretty easy to identify nmap scans, but you can always make things worse:

local REFERENT_ID = 0x50414d4e

Pointers also have interesting properties. A pointer is preceeded by a 4-byte value called (at least by Wireshark) the “referent id”. For a valid pointer, this can be anything except 0 (I use 'NMAP' for it). src: nmap's doc on msrpctypes

While this is not a bug per se, it makes it really easy to identify nmap smb-* nse scripted scans, ngrep for NMAP on port 445,139,135.

metasploit

The metasploit bug was found as my fragmented Read AndX code was doing fragmentation wrong.

	def read()
 
		max_read = self.options['pipe_read_max_size'] || 1024*1024
		min_read = self.options['pipe_read_min_size'] || max_read
 
		raw_response = ''
 
		# Are we reading from a remote pipe over SMB?	
		if (self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe)
			begin
 
				# Max SMB read is 65535, cap it at 64000
				max_read = [64000, max_read].min
				min_read = [64000, min_read].min
 
				read_limit = nil
 
				while(true)
					# Random read offsets will not work on Windows NT 4.0 (thanks Dave!)
 
					read_cnt = (rand(max_read-min_read)+min_read)
					if(read_limit)
						if(read_cnt + raw_response.length > read_limit)
							read_cnt = raw_response.length - read_limit
						end
					end
 
					data = self.socket.read( read_cnt, rand(1024)+1)
					break if !(data and data.length > 0)
					raw_response += data
 
					# Keep reading until we have at least the DCERPC header
					next if raw_response.length < 10
 
					# We now have to process the raw_response and parse out the DCERPC fragment length
					# if we have read enough data. Once we have the length value, we need to make sure
					# that we don't read beyond this amount, or it can screw up the SMB state
					if (not read_limit)
						begin 
							check = Rex::Proto::DCERPC::Response.new(raw_response)
							read_limit = check.frag_len
						rescue ::Rex::Proto::DCERPC::Exceptions::InvalidPacket
						end
					end
 
					break if (read_limit and read_limit == raw_response.length)
				end
 
			rescue Rex::Proto::SMB::Exceptions::NoReply
				# I don't care if I didn't get a reply...
			rescue Rex::Proto::SMB::Exceptions::ErrorCode => exception
				if exception.error_code != 0xC000014B 
					raise exception
				end
			end
		# This must be a regular TCP or UDP socket
		else 
			...
		end
 
		raw_response
	end

Rex::Proto::DCERPC::Client revision 7248

The read code … reads from the remote, forcing the remote to fragment the data randomly. Once it received enough data to read the frag_len, which indicates the length of the total DCERPC which has to be transferred, it will continue until it received exactly frag_len bytes DCERPC payload data.
To exploit, sent the first fragment with a size larger than indicated by your DCERPC response packet, and sent 1 bytes chunks afterwards, so the frag_len is never hit. metasploit will take some cycles on your cpu, and waste memory.

My fix would be to change the abort condition to:

					break if (read_limit and read_limit <= raw_response.length)

UPDATE fixed in revision 9631

As the frag_len is a short, the max amount of data would be 64k, reducing the impact to a minimum.

One could make a Metasploit module for this, but due to metasploits performance, a denial of service to the metasploit smb client might cause a denial of service to the metasploit smb service as well, exhausting the cpu.

Comments



2010/06/28/smb_bugs.txt · Last modified: 2010/06/28 16:56 by common
chimeric.de = chi`s home Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0