--- -- Library methods for handling MongoDB, creating and parsing packets. -- -- @author Martin Holst Swende -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html -- -- @args mongodb.db - the database to use for authentication -- Created 01/13/2010 - v0.1 - created by Martin Holst Swende -- Revised 01/03/2012 - v0.2 - added authentication support local bin = require "bin" local nmap = require "nmap" local stdnse = require "stdnse" local string = require "string" local table = require "table" local openssl = stdnse.silent_require "openssl" _ENV = stdnse.module("mongodb", stdnse.seeall) -- this is not yet widely implemented but at least used for authentication -- ideally, it would be used to set the database against which operations, -- that do not require a specific database, should run local arg_DB = stdnse.get_script_args("mongodb.db") -- Some lazy shortcuts local function dbg(str,...) stdnse.debug3("MngoDb:"..str, ...) end --local dbg =stdnse.debug1 local err =stdnse.debug1 ---------------------------------------------------------------------- -- First of all comes a Bson parsing library. This can easily be moved out into a separate library should other -- services start to use Bson ---------------------------------------------------------------------- -- Library methods for handling the BSON format -- -- For more documentation about the BSON format, ---and more details about its implementations, check out the -- python BSON implementation which is available at -- http://github.com/mongodb/mongo-python-driver/blob/master/pymongo/bson.py -- and licensed under the Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0) -- -- @author Martin Holst Swende -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html -- -- Version 0.1 -- Created 01/13/2010 - v0.1 - created by Martin Holst Swende --module("bson", package.seeall) --require("bin") local function dbg_err(str,...) stdnse.debug1("Bson-ERR:"..str, ...) end --local err =stdnse.log_error -- Packs data into nullterminated string --@param input the string to pack --@return the packed nullterminated string local function make_nullterminated_string(input) return bin.pack("z",input) end --Converts an element (key, value) into bson binary data --@param key the key name, must *NOT* contain . (period) or start with $ --@param value, the element value --@return status : true if ok, false if error --@return result : the packed binary data OR error message local function _element_to_bson(key, value) --Some constraints-checking if type(key) ~= 'string' then return false, "Documents must have only string keys, key was " .. type(key) end if key:sub(1,1) == "$" then return false, "key must not start with $: ".. key end if key:find("%.") then return false, ("key %r must not contain '.'"):format(tostring(key)) end local name =bin.pack("z",key) -- null-terminated string if type(value) == 'string' then local cstring = bin.pack("z",value) -- null-terminated string local length = bin.pack(" 4 * 1024 * 1024 then return false, "document too large - BSON documents are limited to 4 MB" end dbg("Packet length is %d",length) --Final pack return true, bin.pack("I", length) .. elements .. "\0" end -- Reads a null-terminated string. If length is supplied, it is just cut -- out from the data, otherwise the data is scanned for at null-char. --@param data the data which starts with a c-string --@param length optional length of the string --@return the string --@return the remaining data (*without* null-char) local function get_c_string(data,length) if not length then local index = data:find('\0') if index == nil then error({code="C-string did not contain NULL char"}) end length = index end local value = data:sub(1,length-1) --dbg("Found char at pos %d, data is %s c-string is %s",length, data, value) return value, data:sub(length+1) end -- Element parser. Parse data elements -- @param data String containing binary data -- @return Position in the data string where parsing stopped -- @return Unpacked value -- @return error string if error occurred local function parse(code,data) if 1 == code then -- double return bin.unpack(" local value = get_c_string(data:sub(5), len) -- Count position as header (=4) + length of string (=len)+ null char (=1) return 4+len+1,value elseif 3 == code or 4 == code then -- table or array local object, err -- Need to know the length, to return later local _,obj_size = bin.unpack(" 1 do key, value, data = _element_to_dict(data) dbg("Parsed (%s='%s'), data left : %d", tostring(key),tostring(value), data:len()) if type(value) ~= 'table' then value=tostring(value) end result[key] = value end return result end --Checks if enough data to parse the result is captured --@data binary bson data read from socket --@return true if the full bson table is contained in the data, false if data is incomplete --@return required size of packet, if known, otherwise nil function isPacketComplete(data) -- First, we check that the header is complete if data:len() < 4 then local err_msg = "Not enough data in buffer, at least 4 bytes header info expected" return false end local _,obj_size = bin.unpack("stdnse.format_output function queryResultToTable( resultTable ) local result = {} for k,v in pairs( resultTable ) do if type(v) == 'table' then table.insert(result,k) table.insert(result,queryResultToTable(v)) else table.insert(result,(("%s = %s"):format(tostring(k), tostring(v)))) end end return result end ---------------------------------------------------------------------------------- -- Test-code for debugging purposes below ---------------------------------------------------------------------------------- --- Prints data (string) as Hex values, e.g so it more easily can -- be compared with a packet dump -- @param strData the data in a string format local function printBuffer(strData) local out = '' local ch for i = 1,strData:len() do out = out .." " ch =strData:byte(i) if(ch < 16) then ch = string.format("0%x",ch) else ch = string.format("%x",ch) end --if ch > 64 and ch < 123 then -- out = out .. string.char(ch) --else out = out .. ch --end end print(out) end -- function test() -- local res -- res = versionQuery() -- print(type(res),res:len(),res) -- local out= bin.unpack('C'..#res,res) -- printBuffer(res) -- end --test() return _ENV;