mysql-audit NSE Script

2011-06-17T06:12:01
ID NMAP:MYSQL-AUDIT.NSE
Type nmap
Reporter Patrik Karlsson
Modified 2018-10-18T01:08:19

Description

Audits MySQL database server security configuration against parts of the CIS MySQL v1.0.2 benchmark (the engine can be used for other MySQL audits by creating appropriate audit files).

Script Arguments

mysql-audit.password

the password with which to connect to the database

mysql-audit.username

the username with which to connect to the database

mysql-audit.filename

the name of the file containing the audit rulebase

Example Usage

nmap -p 3306 --script mysql-audit --script-args "mysql-audit.username='root', \
  mysql-audit.password='foobar',mysql-audit.filename='nselib/data/mysql-cis.audit'"

Script Output

PORT     STATE SERVICE
3306/tcp open  mysql
| mysql-audit:
|   CIS MySQL Benchmarks v1.0.2
|       3.1: Skip symbolic links => PASS
|       3.2: Logs not on system partition => PASS
|       3.2: Logs not on database partition => PASS
|       4.1: Supported version of MySQL => REVIEW
|         Version: 5.1.54-1ubuntu4
|       4.4: Remove test database => PASS
|       4.5: Change admin account name => FAIL
|       4.7: Verify Secure Password Hashes => PASS
|       4.9: Wildcards in user hostname => FAIL
|         The following users were found with wildcards in hostname
|           root
|           super
|           super2
|       4.10: No blank passwords => PASS
|       4.11: Anonymous account => PASS
|       5.1: Access to mysql database => REVIEW
|         Verify the following users that have access to the MySQL database
|           user              host
|           root              localhost
|           root              patrik-11
|           root              127.0.0.1
|           debian-sys-maint  localhost
|           root              %
|           super             %
|       5.2: Do not grant FILE privileges to non Admin users => REVIEW
|         The following users were found having the FILE privilege
|           super
|           super2
|       5.3: Do not grant PROCESS privileges to non Admin users => REVIEW
|         The following users were found having the PROCESS privilege
|           super
|       5.4: Do not grant SUPER privileges to non Admin users => REVIEW
|         The following users were found having the SUPER privilege
|           super
|       5.5: Do not grant SHUTDOWN privileges to non Admin users => REVIEW
|         The following users were found having the SHUTDOWN privilege
|           super
|       5.6: Do not grant CREATE USER privileges to non Admin users => REVIEW
|         The following users were found having the CREATE USER privilege
|           super
|       5.7: Do not grant RELOAD privileges to non Admin users => REVIEW
|         The following users were found having the RELOAD privilege
|           super
|       5.8: Do not grant GRANT privileges to non Admin users => PASS
|       6.2: Disable Load data local => FAIL
|       6.3: Disable old password hashing => PASS
|       6.4: Safe show database => FAIL
|       6.5: Secure auth => FAIL
|       6.6: Grant tables => FAIL
|       6.7: Skip merge => FAIL
|       6.8: Skip networking => FAIL
|       6.9: Safe user create => FAIL
|       6.10: Skip symbolic links => FAIL
|
|_      The audit was performed using the db-account: root

Requires

  • mysql
  • nmap
  • shortport
  • stdnse
  • table

                                        
                                            local _G = require "_G"
local mysql = require "mysql"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local table = require "table"

description = [[
Audits MySQL database server security configuration against parts of
the CIS MySQL v1.0.2 benchmark (the engine can be used for other MySQL
audits by creating appropriate audit files).
]]


---
-- @usage
-- nmap -p 3306 --script mysql-audit --script-args "mysql-audit.username='root', \
--   mysql-audit.password='foobar',mysql-audit.filename='nselib/data/mysql-cis.audit'"
--
-- @args mysql-audit.username the username with which to connect to the database
-- @args mysql-audit.password the password with which to connect to the database
-- @args mysql-audit.filename the name of the file containing the audit rulebase
--
-- @output
-- PORT     STATE SERVICE
-- 3306/tcp open  mysql
-- | mysql-audit:
-- |   CIS MySQL Benchmarks v1.0.2
-- |       3.1: Skip symbolic links => PASS
-- |       3.2: Logs not on system partition => PASS
-- |       3.2: Logs not on database partition => PASS
-- |       4.1: Supported version of MySQL => REVIEW
-- |         Version: 5.1.54-1ubuntu4
-- |       4.4: Remove test database => PASS
-- |       4.5: Change admin account name => FAIL
-- |       4.7: Verify Secure Password Hashes => PASS
-- |       4.9: Wildcards in user hostname => FAIL
-- |         The following users were found with wildcards in hostname
-- |           root
-- |           super
-- |           super2
-- |       4.10: No blank passwords => PASS
-- |       4.11: Anonymous account => PASS
-- |       5.1: Access to mysql database => REVIEW
-- |         Verify the following users that have access to the MySQL database
-- |           user              host
-- |           root              localhost
-- |           root              patrik-11
-- |           root              127.0.0.1
-- |           debian-sys-maint  localhost
-- |           root              %
-- |           super             %
-- |       5.2: Do not grant FILE privileges to non Admin users => REVIEW
-- |         The following users were found having the FILE privilege
-- |           super
-- |           super2
-- |       5.3: Do not grant PROCESS privileges to non Admin users => REVIEW
-- |         The following users were found having the PROCESS privilege
-- |           super
-- |       5.4: Do not grant SUPER privileges to non Admin users => REVIEW
-- |         The following users were found having the SUPER privilege
-- |           super
-- |       5.5: Do not grant SHUTDOWN privileges to non Admin users => REVIEW
-- |         The following users were found having the SHUTDOWN privilege
-- |           super
-- |       5.6: Do not grant CREATE USER privileges to non Admin users => REVIEW
-- |         The following users were found having the CREATE USER privilege
-- |           super
-- |       5.7: Do not grant RELOAD privileges to non Admin users => REVIEW
-- |         The following users were found having the RELOAD privilege
-- |           super
-- |       5.8: Do not grant GRANT privileges to non Admin users => PASS
-- |       6.2: Disable Load data local => FAIL
-- |       6.3: Disable old password hashing => PASS
-- |       6.4: Safe show database => FAIL
-- |       6.5: Secure auth => FAIL
-- |       6.6: Grant tables => FAIL
-- |       6.7: Skip merge => FAIL
-- |       6.8: Skip networking => FAIL
-- |       6.9: Safe user create => FAIL
-- |       6.10: Skip symbolic links => FAIL
-- |
-- |_      The audit was performed using the db-account: root

-- Version 0.1
-- Created 05/29/2011 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>

author = "Patrik Karlsson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "safe"}


portrule = shortport.port_or_service(3306, "mysql")
local TEMPLATE_NAME, ADMIN_ACCOUNTS = "", ""

local function fail (err) return stdnse.format_output(false, err) end

local function loadAuditRulebase( filename )
  local rules = {}

  local env = setmetatable({
    test = function(t) table.insert(rules, t) end;
  }, {__index = _G})

  local file, err = loadfile(filename, "t", env)

  if ( not(file) ) then
    return false, fail(("Failed to load rulebase:\n%s"):format(err))
  end


  file()
  TEMPLATE_NAME = env.TEMPLATE_NAME
  ADMIN_ACCOUNTS = env.ADMIN_ACCOUNTS
  return true, rules
end

action = function( host, port )

  local username = stdnse.get_script_args("mysql-audit.username")
  local password = stdnse.get_script_args("mysql-audit.password")
  local filename = stdnse.get_script_args("mysql-audit.filename")

  if ( not(filename) ) then
    return fail("No audit rulebase file was supplied (see mysql-audit.filename)")
  end

  if ( not(username) ) then
    return fail("No username was supplied (see mysql-audit.username)")
  end

  local status, tests = loadAuditRulebase( filename )
  if( not(status) ) then return tests end

  local socket = nmap.new_socket()
  status = socket:connect(host, port)

  local response
  status, response = mysql.receiveGreeting( socket )
  if ( not(status) ) then return response end

  status, response = mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt )

  if ( not(status) ) then return fail("Failed to authenticate") end
  local results = {}

  for _, test in ipairs(tests) do
    local queries = ( "string" == type(test.sql) ) and { test.sql } or test.sql
    local rowstab = {}

    for _, query in ipairs(queries) do
      local row
      status, row = mysql.sqlQuery( socket, query )
      if ( not(status) ) then
        table.insert( results, { ("%s: ERROR: Failed to execute SQL statement"):format(test.id) } )
      else
        table.insert(rowstab, row)
      end
    end

    if ( #rowstab > 0 ) then
      local result_part = {}
      local res = test.check(rowstab)
      local status, data = res.status, res.result
      status = ( res.review and "REVIEW" ) or (status and "PASS" or "FAIL")

      table.insert( result_part, ("%s: %s => %s"):format(test.id, test.desc, status) )
      if ( data ) then
        table.insert(result_part, { data } )
      end
      table.insert( results, result_part )
    end
  end

  socket:close()
  results.name = TEMPLATE_NAME

  table.insert(results, "")
  table.insert(results, {name = "Additional information", ("The audit was performed using the db-account: %s"):format(username),
    ("The following admin accounts were excluded from the audit: %s"):format(table.concat(ADMIN_ACCOUNTS, ","))
  })

return stdnse.format_output(true, { results })
end