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 --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