diff --git a/CHANGELOG b/CHANGELOG index a2a1773c5..31643e0d4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added voldemort-info, that retrieves cluster and store information + from the Voldemort distributed key-value store. [Patrik] + o [NSE] Added http-qnap-nas-info, that retrieves the model, firware version, and enabled services from a QNAP Network Attached Storage (NAS) device. [Brendan Coles] diff --git a/nmap-service-probes b/nmap-service-probes index d3e814f0f..3f72ddeda 100644 --- a/nmap-service-probes +++ b/nmap-service-probes @@ -10490,3 +10490,10 @@ Probe TCP epmd q|\0\x01\x6e| rarity 8 ports 4369 match epmd m|^\0\0\x11\x11.*| p/Erlang Port Mapper Daemon/ + +##############################NEXT PROBE############################## +# Voldemort Native Protocol Version 3 connect probe +Probe TCP vp3 q|vp3| +rarity 8 +ports 6666 +match vp3 m|^ok$| p/Voldemort/ diff --git a/scripts/script.db b/scripts/script.db index a542f8fa2..0c2892051 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -313,6 +313,7 @@ Entry { filename = "url-snarf.nse", categories = { "safe", } } Entry { filename = "vmauthd-brute.nse", categories = { "brute", "intrusive", } } Entry { filename = "vnc-brute.nse", categories = { "brute", "intrusive", } } Entry { filename = "vnc-info.nse", categories = { "default", "discovery", "safe", } } +Entry { filename = "voldemort-info.nse", categories = { "discovery", "safe", } } Entry { filename = "vuze-dht-info.nse", categories = { "discovery", "safe", } } Entry { filename = "wdb-version.nse", categories = { "default", "discovery", "version", "vuln", } } Entry { filename = "whois.nse", categories = { "discovery", "external", "safe", } } diff --git a/scripts/voldemort-info.nse b/scripts/voldemort-info.nse new file mode 100644 index 000000000..d4d474ee1 --- /dev/null +++ b/scripts/voldemort-info.nse @@ -0,0 +1,169 @@ +description = [[ +Retrieves cluster and store information from the Voldemort distributed key- +value store using the Voldemort Native Protocol. +]] + +--- +-- @usage +-- nmap -p 6666 --script voldemort-info +-- +-- @output +-- PORT STATE SERVICE +-- 6666/tcp open irc +-- | voldemort-info: +-- | Cluster +-- | Name: mycluster +-- | Id: 0 +-- | Host: localhost +-- | HTTP Port: 8081 +-- | TCP Port: 6666 +-- | Admin Port: 6667 +-- | Partitions: 0, 1 +-- | Stores +-- | test +-- | Persistence: bdb +-- | Description: Test store +-- | Owners: harry@hogwarts.edu, hermoine@hogwarts.edu +-- | Routing strategy: consistent-routing +-- | Routing: client +-- | wordcounts +-- | Persistence: read-only +-- | Routing strategy: consistent-routing +-- |_ Routing: client +-- + +author = "Patrik Karlsson" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"discovery", "safe"} + +require 'shortport' + +portrule = shortport.port_or_service(6666, "vp3", "tcp") + +local function fail(err) return ("\n ERROR: %s"):format(err or "") end + +-- Connect to the server and make sure it supports the vp3 protocol +-- @param host table as received by the action method +-- @param port table as received by the action method +-- @return status true on success, false on failure +-- @return socket connected to the server +local function connect(host, port) + local socket = nmap.new_socket() + socket:set_timeout(5000) + + local status, err = socket:connect(host, port) + if ( not(status) ) then + return false, "Failed to connect to server" + end + + status, err = socket:send("vp3") + if ( not(status) ) then + return false, "Failed to send request to server" + end + + local response + status, response = socket:receive(2) + if ( not(status) ) then + return false, "Failed to receive response from server" + elseif( response ~= "ok" ) then + return false, "Unsupported protocol" + end + return true, socket +end + +-- Get Voldemort metadata +-- @param socket connected to the server +-- @param file the xml file to retrieve +-- @return status true on success false on failure +-- @return data string as received from the server +local function getMetadata(socket, file) + + local req = bin.pack(">HCzIcz", "0100", #("metadata"), "metadata", 0, #file, file) + local status, err = socket:send(req) + if ( not(status) ) then + return false, "Failed to send request to server" + end + local status, data = socket:receive(8) + if ( not(status) ) then + return false, "Failed to receive response from server" + end + local _, len = bin.unpack(">S", data, 9) + while( #data < len - 2 ) do + local status, tmp = socket:receive(len - 2 - #data) + if ( not(status) ) then + return false, "Failed to receive response from server" + end + data = data .. tmp + end + return true, data +end + + +action = function(host, port) + + -- table of variables to query the server + local vars = { + ["cluster"] = { + { key = "Name", match = ".-(.-)" }, + { key = "Id", match = ".-.-(%d-).-" }, + { key = "Host", match = ".-.-(%w-).-" }, + { key = "HTTP Port", match = ".-.-(%d-).-" }, + { key = "TCP Port", match = ".-.-(%d-).-" }, + { key = "Admin Port", match = ".-.-(%d-).-" }, + { key = "Partitions", match = ".-.-([%d%s,]*).-" }, + }, + ["store"] = { + { key = "Persistence", match = ".-(.-)" }, + { key = "Description", match = ".-(.-)" }, + { key = "Owners", match = ".-(.-)" }, + { key = "Routing strategy", match = ".-(.-)" }, + { key = "Routing", match = ".-(.-)" }, + }, + } + + -- connect to the server + local status, socket = connect(host, port) + if ( not(status) ) then + return fail(socket) + end + + -- get the cluster meta data + local status, response = getMetadata(socket, "cluster.xml") + if ( not(status) or not(response:match(".*")) ) then + return + end + + -- Get the cluster details + local cluster_tbl = { name = "Cluster" } + for _, item in ipairs(vars["cluster"]) do + local val = response:match(item.match) + if ( val ) then + table.insert(cluster_tbl, ("%s: %s"):format(item.key, val)) + end + end + + -- get the stores meta data + local status, response = getMetadata(socket, "stores.xml") + if ( not(status) or not(response:match(".-")) ) then + return + end + + local result, stores = {}, { name = "Stores" } + table.insert(result, cluster_tbl) + + -- iterate over store items + for store in response:gmatch(".-") do + local name = store:match(".-(.-)") + local store_tbl = { name = name or "unknown" } + + for _, item in ipairs(vars["store"]) do + local val = store:match(item.match) + if ( val ) then + table.insert(store_tbl, ("%s: %s"):format(item.key, val)) + end + end + table.insert(stores, store_tbl) + end + table.insert(result, stores) + return stdnse.format_output(true, result) +end \ No newline at end of file