diff options
Diffstat (limited to '')
-rw-r--r-- | scripts/membase-http-info.nse | 151 |
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 |