diff --git a/nselib/rpc.lua b/nselib/rpc.lua index 675d3c99b..1e9a98615 100644 --- a/nselib/rpc.lua +++ b/nselib/rpc.lua @@ -121,7 +121,7 @@ local mutex = nmap.mutex("rpc") -- Supported protocol versions RPC_version = { - ["rpcbind"] = { min=2, max=2 }, + ["rpcbind"] = { min=2, max=4 }, ["nfs"] = { min=1, max=3 }, ["mountd"] = { min=1, max=3 }, } @@ -577,6 +577,16 @@ Portmap = CALLIT = 5, }, + [3] = + { + DUMP = 4, + }, + + [4] = + { + DUMP = 4, + }, + }, State = @@ -635,11 +645,13 @@ Portmap = -- -- table[program_id][protocol]["port"] = -- table[program_id][protocol]["version"] = + -- table[program_id][protocol]["addr"] = -- -- -- Where -- o program_id is the number associated with the program - -- o protocol is either "tcp" or "udp" + -- o protocol is one of "tcp", "udp", "tcp6", or "udp6", or another netid + -- reported by the system. -- Dump = function(self, comm) local status, data, packet, response, pos, header @@ -701,16 +713,38 @@ Portmap = break end - program, version, protocol, port, pos = string.unpack(">I4 I4 I4 I4", data, pos) - if ( protocol == Portmap.PROTOCOLS.tcp ) then - protocol = "tcp" - elseif ( protocol == Portmap.PROTOCOLS.udp ) then - protocol = "udp" + program, version, pos = string.unpack(">I4 I4", data, pos) + local addr, owner + if comm.version > 2 then + local len + len, pos = string.unpack(">I4", data, pos) + pos, protocol = Util.unmarshall_vopaque(len, data, pos) + len, pos = string.unpack(">I4", data, pos) + pos, addr = Util.unmarshall_vopaque(len, data, pos) + len, pos = string.unpack(">I4", data, pos) + pos, owner = Util.unmarshall_vopaque(len, data, pos) + if protocol:match("^[tu][cd]p6?$") then + -- RFC 5665 + local upper, lower + addr, upper, lower = addr:match("^(.-)%.(%d+)%.(%d+)$") + if addr then + port = tonumber(upper) * 0x100 + tonumber(lower) + end + end + else + protocol, port, pos = string.unpack(">I4 I4", data, pos) + if ( protocol == Portmap.PROTOCOLS.tcp ) then + protocol = "tcp" + elseif ( protocol == Portmap.PROTOCOLS.udp ) then + protocol = "udp" + end end program_table[program] = program_table[program] or {} program_table[program][protocol] = program_table[program][protocol] or {} program_table[program][protocol]["port"] = port + program_table[program][protocol]["addr"] = addr + program_table[program][protocol]["owner"] = owner program_table[program][protocol]["version"] = program_table[program][protocol]["version"] or {} table.insert( program_table[program][protocol]["version"], version ) -- parts of the code rely on versions being in order @@ -2761,7 +2795,8 @@ Helper = { RpcInfo = function( host, port ) local status, result local portmap = Portmap:new() - local comm = Comm:new('rpcbind', 2) + local pversion = 4 + local comm = Comm:new('rpcbind', pversion) mutex "lock" @@ -2775,21 +2810,25 @@ Helper = { return true, nmap.registry[host.ip]['portmapper'] end - status, result = comm:Connect(host, port) - if (not(status)) then - mutex "done" - stdnse.debug4("rpc.Helper.RpcInfo: %s", result) - return status, result - end + while pversion >= 2 do + status, result = comm:Connect(host, port) + if (not(status)) then + mutex "done" + stdnse.debug4("rpc.Helper.RpcInfo: %s", result) + return status, result + end - status, result = portmap:Dump(comm) - comm:Disconnect() + status, result = portmap:Dump(comm) + comm:Disconnect() + + if status then + break + end + stdnse.debug4("rpc.Helper.RpcInfo: %s", result) + pversion = pversion - 1 + end mutex "done" - if (not(status)) then - stdnse.debug4("rpc.Helper.RpcInfo: %s", result) - end - return status, result end, @@ -2837,40 +2876,60 @@ Helper = { return status, portmap_table end - local info = {} -- assume failure status = false - for _, p in ipairs( RPC_PROTOCOLS ) do - local tmp = portmap_table[Util.ProgNameToNumber(program)] + local tmp = portmap_table[Util.ProgNameToNumber(program)] + if not tmp then + return false, "Program not supported by target" + end - if ( tmp and tmp[p] ) then - info = {} + local info = {} + local proginfo + local ipv6 = nmap.address_family() == "inet6" + ::AF_FALLBACK:: + for _, p in ipairs( RPC_PROTOCOLS ) do + if ipv6 then + proginfo = tmp[p .. "6"] + else + proginfo = tmp[p] + end + if proginfo then info.port = {} - info.port.number = tmp[p].port + info.port.number = proginfo.port info.port.protocol = p - -- choose the highest version available - if ( not(RPC_version[program]) ) then - info.version = tmp[p].version[#tmp[p].version] - status = true - else - for i=#tmp[p].version, 1, -1 do - if ( RPC_version[program].max >= tmp[p].version[i] ) then - if ( not(max_version) ) then - info.version = tmp[p].version[i] - status = true - break - else - if ( max_version >= tmp[p].version[i] ) then - info.version = tmp[p].version[i] - status = true - break - end - end + break + end + end + if ipv6 and not proginfo then + -- Fall back to trying IPv4 + ipv6 = false + goto AF_FALLBACK + end + + if not proginfo then + return false, "No transport protocol supported" + end + + -- choose the highest version available + if ( not(RPC_version[program]) ) then + info.version = proginfo.version[#proginfo.version] + status = true + else + for i=#proginfo.version, 1, -1 do + if ( RPC_version[program].max >= proginfo.version[i] ) then + if ( not(max_version) ) then + info.version = proginfo.version[i] + status = true + break + else + if ( max_version >= proginfo.version[i] ) then + info.version = proginfo.version[i] + status = true + break end end end - break end end diff --git a/scripts/rpcinfo.nse b/scripts/rpcinfo.nse index 99a42244b..87edfc7bc 100644 --- a/scripts/rpcinfo.nse +++ b/scripts/rpcinfo.nse @@ -117,14 +117,17 @@ action = function(host, port) end end - table.insert( result, ("%-7d %-10s %5d/%s %s"):format(progid, table.concat(v2.version, ","), v2.port, proto, rpc.Util.ProgNumberToName(progid) or "") ) + if v2.port then + -- TODO: report other transports that don't have a port; e.g. "local" + table.insert( result, ("%-7d %-10s %5d/%-4s %s"):format(progid, table.concat(v2.version, ","), v2.port, proto, rpc.Util.ProgNumberToName(progid) or "") ) + end end end table.sort(result) if (#result > 0) then - table.insert(result, 1, "program version port/proto service") + table.insert(result, 1, "program version port/proto service") end return xmlout, stdnse.format_output( true, result )