summaryrefslogtreecommitdiffstats
path: root/scripts/membase-http-info.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/membase-http-info.nse')
-rw-r--r--scripts/membase-http-info.nse151
1 files changed, 151 insertions, 0 deletions
diff --git a/scripts/membase-http-info.nse b/scripts/membase-http-info.nse
new file mode 100644
index 0000000..107acab
--- /dev/null
+++ b/scripts/membase-http-info.nse
@@ -0,0 +1,151 @@
+local _G = require "_G"
+local http = require "http"
+local json = require "json"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local table = require "table"
+local tab = require "tab"
+
+description = [[
+Retrieves information (hostname, OS, uptime, etc.) from the CouchBase
+Web Administration port. The information retrieved by this script
+does not require any credentials.
+]]
+
+---
+-- @usage
+-- nmap -p 8091 <ip> --script membase-http-info
+--
+-- @output
+-- PORT STATE SERVICE
+-- 8091/tcp open unknown
+-- | membase-http-info:
+-- | Hostname 192.168.0.5:8091
+-- | OS x86_64-unknown-linux-gnu
+-- | Version 1.7.2r-20-g6604356
+-- | Kernel version 2.14.4
+-- | Mnesia version 4.4.19
+-- | Stdlib version 1.17.4
+-- | OS mon version 2.2.6
+-- | NS server version 1.7.2r-20-g6604356
+-- | SASL version 2.1.9.4
+-- | Status healthy
+-- | Uptime 21465
+-- | Total memory 522022912
+-- | Free memory 41779200
+-- |_ Server list 192.168.0.5:11210
+--
+
+author = "Patrik Karlsson"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+categories = {"discovery", "safe"}
+
+
+portrule = shortport.port_or_service(8091, "http", "tcp")
+
+local function fail(err) return stdnse.format_output(false, err) end
+
+local filter = {
+ ["parsed[1]['nodes'][1]['os']"] = { name = "OS" },
+ ["parsed[1]['nodes'][1]['version']"] = { name = "Version" },
+ ["parsed[1]['nodes'][1]['hostname']"] = { name = "Hostname" },
+ ["parsed[1]['nodes'][1]['status']"] = { name = "Status" },
+ ["parsed[1]['nodes'][1]['uptime']"] = { name = "Uptime" },
+ ["parsed[1]['nodes'][1]['memoryTotal']"] = { name = "Total memory" },
+ ["parsed[1]['nodes'][1]['memoryFree']"] = { name = "Free memory" },
+ ["parsed[1]['vBucketServerMap']['serverList']"] = { name = "Server list" },
+ ["parsed['componentsVersion']['kernel']"] = { name = "Kernel version" },
+ ["parsed['componentsVersion']['mnesia']"] = { name = "Mnesia version" },
+ ["parsed['componentsVersion']['stdlib']"] = { name = "Stdlib version" },
+ ["parsed['componentsVersion']['os_mon']"] = { name = "OS mon version" },
+ ["parsed['componentsVersion']['ns_server']"] = { name = "NS server version" },
+ ["parsed['componentsVersion']['sasl']"] = { name = "SASL version" },
+}
+
+local order = {
+ "parsed[1]['nodes'][1]['hostname']",
+ "parsed[1]['nodes'][1]['os']",
+ "parsed[1]['nodes'][1]['version']",
+ "parsed['componentsVersion']['kernel']",
+ "parsed['componentsVersion']['mnesia']",
+ "parsed['componentsVersion']['stdlib']",
+ "parsed['componentsVersion']['os_mon']",
+ "parsed['componentsVersion']['ns_server']",
+ "parsed['componentsVersion']['sasl']",
+ "parsed[1]['nodes'][1]['status']",
+ "parsed[1]['nodes'][1]['uptime']",
+ "parsed[1]['nodes'][1]['memoryTotal']",
+ "parsed[1]['nodes'][1]['memoryFree']",
+ "parsed[1]['vBucketServerMap']['serverList']",
+}
+
+local function cmdReq(host, port, url, result)
+ local response = http.get(host, port, url)
+
+ if ( 200 ~= response.status ) or ( response.header['server'] == nil ) then
+ return false
+ end
+
+ if ( response.header['server'] and
+ not( response.header['server']:match("^Couchbase Server") or response.header['server']:match("^Membase Server") ) ) then
+ return false
+ end
+
+ local status, parsed = json.parse(response.body)
+ if ( not(status) ) then
+ return false, "Failed to parse response from server"
+ end
+
+ result = result or {}
+ for item in pairs(filter) do
+ local var, val = ""
+ for x in item:gmatch("(.-%])") do
+ var = var .. x
+ local env = setmetatable({parsed=parsed}, {__index = _G})
+ local func = load("return " .. var, nil, "t", env)
+
+ if ( not(func()) ) then
+ val = nil
+ break
+ end
+ val = func()
+ end
+
+ if ( val ) then
+ local name = filter[item].name
+ val = ( "table" == type(val) and table.concat(val, ",") or val )
+ result[item] = { name = name, value = val }
+ end
+ end
+ return true, result
+end
+
+action = function(host, port)
+
+ -- Identify servers that answer 200 to invalid HTTP requests and exit as these would invalidate the tests
+ local status_404, result_404, _ = http.identify_404(host,port)
+ if ( status_404 and result_404 == 200 ) then
+ stdnse.debug1("Exiting due to ambiguous response from web server on %s:%s. All URIs return status 200.", host.ip, port.number)
+ return nil
+ end
+
+ local urls = { "/pools/default/buckets", "/pools" }
+
+ local status, result
+ for _, u in ipairs(urls) do
+ status, result = cmdReq(host, port, u, result)
+ end
+
+ if ( not(result) or not(next(result)) ) then
+ return
+ end
+
+ local output = tab.new(2)
+ for _, item in ipairs(order) do
+ if ( result[item] ) then
+ tab.addrow(output, result[item].name, result[item].value)
+ end
+ end
+
+ return stdnse.format_output(true, tab.dump(output))
+end