152 lines
4.8 KiB
Lua
152 lines
4.8 KiB
Lua
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
|