diff --git a/CHANGELOG b/CHANGELOG index d7d499bea..c7ff9f050 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added wdb-version, which discovers information from a VxWorks + debug service that is often left open. [Daniel Miller] + o [NSE] Added one script (vnc-brute) that performs password guessing against VNC using the new brute library and another (vnc-info) that lists supported security mechanisms. [Patrik] diff --git a/scripts/wdb-version.nse b/scripts/wdb-version.nse new file mode 100644 index 000000000..571343082 --- /dev/null +++ b/scripts/wdb-version.nse @@ -0,0 +1,196 @@ +description = [[ +Gathers information from a Wind DeBug Agent on VxWorks + +Wind DeBug is a SunRPC-type service that is enabled by default on many devices +that use the popular VxWorks real-time embedded operating system. H.D. Moore +of Metasploit has identified several security vulnerabilities and design flaws +with the service, including weakly-hashed passwords and raw memory dumping. + +See also: +http://www.kb.cert.org/vuls/id/362332 +]] + +--- +-- @usage +-- nmap -sU -p 17185 --script wdb-version +-- @output +-- 17185/udp open wdb Wind DeBug Agent 2.0 +-- | wdb-version: +-- | Agent version: 2.0 +-- | VxWorks version: VxWorks5.4.2 +-- | Board Support Package: PCD ARM940T REV 1 +-- | Boot line: host:vxWorks.z + +author = "Daniel Miller" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +-- may also be "safe", but need testing to determine +categories = {"version", "discovery"} + +require 'shortport' +require 'rpc' +require 'stdnse' + +portrule = shortport.port_or_service(0x54321, "wdbrpc", {"udp"} ) + +rpc.RPC_version["wdb"] = { min=1, max=1 } + +local WDB_Procedure = { + ["WDB_TARGET_CONNECT"] = 1, +} + +local function checksum(data) + local sum = 0 + local p = 0 + p, _ = bin.unpack(">I", data) + while p < data:len() do + local c + p, c = bin.unpack(">S", data, p) + sum = sum + c + end + sum = bit.band(sum, 0xffff) + bit.rshift(sum, 16) + return bit.bnot( sum ) +end + +local seqnum = 0 +local function seqno() + seqnum = seqnum + 1 + return seqnum +end + +local function request(comm, procedure, data) + local packet = comm:EncodePacket( nil, procedure, {type = rpc.Portmap.AuthType.NULL}, nil ) + local wdbwrapper = bin.pack( ">I2", data:len() + packet:len() + 8, seqno() ) + local sum = checksum(packet..bin.pack(">I", 0x00000000)..wdbwrapper..data) + return packet .. bin.pack(">S2", 0xffff, sum) .. wdbwrapper .. data +end + +local function decode_reply(data, pos) + local wdberr, len + local done = data:len() + local info = {} + pos, _ = rpc.Util.unmarshall_uint32(data, pos) + pos, _ = rpc.Util.unmarshall_uint32(data, pos) + pos, wdberr = rpc.Util.unmarshall_uint32(data, pos) + info["error"] = bit.band(wdberr, 0xc0000000) + if (info["error"] ~= 0x00000000 ) then + stdnse.print_debug(1,"Error from decode_reply: %x", info["error"]) + return nil, info + end + pos, len = rpc.Util.unmarshall_uint32(data, pos) + if (len ~= 0) then + pos, info["agent_ver"] = rpc.Util.unmarshall_vopaque(len, data, pos) + end + pos, info["agent_mtu"] = rpc.Util.unmarshall_uint32(data, pos) + pos, info["agent_mod"] = rpc.Util.unmarshall_uint32(data, pos) + pos, info["rt_type"] = rpc.Util.unmarshall_uint32(data, pos) + pos, len = rpc.Util.unmarshall_uint32(data, pos) + if (pos == done) then return pos, info end + if (len ~= 0) then + pos, info["rt_vers"] = rpc.Util.unmarshall_vopaque(len, data, pos) + end + pos, info["rt_cpu_type"] = rpc.Util.unmarshall_uint32(data, pos) + pos, len = rpc.Util.unmarshall_uint32(data, pos) + info["rt_has_fpp"] = ( len ~= 0 ) + pos, len = rpc.Util.unmarshall_uint32(data, pos) + info["rt_has_wp"] = ( len ~= 0 ) + pos, info["rt_page_size"] = rpc.Util.unmarshall_uint32(data, pos) + pos, info["rt_endian"] = rpc.Util.unmarshall_uint32(data, pos) + pos, len = rpc.Util.unmarshall_uint32(data, pos) + if (len ~= 0) then + pos, info["rt_bsp_name"] = rpc.Util.unmarshall_vopaque(len, data, pos) + end + pos, len = rpc.Util.unmarshall_uint32(data, pos) + if (len ~= 0) then + pos, info["rt_bootline"] = rpc.Util.unmarshall_vopaque(len, data, pos) + end + if (pos == done) then return pos, info end + pos, info["rt_membase"] = rpc.Util.unmarshall_uint32(data, pos) + if (pos == done) then return pos, info end + pos, info["rt_memsize"] = rpc.Util.unmarshall_uint32(data, pos) + if (pos == done) then return pos, info end + pos, info["rt_region_count"] = rpc.Util.unmarshall_uint32(data, pos) + if (pos == done) then return pos, info end + pos, len = rpc.Util.unmarshall_uint32(data, pos) + if (len ~= 0) then + info["rt_regions"] = {} + for i = 1, len do + pos, info["rt_regions"][i] = rpc.Util.unmarshall_uint32(data, pos) + end + end + if (pos == done) then return pos, info end + pos, len = rpc.Util.unmarshall_uint32(data, pos) + if (len == nil) then return pos, info end + if (len ~= 0) then + pos, info["rt_hostpool_base"] = rpc.Util.unmarshall_vopaque(len, data, pos) + end + if (pos == done) then return pos, info end + pos, len = rpc.Util.unmarshall_uint32(data, pos) + if (len ~= 0) then + pos, info["rt_hostpool_size"] = rpc.Util.unmarshall_vopaque(len, data, pos) + end + return pos, info +end + +action = function(host, port) + local comm = rpc.Comm:new("wdb", 1) + local status, err, data, pos, header + local info = {} + status, err = comm:Connect(host, port) + if (not(status)) then + return stdnse.format_output(false, err) + end + comm.socket:set_timeout(3000) + local packet = request(comm, WDB_Procedure["WDB_TARGET_CONNECT"], bin.pack(">I3", 0x00000002, 0x00000000, 0x00000000)) + if (not(comm:SendPacket(packet))) then + return stdnse.format_output(false, "Failed to send request") + end + + status, data = comm:ReceivePacket() + if (not(status)) then + --return stdnse.format_output(false, "Failed to read data") + return nil + end + nmap.set_port_state(host, port, "open") + + pos = 0 + pos, header = comm:DecodeHeader(data, pos) + if not header then + return stdnse.format_output(false, "Failed to decode header") + end + + if ( pos == data:len() ) then + return stdnse.format_output(false, "No WDB data in reply") + end + + pos, info = decode_reply(data, pos) + if not pos then + return stdnse.format_output(false, "WDB error: "..info.error) + end + port.version.name = "wdb" + port.version.name_confidence = 10 + port.version.product = "Wind DeBug Agent" + port.version.version = info["agent_ver"] + if (port.version.ostype ~= nil) then + port.version.ostype = "VxWorks " .. info["rt_vers"] + end + nmap.set_port_version(host, port, "hardmatched") + local o = {} + o.name = "WDB Dump info" + if (info.agent_ver) then + table.insert(o, "Agent version: " .. info.agent_ver) + end + --table.insert(o, "Agent MTU: " .. info.agent_mtu) + if (info.rt_vers) then + table.insert(o, "VxWorks version: " .. info.rt_vers) + end + -- rt_cpu_type is an enum type, but I don't have access to + -- cputypes.h, where it is defined + --table.insert(o, "CPU Type: " .. info.rt_cpu_type) + if (info.rt_bsp_name) then + table.insert(o, "Board Support Package: " .. info.rt_bsp_name) + end + if (info.rt_bootline) then + table.insert(o, "Boot line: " .. info.rt_bootline) + end + return stdnse.format_output(true, o) +end