diff --git a/scripts/mysql-info.nse b/scripts/mysql-info.nse index b4b097796..ebe802903 100644 --- a/scripts/mysql-info.nse +++ b/scripts/mysql-info.nse @@ -1,5 +1,8 @@ local bit = require "bit" -local comm = require "comm" +local mysql = require "mysql" +local nmap = require "nmap" +local stdnse = require "stdnse" +local table = require "table" description = [[ Connects to a MySQL server and prints information such as the protocol and @@ -13,12 +16,27 @@ isn't run (see the portrule). --- -- @output -- 3306/tcp open mysql --- | mysql-info: Protocol: 10 --- | Version: 5.0.51a-3ubuntu5.1 --- | Thread ID: 7 --- | Some Capabilities: Connect with DB, Transactions, Secure Connection --- | Status: Autocommit --- |_ Salt: bYyt\NQ/4V6IN+*3`imj +-- | mysql-info: +-- | Protocol: 10 +-- | Version: 5.0.51a-3ubuntu5.1 +-- | Thread ID: 7 +-- | Capabilities flags: 40968 +-- | Some Capabilities: ConnectWithDatabase, SupportsTransactions, Support41Auth +-- | Status: Autocommit +-- |_ Salt: bYyt\NQ/4V6IN+*3`imj +-- +--@xmloutput +-- 10 +-- 5.0.51a-3ubuntu5.1 +-- 7 +-- 40968 +-- +-- ConnectWithDatabase +-- SupportsTransactions +-- Support41Auth +--
+-- Autocommit +-- bYyt\NQ/4V6IN+*3`imj -- Many thanks to jah (jah@zadkiel.plus.com) for testing and enhancements @@ -28,82 +46,19 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = { "default", "discovery", "safe" } - ---- Grabs NUL-terminated string ---@param orig Start of the string ---@return The NUL-terminated string -local getstring = function(orig) - return orig:match("^([^\0]*)"); -end - ---- Converts two bytes into a number ---@param num Start of the two bytes ---@return The converted number -local ntohs = function(num) - local b1 = bit.band(num:byte(1), 255) - local b2 = bit.band(num:byte(2), 255) - - return bit.bor(b1, bit.lshift(b2, 8)) -end - ---- Converts three bytes into a number ---@param num Start of the three bytes ---@return The converted number -local ntoh3 = function(num) - local b1 = bit.band(num:byte(1), 255) - local b2 = bit.band(num:byte(2), 255) - local b3 = bit.band(num:byte(3), 255) - - return bit.bor(b1, bit.lshift(b2, 8), bit.lshift(b3, 16)) -end - ---- Converts four bytes into a number ---@param num Start of the four bytes ---@return The converted number -local ntohl = function(num) - local b1 = bit.band(num:byte(1), 255) - local b2 = bit.band(num:byte(2), 255) - local b3 = bit.band(num:byte(3), 255) - local b4 = bit.band(num:byte(4), 255) - - return bit.bor(b1, bit.lshift(b2, 8), bit.lshift(b3, 16), bit.lshift(b4, 24)) -end - --- Converts a number to a string description of the capabilities --@param num Start of the capabilities data ---@return String describing the capabilities offered +--@return table containing the names of the capabilities offered local capabilities = function(num) - local caps = "" + local caps = {} - if bit.band(num, 1) > 0 then - caps = caps .. "Long Passwords, " + for k, v in pairs(mysql.Capabilities) do + if bit.band(num, v) > 0 then + caps[#caps+1] = k + end end - if bit.band(num, 8) > 0 then - caps = caps .. "Connect with DB, " - end - - if bit.band(num, 32) > 0 then - caps = caps .. "Compress, " - end - - if bit.band(num, 64) > 0 then - caps = caps .. "ODBC, " - end - - if bit.band(num, 2048) > 0 then - caps = caps .. "SSL, " - end - - if bit.band(num, 8192) > 0 then - caps = caps .. "Transactions, " - end - - if bit.band(num, 32768) > 0 then - caps = caps .. "Secure Connection, " - end - - return caps:gsub(", $", "") + return caps end portrule = function(host, port) @@ -118,90 +73,51 @@ portrule = function(host, port) end action = function(host, port) - local output = "" + local output = stdnse.output_table() + local socket = nmap.new_socket() + socket:set_timeout(5000) - local status, response = comm.get_banner(host, port, {timeout=5000}) + local status, err = socket:connect(host, port) if not status then - return + stdnse.print_debug("%s: error connecting: %s", SCRIPT_NAME, err) + return nil end - local length = ntoh3(response:sub(1, 3)) + local status, info = mysql.receiveGreeting(socket) - if length ~= response:len() - 4 then - return "Invalid greeting (Not MySQL?)" + if not status then + stdnse.print_debug("%s: MySQL error: %s", SCRIPT_NAME, info) + output["MySQL Error"] = info + if nmap.verbosity() > 1 then + return output + else + return nil + end end - -- Keeps track of where we are in the binary data - local offset = 1 + 4 + output["Protocol"] = info.proto + output["Version"] = info.version + output["Thread ID"] = info.threadid - local protocol = response:byte(offset) - - offset = offset + 1 - - -- If a 0xff is here instead of the protocol, an error occurred. - -- Pass it along to the user.. - if (protocol == 255) then - output = "MySQL Error detected!\n" - - local sqlerrno = ntohs(response:sub(offset, offset + 2)) - - offset = offset + 2 - - local sqlerrstr = response:sub(offset) - - output = output .. "Error Code was: " .. sqlerrno .. "\n" - - output = output .. sqlerrstr - - return output + output["Capabilities flags"] = info.capabilities + local caps = capabilities(info.capabilities) + if #caps > 0 then + setmetatable(caps, { + __tostring = function (self) + return table.concat(self, ", ") + end + }) + output["Some Capabilities"] = caps end - local version = getstring(response:sub(offset)) - - offset = offset + version:len() + 1 - - local threadid = ntohl(response:sub(offset, offset + 4)) - - offset = offset + 4 - - local salt = getstring(response:sub(offset)) - - offset = offset + salt:len() + 1 - - local caps = capabilities(ntohs(response:sub(offset, offset + 2))) - - offset = offset + 2 - - offset = offset + 1 - - local status = "" - - if ntohs(response:sub(offset, offset + 2)) == 2 then - status = "Autocommit" + if info.status == 2 then + output["Status"] = "Autocommit" + else + output["Status"] = info.status end - offset = offset + 2 - - offset = offset + 13 -- unused - - if response:len() - offset + 1 == 13 then - salt = salt .. getstring(response:sub(offset)) - end - - output = output .. "Protocol: " .. protocol .. "\n" - output = output .. "Version: " .. version .. "\n" - output = output .. "Thread ID: " .. threadid .. "\n" - - if caps:len() > 0 then - output = output .. "Some Capabilities: " .. caps .. "\n" - end - - if status:len() > 0 then - output = output .. "Status: " .. status .. "\n" - end - - output = output .. "Salt: " .. salt .. "\n" + output["Salt"] = info.salt return output end