From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- scripts/couchdb-stats.nse | 225 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 scripts/couchdb-stats.nse (limited to 'scripts/couchdb-stats.nse') diff --git a/scripts/couchdb-stats.nse b/scripts/couchdb-stats.nse new file mode 100644 index 0000000..9bcfe8b --- /dev/null +++ b/scripts/couchdb-stats.nse @@ -0,0 +1,225 @@ +local http = require "http" +local json = require "json" +local nmap = require "nmap" +local shortport = require "shortport" +local stdnse = require "stdnse" +local string = require "string" +local table = require "table" + +description = [[ +Gets database statistics from a CouchDB database. + +For more info about the CouchDB HTTP API and the statistics, see +http://wiki.apache.org/couchdb/Runtime_Statistics +and +http://wiki.apache.org/couchdb/HTTP_database_API. +]] + +--- +-- @usage +-- nmap -p 5984 --script "couchdb-stats.nse" +-- @output +-- PORT STATE SERVICE REASON +-- 5984/tcp open httpd syn-ack +-- | couchdb-stats: +-- | httpd_request_methods +-- | GET (number of HTTP GET requests) +-- | current = 5 +-- | count = 1617 +-- | couchdb +-- | request_time (length of a request inside CouchDB without MochiWeb) +-- | current = 1 +-- | count = 5 +-- | httpd_status_codes +-- | 200 (number of HTTP 200 OK responses) +-- | current = 5 +-- | count = 1617 +-- | httpd +-- | requests (number of HTTP requests) +-- | current = 5 +-- | count = 1617 +-- |_ Authentication : NOT enabled ('admin party') + +-- version 0.3 +-- +-- Created 01/20/2010 - v0.1 - created by Martin Holst Swende +-- Modified 07/02/2010 - v0.2 - added test if auth is enabled, compacted output a bit (mhs) + +author = "Martin Holst Swende" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = {"discovery", "safe"} +portrule = shortport.port_or_service({5984}) +-- Some lazy shortcuts +local dbg = stdnse.debug1 + +local DISCARD = {stddev=1,min=1,max=1, mean=1} +--- Removes uninteresting data from the table +-- uses the DISCARD table above to see what +-- keys should be omitted from the results +-- @param data a table containing data +--@return another table containing data, with some keys removed +local function queryResultToTable(data) + local result = {} + for k,v in pairs(data) do + dbg("(%s,%s)",k,tostring(v)) + if DISCARD[k] ~= 1 then + if type(v) == 'table' then + if v["description"] ~= nil then + k = string.format("%s (%s)",tostring(k), tostring(v["description"])) + v["description"] = nil + end + table.insert(result,k) + table.insert(result,queryResultToTable(v)) + else + table.insert(result,(("%s = %s"):format(tostring(k), tostring(v)))) + end + end + end + return result +end + + +action = function(host, port) + local data, result, err + + data = http.get( host, port, '/_stats' ) + + -- check that body was received + if not data.body or data.body == "" then + local msg = ("%s did not respond with any data."):format(host.targetname or host.ip ) + dbg( msg ) + return msg + end + + -- The html body should look like this (minus whitespace): + --[[ +{ + "httpd_status_codes": { + "200": { + "count": 29894, + "description": "number of HTTP 200 OK responses", + "min": 0, + "max": 1, + "current": 10, + "stddev": 0.01828669972606202, + "mean": 0.0003345152873486337 + }, + "500": { + "count": 28429, + "description": "number of HTTP 500 Internal Server Error responses", + "min": 0, + "max": 1, + "current": 1, + "stddev": 0.005930776661631644, + "mean": 3.517534911534013e-05 + } + }, + "httpd": { + "requests": { + "count": 29894, + "description": "number of HTTP requests", + "min": 0, + "max": 2, + "current": 12, + "stddev": 0.02163701147572207, + "mean": 0.00040141834481835866 + } + }, + "couchdb": { + "request_time": { + "count": 12, + "description": "length of a request inside CouchDB without MochiWeb", + "min": 1, + "max": 287, + "current": 23, + "stddev": 77.76723638882608, + "mean": 32.58333333333333 + } + }, + "httpd_request_methods": { + "GET": { + "count": 29894, + "description": "number of HTTP GET requests", + "min": 0, + "max": 2, + "current": 12, + "stddev": 0.02163701147572207, + "mean": 0.00040141834481835866 + } + } +} + ]]-- + + local status, result = json.parse(data.body) + if not status then + dbg(result) + return result + end + + -- Here we know it is a couchdb + port.version.name ='httpd' + port.version.product='Apache CouchDB' + nmap.set_port_version(host,port) + + -- We have a valid table in result containing the parsed json + -- now, get all the interesting bits + + result = queryResultToTable(result) + + -- Additionally, we can check if authentication is used : + -- The following actions are restricted if auth is used + -- create db (PUT /database) + -- delete db (DELETE /database) + -- Creating a design document (PUT /database/_design/app) + -- Updating a design document (PUT /database/_design/app?rev=1-4E2) + -- Deleting a design document (DELETE /database/_design/app?rev=1-6A7) + -- Triggering compaction (POST /_compact) + -- Reading the task status list (GET /_active_tasks) + -- Restart the server (POST /_restart) + -- Read the active configuration (GET /_config) + -- Update the active configuration (PUT /_config) + + data = http.get( host, port, '/_config' ) + local status, authresult = json.parse(data.body) + + -- If authorization is used, we should get back something like + -- {"error":"unauthorized","reason":"You are not a server admin."} + -- Otherwise, a *lot* of data, : + -- {"httpd_design_handlers":{"_info":"{couch_httpd_db, handle_design_info_req}", + -- "_list":"{couch_httpd_show, handle_view_list_req}","_show":"{couch_httpd_show, handle_doc_show_req}", + -- "_update":"{couch_httpd_show, handle_doc_update_req}","_view":"{couch_httpd_view, handle_view_req}"}, + -- "httpd_global_handlers":{"/":"{couch_httpd_misc_handlers, handle_welcome_req, <<\"Welcome\">>}", + -- "_active_tasks":"{couch_httpd_misc_handlers, handle_task_status_req}", + -- "_all_dbs":"{couch_httpd_misc_handlers, handle_all_dbs_req}", + -- "_config":"{couch_httpd_misc_handlers, handle_config_req}", + -- "_log":"{couch_httpd_misc_handlers, handle_log_req}","_oauth":"{couch_httpd_oauth, handle_oauth_req}", + -- "_replicate":"{couch_httpd_misc_handlers, handle_replicate_req}","_restart":"{couch_httpd_misc_handlers, handle_restart_req}", + -- "_session":"{couch_httpd_auth, handle_session_req}","_sleep":"{couch_httpd_misc_handlers, handle_sleep_req}", + -- "_stats":"{couch_httpd_stats_handlers, handle_stats_req}","_user":"{couch_httpd_auth, handle_user_req}", + -- "_utils":"{couch_httpd_misc_handlers, handle_utils_dir_req, \"/usr/share/couchdb/www\"}", + -- "_uuids":"{couch_httpd_misc_handlers, handle_uuids_req}","favicon.ico":"{couch_httpd_misc_handlers, handle_favicon_req, \"/usr/share/couchdb/www\"}"}, + -- "query_server_config":{"reduce_limit":"true"},"log":{"file":"/var/log/couchdb/0.10.0/couch.log","level":"info"}, + -- "query_servers":{"javascript":"/usr/bin/couchjs /usr/share/couchdb/server/main.js"}, + -- "daemons":{"batch_save":"{couch_batch_save_sup, start_link, []}","db_update_notifier":"{couch_db_update_notifier_sup, start_link, []}", + -- "external_manager":"{couch_external_manager, start_link, []}","httpd":"{couch_httpd, start_link, []}", + -- "query_servers":"{couch_query_servers, start_link, []}","stats_aggregator":"{couch_stats_aggregator, start, []}", + -- "stats_collector":"{couch_stats_collector, start, []}","view_manager":"{couch_view, start_link, []}"}, + -- "httpd":{"WWW-Authenticate":"Basic realm=\"administrator\"","authentication_handlers":"{couch_httpd_oauth, oauth_authentication_handler}, {couch_httpd_auth, default_authentication_handler}", + -- "bind_address":"127.0.0.1","default_handler":"{couch_httpd_db, handle_request}","port":"5984"},"httpd_db_handlers":{"_changes":"{couch_httpd_db, handle_changes_req}", + -- "_compact":"{couch_httpd_db, handle_compact_req}","_design":"{couch_httpd_db, handle_design_req}","_temp_view":"{couch_httpd_view, handle_temp_view_req}", + -- "_view":"{couch_httpd_view, handle_db_view_req}","_view_cleanup":"{couch_httpd_db, handle_view_cleanup_req}"}, + -- "couch_httpd_auth":{"authentication_db":"users","require_valid_user":"false","secret":"replace this with a real secret in your local.ini file"}, + -- "couchdb":{"batch_save_interval":"1000","batch_save_size":"1000","database_dir":"/var/lib/couchdb/0.10.0","delayed_commits":"true", + -- "max_attachment_chunk_size":"4294967296","max_dbs_open":"100","max_document_size":"4294967296", + -- "os_process_timeout":"5000","util_driver_dir":"/usr/lib/couchdb/erlang/lib/couch-0.10.0/priv/lib","view_index_dir":"/var/lib/couchdb/0.10.0"}} + local auth = "Authentication : %s" + local authEnabled = "unknown" + + if(status) then + if(authresult["error"] == "unauthorized") then authEnabled = "enabled" + elseif (authresult["httpd_design_handlers"] ~= nil) then authEnabled = "NOT enabled ('admin party')" + end + end + table.insert(result, auth:format(authEnabled)) + return stdnse.format_output(true, result ) +end -- cgit v1.2.3