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