local bin = require "bin" local bit = require "bit" local nmap = require "nmap" local rpc = require "rpc" local shortport = require "shortport" local stdnse = require "stdnse" local table = require "table" description = [[ Detects vulnerabilities and gathers information (such as version numbers and hardware support) from VxWorks Wind DeBug agents. 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: -- | VULNERABLE: Wind River Systems VxWorks debug service enabled. See http://www.kb.cert.org/vuls/id/362332 -- | 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 = {"default", "version", "discovery", "vuln"} -- WDB protocol information -- http://www-kryo.desy.de/documents/vxWorks/V5.5/tornado-api/wdbpcl/wdb.html -- Metasploit scanner module -- http://www.metasploit.com/redmine/projects/framework/repository/entry/lib/msf/core/exploit/wdbrpc.rb portrule = shortport.port_or_service(17185, "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 = {} table.insert(o, "VULNERABLE: Wind River Systems VxWorks debug service enabled. See http://www.kb.cert.org/vuls/id/362332") 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