mirror of
https://github.com/nmap/nmap.git
synced 2025-12-24 08:29:04 +00:00
o Added two new scripts mysql-query and mysql-dump-hashes, which add support
for performing custom MySQL queries and dump MySQL password hashes. [Patrik Karlsson]
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
# Nmap Changelog ($Id$); -*-text-*-
|
# Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o Added two new scripts mysql-query and mysql-dump-hashes, which add support
|
||||||
|
for performing custom MySQL queries and dump MySQL password hashes. [Patrik
|
||||||
|
Karlsson]
|
||||||
|
|
||||||
o Improved the mysql library to handle multiple columns with the same name,
|
o Improved the mysql library to handle multiple columns with the same name,
|
||||||
added a formatResultset function to format a query response to a table
|
added a formatResultset function to format a query response to a table
|
||||||
suitable for script output. [Patrik Karlsson]
|
suitable for script output. [Patrik Karlsson]
|
||||||
|
|||||||
100
scripts/mysql-dump-hashes.nse
Normal file
100
scripts/mysql-dump-hashes.nse
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
description = [[
|
||||||
|
Dumps the password hashes from an MySQL server in a format suitable for
|
||||||
|
cracking by tools such as John-the-ripper. In order to do so the user
|
||||||
|
needs to have the appropriate DB privileges (root).
|
||||||
|
|
||||||
|
The <code>username</code> and <code>password</code> arguments take precedence
|
||||||
|
over credentials discovered by the mysql-brute and mysql-empty-password
|
||||||
|
scripts.
|
||||||
|
]]
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @usage
|
||||||
|
-- nmap -p 3306 <ip> --script mysql-dump-hashes --script-args='username=root,password=secret'
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE
|
||||||
|
-- 3306/tcp open mysql
|
||||||
|
-- | mysql-dump-hashes:
|
||||||
|
-- | root:*9B500343BC52E2911172EB52AE5CF4847604C6E5
|
||||||
|
-- | debian-sys-maint:*92357EE43977D9228AC9C0D60BB4B4479BD7A337
|
||||||
|
-- |_ toor:*14E65567ABDB5135D0CFD9A70B3032C179A49EE7
|
||||||
|
--
|
||||||
|
-- @args username the username to use to connect to the server
|
||||||
|
-- @args password the password to use to connect to the server
|
||||||
|
--
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"auth", "discovery", "safe"}
|
||||||
|
|
||||||
|
local shortport = require('shortport')
|
||||||
|
local mysql = require('mysql')
|
||||||
|
|
||||||
|
dependencies = {"mysql-empty-password", "mysql-brute"}
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service(3306, "mysql")
|
||||||
|
|
||||||
|
local arg_username = stdnse.get_script_args(SCRIPT_NAME .. ".username")
|
||||||
|
local arg_password = stdnse.get_script_args(SCRIPT_NAME .. ".password") or ""
|
||||||
|
|
||||||
|
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||||
|
|
||||||
|
local function getCredentials()
|
||||||
|
-- first, let's see if the script has any credentials as arguments?
|
||||||
|
if ( arg_username ) then
|
||||||
|
return { [arg_username] = arg_password }
|
||||||
|
-- next, let's see if mysql-brute or mysql-empty-password brought us anything
|
||||||
|
elseif nmap.registry.mysqlusers then
|
||||||
|
-- do we have root credentials?
|
||||||
|
if nmap.registry.mysqlusers['root'] then
|
||||||
|
return { ['root'] = nmap.registry.mysqlusers['root'] }
|
||||||
|
else
|
||||||
|
-- we didn't have root, so let's make sure we loop over them all
|
||||||
|
return nmap.registry.mysqlusers
|
||||||
|
end
|
||||||
|
-- last, no dice, we don't have any credentials at all
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mysqlLogin(socket, username, password)
|
||||||
|
local status, response = mysql.receiveGreeting( socket )
|
||||||
|
if ( not(status) ) then
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
return mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
action = function(host, port)
|
||||||
|
local creds = getCredentials()
|
||||||
|
if ( not(creds) ) then
|
||||||
|
stdnse.print_debug(2, "No credentials were supplied, aborting ...")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
for username, password in pairs(creds) do
|
||||||
|
local socket = nmap.new_socket()
|
||||||
|
if ( not(socket:connect(host, port)) ) then
|
||||||
|
return fail("Failed to connect to server")
|
||||||
|
end
|
||||||
|
|
||||||
|
local status, response = mysqlLogin(socket, username, password)
|
||||||
|
if ( status ) then
|
||||||
|
local query = "SELECT DISTINCT CONCAT(user, ':', password) FROM mysql.user WHERE password <> ''"
|
||||||
|
local status, rows = mysql.sqlQuery( socket, query )
|
||||||
|
socket:close()
|
||||||
|
if ( status ) then
|
||||||
|
result = mysql.formatResultset(rows, { noheaders = true })
|
||||||
|
break
|
||||||
|
end
|
||||||
|
else
|
||||||
|
socket:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ( result ) then
|
||||||
|
return stdnse.format_output(true, result)
|
||||||
|
end
|
||||||
|
end
|
||||||
115
scripts/mysql-query.nse
Normal file
115
scripts/mysql-query.nse
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
description = [[
|
||||||
|
Runs a query against a MySQL database and returns the results as a table.
|
||||||
|
]]
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @usage
|
||||||
|
-- nmap -p 3306 <ip> --script mysql-query --script-args='query="<query>"[,username=<username>,password=<password>]'
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE
|
||||||
|
-- 3306/tcp open mysql
|
||||||
|
-- | mysql-query:
|
||||||
|
-- | host user
|
||||||
|
-- | 127.0.0.1 root
|
||||||
|
-- | localhost debian-sys-maint
|
||||||
|
-- | localhost root
|
||||||
|
-- | ubu1110 root
|
||||||
|
-- |
|
||||||
|
-- | Query: SELECT host, user FROM mysql.user
|
||||||
|
-- |_ User: root
|
||||||
|
--
|
||||||
|
-- @args query the query for which to return the results
|
||||||
|
-- @args username (optional) the username used to authenticate to the database server
|
||||||
|
-- @args password (optional) the password used to authenticate to the database server
|
||||||
|
--
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"auth", "discovery", "safe"}
|
||||||
|
|
||||||
|
local shortport = require('shortport')
|
||||||
|
local mysql = require('mysql')
|
||||||
|
local tab = require('tab')
|
||||||
|
|
||||||
|
dependencies = {"mysql-empty-password", "mysql-brute"}
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service(3306, "mysql")
|
||||||
|
|
||||||
|
local arg_username = stdnse.get_script_args(SCRIPT_NAME .. ".username")
|
||||||
|
local arg_password = stdnse.get_script_args(SCRIPT_NAME .. ".password") or ""
|
||||||
|
local arg_query = stdnse.get_script_args(SCRIPT_NAME .. ".query")
|
||||||
|
local arg_noheaders = stdnse.get_script_args(SCRIPT_NAME .. ".noheaders") or false
|
||||||
|
|
||||||
|
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||||
|
|
||||||
|
local function getCredentials()
|
||||||
|
-- first, let's see if the script has any credentials as arguments?
|
||||||
|
if ( arg_username ) then
|
||||||
|
return { [arg_username] = arg_password }
|
||||||
|
-- next, let's see if mysql-brute or mysql-empty-password brought us anything
|
||||||
|
elseif nmap.registry.mysqlusers then
|
||||||
|
-- do we have root credentials?
|
||||||
|
if nmap.registry.mysqlusers['root'] then
|
||||||
|
return { ['root'] = nmap.registry.mysqlusers['root'] }
|
||||||
|
else
|
||||||
|
-- we didn't have root, so let's make sure we loop over them all
|
||||||
|
return nmap.registry.mysqlusers
|
||||||
|
end
|
||||||
|
-- last, no dice, we don't have any credentials at all
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function mysqlLogin(socket, username, password)
|
||||||
|
local status, response = mysql.receiveGreeting( socket )
|
||||||
|
if ( not(status) ) then
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
return mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
action = function(host, port)
|
||||||
|
if ( not(arg_query) ) then
|
||||||
|
stdnse.print_debug(2, "No query was given, aborting ...")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local creds = getCredentials()
|
||||||
|
if ( not(creds) ) then
|
||||||
|
stdnse.print_debug(2, "No credentials were supplied, aborting ...")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if ( arg_noheaders == '1' or arg_noheaders == 'true' ) then
|
||||||
|
arg_noheaders = true
|
||||||
|
else
|
||||||
|
arg_noheaders = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
local last_error
|
||||||
|
|
||||||
|
for username, password in pairs(creds) do
|
||||||
|
local socket = nmap.new_socket()
|
||||||
|
if ( not(socket:connect(host, port)) ) then
|
||||||
|
return fail("Failed to connect to server")
|
||||||
|
end
|
||||||
|
local status, response = mysqlLogin(socket, username, password)
|
||||||
|
if ( status ) then
|
||||||
|
local status, rs = mysql.sqlQuery( socket, arg_query )
|
||||||
|
socket:close()
|
||||||
|
if ( status ) then
|
||||||
|
result = mysql.formatResultset(rs, { noheaders = arg_noheaders })
|
||||||
|
result = ("%s\nQuery: %s\nUser: %s"):format(result, arg_query, username)
|
||||||
|
last_error = nil
|
||||||
|
break
|
||||||
|
else
|
||||||
|
last_error = rs
|
||||||
|
end
|
||||||
|
else
|
||||||
|
socket:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return stdnse.format_output(true, (last_error and ("ERROR: %s"):format(last_error) or result))
|
||||||
|
end
|
||||||
@@ -230,8 +230,10 @@ Entry { filename = "ms-sql-xp-cmdshell.nse", categories = { "intrusive", } }
|
|||||||
Entry { filename = "mysql-audit.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "mysql-audit.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "mysql-brute.nse", categories = { "brute", "intrusive", } }
|
Entry { filename = "mysql-brute.nse", categories = { "brute", "intrusive", } }
|
||||||
Entry { filename = "mysql-databases.nse", categories = { "discovery", "intrusive", } }
|
Entry { filename = "mysql-databases.nse", categories = { "discovery", "intrusive", } }
|
||||||
|
Entry { filename = "mysql-dump-hashes.nse", categories = { "auth", "discovery", "safe", } }
|
||||||
Entry { filename = "mysql-empty-password.nse", categories = { "auth", "intrusive", } }
|
Entry { filename = "mysql-empty-password.nse", categories = { "auth", "intrusive", } }
|
||||||
Entry { filename = "mysql-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "mysql-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
|
Entry { filename = "mysql-query.nse", categories = { "auth", "discovery", "safe", } }
|
||||||
Entry { filename = "mysql-users.nse", categories = { "auth", "intrusive", } }
|
Entry { filename = "mysql-users.nse", categories = { "auth", "intrusive", } }
|
||||||
Entry { filename = "mysql-variables.nse", categories = { "discovery", "intrusive", } }
|
Entry { filename = "mysql-variables.nse", categories = { "discovery", "intrusive", } }
|
||||||
Entry { filename = "nat-pmp-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "nat-pmp-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
|
|||||||
Reference in New Issue
Block a user