mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 14:11:29 +00:00
o [NSE] Added a library for Microsoft SQL Server and 7 new scripts. The new
scripts are:
- ms-sql-brute.nse uses the unpwdb library to guess credentials for MSSQL
- ms-sql-config retrieves various configuration details from the server
- ms-sql-empty-password checks if the sa account has an empty password
- ms-sql-hasdbaccess lists database access per user
- ms-sql-query add support for running custom queries against the database
- ms-sql-tables lists databases, tables, columns and datatypes with optional
keyword filtering
- ms-sql-xp-cmdshell adds support for OS command execution to privileged
users
[Patrik]
This commit is contained in:
13
CHANGELOG
13
CHANGELOG
@@ -1,5 +1,18 @@
|
|||||||
# Nmap Changelog ($Id$); -*-text-*-
|
# Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o [NSE] Added a library for Microsoft SQL Server and 7 new scripts. The new
|
||||||
|
scripts are:
|
||||||
|
- ms-sql-brute.nse uses the unpwdb library to guess credentials for MSSQL
|
||||||
|
- ms-sql-config retrieves various configuration details from the server
|
||||||
|
- ms-sql-empty-password checks if the sa account has an empty password
|
||||||
|
- ms-sql-hasdbaccess lists database access per user
|
||||||
|
- ms-sql-query add support for running custom queries against the database
|
||||||
|
- ms-sql-tables lists databases, tables, columns and datatypes with optional
|
||||||
|
keyword filtering
|
||||||
|
- ms-sql-xp-cmdshell adds support for OS command execution to privileged
|
||||||
|
users
|
||||||
|
[Patrik]
|
||||||
|
|
||||||
o [NSE] Fixed bug in rpc.lua library that incorrectly required file handles
|
o [NSE] Fixed bug in rpc.lua library that incorrectly required file handles
|
||||||
to be 32 octects when calling the ReadDir function. The bug was reported by
|
to be 32 octects when calling the ReadDir function. The bug was reported by
|
||||||
Djalal Harouni. [Patrik]
|
Djalal Harouni. [Patrik]
|
||||||
|
|||||||
1030
nselib/mssql.lua
Normal file
1030
nselib/mssql.lua
Normal file
File diff suppressed because it is too large
Load Diff
93
scripts/ms-sql-brute.nse
Normal file
93
scripts/ms-sql-brute.nse
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
description = [[
|
||||||
|
Performs password guessing against Microsoft SQL Server (mssql)
|
||||||
|
]]
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"auth", "intrusive"}
|
||||||
|
|
||||||
|
require 'shortport'
|
||||||
|
require 'stdnse'
|
||||||
|
require 'mssql'
|
||||||
|
require 'unpwdb'
|
||||||
|
|
||||||
|
---
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE
|
||||||
|
-- 1433/tcp open ms-sql-s
|
||||||
|
-- | mssql-brute:
|
||||||
|
-- | webshop_reader:secret => Login Success
|
||||||
|
-- | testuser:secret1234 => Must change password at next logon
|
||||||
|
-- |_ lordvader:secret1234 => Login Success
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Version 0.1
|
||||||
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service(1433, "ms-sql-s")
|
||||||
|
|
||||||
|
action = function( host, port )
|
||||||
|
|
||||||
|
local result, response, status, aborted = {}, nil, nil, false
|
||||||
|
local valid_accounts = {}
|
||||||
|
local usernames, passwords
|
||||||
|
local username, password
|
||||||
|
local max_time = unpwdb.timelimit() ~= nil and unpwdb.timelimit() * 1000 or -1
|
||||||
|
local clock_start = nmap.clock_ms()
|
||||||
|
local helper = mssql.Helper:new()
|
||||||
|
|
||||||
|
status, usernames = unpwdb.usernames()
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\nFailed to load usernames.lst"
|
||||||
|
end
|
||||||
|
status, passwords = unpwdb.passwords()
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\nFailed to load usernames.lst"
|
||||||
|
end
|
||||||
|
|
||||||
|
for username in usernames do
|
||||||
|
for password in passwords do
|
||||||
|
|
||||||
|
if max_time>0 and nmap.clock_ms() - clock_start > max_time then
|
||||||
|
aborted=true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
status, result = helper:Connect(host, port)
|
||||||
|
if( not(status) ) then
|
||||||
|
return " \n\n" .. result
|
||||||
|
end
|
||||||
|
|
||||||
|
stdnse.print_debug( "Trying %s/%s ...", username, password )
|
||||||
|
status, result = helper:Login( username, password, "tempdb", host.ip )
|
||||||
|
helper:Disconnect()
|
||||||
|
|
||||||
|
if status then
|
||||||
|
-- Add credentials for other mysql scripts to use
|
||||||
|
table.insert( valid_accounts, string.format("%s:%s => %s", username, password:len()>0 and password or "<empty>", result ) )
|
||||||
|
-- don't add accounts that need to change passwords to the registry
|
||||||
|
if ( result ~= "Login Success") then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if nmap.registry.mssqlusers == nil then
|
||||||
|
nmap.registry.mssqlusers = {}
|
||||||
|
end
|
||||||
|
nmap.registry.mssqlusers[username]=password
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
passwords("reset")
|
||||||
|
end
|
||||||
|
|
||||||
|
local output = stdnse.format_output(true, valid_accounts)
|
||||||
|
|
||||||
|
if max_time > 0 and aborted then
|
||||||
|
output = output .. string.format(" \n\nscript aborted execution after %d seconds", max_time/1000 )
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end
|
||||||
109
scripts/ms-sql-config.nse
Normal file
109
scripts/ms-sql-config.nse
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
description = [[
|
||||||
|
Queries Microsoft SQL Server (MSSQL) for a list of:
|
||||||
|
* Databases
|
||||||
|
* Linked Servers
|
||||||
|
* Configuration settings
|
||||||
|
]]
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"discovery", "safe"}
|
||||||
|
|
||||||
|
require 'shortport'
|
||||||
|
require 'stdnse'
|
||||||
|
require 'mssql'
|
||||||
|
|
||||||
|
dependencies = {"ms-sql-brute", "ms-sql-empty-password"}
|
||||||
|
|
||||||
|
--
|
||||||
|
-- @args mssql.username specifies the username to use to connect to
|
||||||
|
-- the server. This option overrides any accounts found by
|
||||||
|
-- the mssql-brute and mssql-empty-password scripts.
|
||||||
|
--
|
||||||
|
-- @args mssql.password specifies the password to use to connect to
|
||||||
|
-- the server. This option overrides any accounts found by
|
||||||
|
-- the mssql-brute and mssql-empty-password scripts.
|
||||||
|
--
|
||||||
|
-- @args mssql-config.showall if set shows all configuration options.
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Version 0.1
|
||||||
|
-- Created 04/02/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service(1433, "ms-sql-s")
|
||||||
|
|
||||||
|
action = function( host, port )
|
||||||
|
|
||||||
|
local status, helper, response
|
||||||
|
local username = nmap.registry.args['mssql.username']
|
||||||
|
local password = nmap.registry.args['mssql.password'] or ""
|
||||||
|
local result, result_part = {}, {}
|
||||||
|
local conf_filter = ( nmap.registry.args['mssql-config.showall'] ) and "" or " WHERE configuration_id > 16384"
|
||||||
|
local db_filter = ( nmap.registry.args['mssql-config.showall'] ) and "" or " WHERE name NOT IN ('master','model','tempdb','msdb')"
|
||||||
|
|
||||||
|
local queries = {
|
||||||
|
[2]={ ["Configuration"] = [[ SELECT name,
|
||||||
|
cast(value as varchar) value,
|
||||||
|
cast(value_in_use as varchar) inuse,
|
||||||
|
description
|
||||||
|
FROM sys.configurations ]] .. conf_filter },
|
||||||
|
[3]={ ["Linked Servers"] = [[ SELECT srvname, srvproduct, providername
|
||||||
|
FROM master..sysservers
|
||||||
|
WHERE srvid > 0 ]] },
|
||||||
|
[1]={ ["Databases"] = [[ CREATE TABLE #nmap_dbs(name varchar(255), db_size varchar(255), owner varchar(255),
|
||||||
|
dbid int, created datetime, status varchar(512), compatibility_level int )
|
||||||
|
INSERT INTO #nmap_dbs EXEC sp_helpdb
|
||||||
|
SELECT name, db_size, owner
|
||||||
|
FROM #nmap_dbs ]] .. db_filter .. [[
|
||||||
|
DROP DATABASE #nmap_dbs ]] }
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( not(username) and nmap.registry.mssqlusers ) then
|
||||||
|
-- do we have a sysadmin?
|
||||||
|
if ( nmap.registry.mssqlusers.sa ) then
|
||||||
|
username = "sa"
|
||||||
|
password = nmap.registry.mssqlusers.sa
|
||||||
|
else
|
||||||
|
-- ok were stuck with some non sysadmin account, just get the first one
|
||||||
|
for user, pass in pairs(nmap.registry.mssqlusers) do
|
||||||
|
username = user
|
||||||
|
password = pass
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If we don't have a valid username, simply fail silently
|
||||||
|
if ( not(username) ) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
helper = mssql.Helper:new()
|
||||||
|
status, response = helper:Connect(host, port)
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\n" .. response
|
||||||
|
end
|
||||||
|
|
||||||
|
status, response = helper:Login( username, password, nil, host.ip )
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\nERROR: " .. response
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, v in ipairs( queries ) do
|
||||||
|
for header, query in pairs(v) do
|
||||||
|
status, result_part = helper:Query( query )
|
||||||
|
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\nERROR: " .. result_part
|
||||||
|
end
|
||||||
|
result_part = mssql.Util.FormatOutputTable( result_part, true )
|
||||||
|
result_part.name = header
|
||||||
|
table.insert( result, result_part )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
helper:Disconnect()
|
||||||
|
|
||||||
|
return stdnse.format_output( true, result )
|
||||||
|
|
||||||
|
end
|
||||||
52
scripts/ms-sql-empty-password.nse
Normal file
52
scripts/ms-sql-empty-password.nse
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
description = [[
|
||||||
|
Attempts to authenticate using an empty password for the sysadmin (sa) account.
|
||||||
|
]]
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"auth","intrusive"}
|
||||||
|
|
||||||
|
require 'shortport'
|
||||||
|
require 'stdnse'
|
||||||
|
require 'mssql'
|
||||||
|
|
||||||
|
---
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE
|
||||||
|
-- 1433/tcp open ms-sql-s
|
||||||
|
-- | mssql-empty-password:
|
||||||
|
-- |_ sa:<empty> => Login Correct
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Version 0.1
|
||||||
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service(1433, "ms-sql-s")
|
||||||
|
|
||||||
|
action = function( host, port )
|
||||||
|
|
||||||
|
local helper, status, result
|
||||||
|
local username, password, database, valid_accounts = "sa", "", "tempdb", {}
|
||||||
|
|
||||||
|
helper = mssql.Helper:new()
|
||||||
|
status, result = helper:Connect(host, port)
|
||||||
|
|
||||||
|
if( not(status) ) then
|
||||||
|
return " \n\n" .. result
|
||||||
|
end
|
||||||
|
|
||||||
|
status, result = helper:Login( username, password, database, host.ip )
|
||||||
|
helper:Disconnect()
|
||||||
|
|
||||||
|
if status then
|
||||||
|
nmap.registry.mssqlusers = nmap.registry.mssqlusers or {}
|
||||||
|
nmap.registry.mssqlusers[username]=password
|
||||||
|
|
||||||
|
table.insert( valid_accounts, string.format("%s:%s => Login Success", username, password:len()>0 and password or "<empty>" ) )
|
||||||
|
end
|
||||||
|
|
||||||
|
return stdnse.format_output(true, valid_accounts)
|
||||||
|
|
||||||
|
end
|
||||||
143
scripts/ms-sql-hasdbaccess.nse
Normal file
143
scripts/ms-sql-hasdbaccess.nse
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
description = [[
|
||||||
|
Queries Microsoft SQL Server (MSSQL) for a list of databases a user has access to.
|
||||||
|
]]
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"auth", "discovery","safe"}
|
||||||
|
|
||||||
|
require 'shortport'
|
||||||
|
require 'stdnse'
|
||||||
|
require 'mssql'
|
||||||
|
|
||||||
|
dependencies = {"ms-sql-brute", "ms-sql-empty-password"}
|
||||||
|
---
|
||||||
|
-- @args mssql.username specifies the username to use to connect to
|
||||||
|
-- the server. This option overrides any accounts found by
|
||||||
|
-- the mssql-brute and mssql-empty-password scripts.
|
||||||
|
--
|
||||||
|
-- @args mssql.password specifies the password to use to connect to
|
||||||
|
-- the server. This option overrides any accounts found by
|
||||||
|
-- the mssql-brute and mssql-empty-password scripts.
|
||||||
|
--
|
||||||
|
-- @args mssql-hasdbaccess.limit limits the amount of databases per-user
|
||||||
|
-- that are returned (default 5). If set to zero or less all
|
||||||
|
-- databases the user has access to are returned.
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE
|
||||||
|
-- 1433/tcp open ms-sql-s
|
||||||
|
-- | mssql-hasdbaccess:
|
||||||
|
-- | webshop_reader
|
||||||
|
-- | dbname owner
|
||||||
|
-- | hr sa
|
||||||
|
-- | finance sa
|
||||||
|
-- | webshop sa
|
||||||
|
-- | lordvader
|
||||||
|
-- | dbname owner
|
||||||
|
-- | testdb CQURE-NET\Administr
|
||||||
|
-- |_ webshop sa
|
||||||
|
|
||||||
|
--
|
||||||
|
-- The script needs an account with the sysadmin server role to work.
|
||||||
|
-- It needs to be fed credentials through the script arguments or from
|
||||||
|
-- the scripts mssq-brute or mssq-empty-password.
|
||||||
|
--
|
||||||
|
-- When run, the script iterates over the credentials and attempts to run
|
||||||
|
-- the command until either all credentials are exhausted or until the
|
||||||
|
-- command is executed.
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Version 0.1
|
||||||
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service(1433, "ms-sql-s")
|
||||||
|
|
||||||
|
local function table_contains( tbl, val )
|
||||||
|
for k,v in pairs(tbl) do
|
||||||
|
if ( v == val ) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
action = function( host, port )
|
||||||
|
|
||||||
|
local status, result, helper, rs
|
||||||
|
local username = nmap.registry.args['mssql.username']
|
||||||
|
local password = nmap.registry.args['mssql.password'] or ""
|
||||||
|
local creds
|
||||||
|
local query, limit
|
||||||
|
local output = {}
|
||||||
|
local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" }
|
||||||
|
|
||||||
|
local RS_LIMIT = nmap.registry.args["mssql-hasdbaccess.limit"] and tonumber(nmap.registry.args["mssql-hasdbaccess.limit"]) or 5
|
||||||
|
|
||||||
|
if ( RS_LIMIT <= 0 ) then
|
||||||
|
limit = ""
|
||||||
|
else
|
||||||
|
limit = string.format( "TOP %d", RS_LIMIT )
|
||||||
|
end
|
||||||
|
|
||||||
|
local query = { [[CREATE table #hasaccess(dbname varchar(255), owner varchar(255),
|
||||||
|
DboOnly bit, ReadOnly bit, SingelUser bit, Detached bit,
|
||||||
|
Suspect bit, Offline bit, InLoad bit, EmergencyMode bit,
|
||||||
|
StandBy bit, [ShutDown] bit, InRecovery bit, NotRecovered bit )]],
|
||||||
|
|
||||||
|
|
||||||
|
"INSERT INTO #hasaccess EXEC sp_MShasdbaccess",
|
||||||
|
("SELECT %s dbname, owner FROM #hasaccess WHERE dbname NOT IN(%s)"):format(limit, stdnse.strjoin(",", exclude_dbs)),
|
||||||
|
"DROP TABLE #hasaccess" }
|
||||||
|
|
||||||
|
if ( username ) then
|
||||||
|
creds = {}
|
||||||
|
creds[username] = password
|
||||||
|
elseif ( not(username) and nmap.registry.mssqlusers ) then
|
||||||
|
-- do we have a sysadmin?
|
||||||
|
creds = nmap.registry.mssqlusers
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If we don't have valid creds, simply fail silently
|
||||||
|
if ( not(creds) ) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for username, password in pairs( creds ) do
|
||||||
|
helper = mssql.Helper:new()
|
||||||
|
status, result = helper:Connect(host, port)
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\n" .. result
|
||||||
|
end
|
||||||
|
|
||||||
|
status, result = helper:Login( username, password, nil, host.ip )
|
||||||
|
if ( not(status) ) then
|
||||||
|
stdnse.print_debug("ERROR: %s", result)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, q in pairs(query) do
|
||||||
|
status, result = helper:Query( q )
|
||||||
|
if ( status ) then
|
||||||
|
-- Only the SELECT statement should produce output
|
||||||
|
if ( #result.rows > 0 ) then
|
||||||
|
rs = result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
helper:Disconnect()
|
||||||
|
|
||||||
|
if ( status ) then
|
||||||
|
result = mssql.Util.FormatOutputTable( rs, true )
|
||||||
|
result.name = username
|
||||||
|
if ( RS_LIMIT > 0 ) then
|
||||||
|
result.name = result.name .. (" (Showing %d first results)"):format(RS_LIMIT)
|
||||||
|
end
|
||||||
|
table.insert( output, result )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stdnse.format_output( true, output )
|
||||||
|
|
||||||
|
end
|
||||||
96
scripts/ms-sql-query.nse
Normal file
96
scripts/ms-sql-query.nse
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
description = [[
|
||||||
|
Runs a Query against Microsoft SQL Server (MSSQL).
|
||||||
|
]]
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"discovery", "safe"}
|
||||||
|
|
||||||
|
require 'shortport'
|
||||||
|
require 'stdnse'
|
||||||
|
require 'mssql'
|
||||||
|
|
||||||
|
dependencies = {"ms-sql-brute", "ms-sql-empty-password"}
|
||||||
|
|
||||||
|
--
|
||||||
|
-- @args mssql-query.query specifies the query to run against the server.
|
||||||
|
-- (default SELECT @@version version)
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
--
|
||||||
|
-- PORT STATE SERVICE
|
||||||
|
-- 1433/tcp open ms-sql-s
|
||||||
|
-- | mssql-query:
|
||||||
|
-- |
|
||||||
|
-- | Microsoft SQL Server 2005 - 9.00.3068.00 (Intel X86)
|
||||||
|
-- | Feb 26 2008 18:15:01
|
||||||
|
-- | Copyright (c) 1988-2005 Microsoft Corporation
|
||||||
|
-- |_ Express Edition on Windows NT 5.2 (Build 3790: Service Pack 2)
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Version 0.1
|
||||||
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service(1433, "ms-sql-s")
|
||||||
|
|
||||||
|
action = function( host, port )
|
||||||
|
|
||||||
|
local status, result, helper
|
||||||
|
local username = nmap.registry.args['mssql.username']
|
||||||
|
local password = nmap.registry.args['mssql.password'] or ""
|
||||||
|
-- the tempdb should be a safe guess, anyway the library is set up
|
||||||
|
-- to continue even if the DB is not accessible to the user
|
||||||
|
local database = nmap.registry.args['mssql.database'] or "tempdb"
|
||||||
|
local query = nmap.registry.args['mssql-query.query'] or "SELECT @@version version"
|
||||||
|
|
||||||
|
if ( not(username) and nmap.registry.mssqlusers ) then
|
||||||
|
-- do we have a sysadmin?
|
||||||
|
if ( nmap.registry.mssqlusers.sa ) then
|
||||||
|
username = "sa"
|
||||||
|
password = nmap.registry.mssqlusers.sa
|
||||||
|
else
|
||||||
|
-- ok were stuck with some n00b account, just get the first one
|
||||||
|
for user, pass in pairs(nmap.registry.mssqlusers) do
|
||||||
|
username = user
|
||||||
|
password = pass
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If we don't have a valid username, simply fail silently
|
||||||
|
if ( not(username) ) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
helper = mssql.Helper:new()
|
||||||
|
status, result = helper:Connect(host, port)
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\n" .. result
|
||||||
|
end
|
||||||
|
|
||||||
|
status, result = helper:Login( username, password, database, host.ip )
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\nERROR: " .. result
|
||||||
|
end
|
||||||
|
|
||||||
|
status, result = helper:Query( query )
|
||||||
|
helper:Disconnect()
|
||||||
|
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\nERROR: " .. result
|
||||||
|
end
|
||||||
|
|
||||||
|
result = mssql.Util.FormatOutputTable( result, true )
|
||||||
|
if ( not(nmap.registry.args['mssql-query.query']) ) then
|
||||||
|
table.insert(result, 1, query)
|
||||||
|
result = stdnse.format_output( true, result )
|
||||||
|
result = "(Use --script-args=mssql-query.query='<QUERY>' to change query.)" .. result
|
||||||
|
else
|
||||||
|
result = stdnse.format_output( true, result )
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
end
|
||||||
251
scripts/ms-sql-tables.nse
Normal file
251
scripts/ms-sql-tables.nse
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
description = [[
|
||||||
|
Queries Microsoft SQL Server (MSSQL) for a list of tables per database.
|
||||||
|
]]
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"discovery", "safe"}
|
||||||
|
|
||||||
|
require 'shortport'
|
||||||
|
require 'stdnse'
|
||||||
|
require 'mssql'
|
||||||
|
|
||||||
|
dependencies = {"ms-sql-brute", "ms-sql-empty-password"}
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @args mssql.username specifies the username to use to connect to
|
||||||
|
-- the server. This option overrides any accounts found by
|
||||||
|
-- the mssql-brute and mssql-empty-password scripts.
|
||||||
|
--
|
||||||
|
-- @args mssql.password specifies the password to use to connect to
|
||||||
|
-- the server. This option overrides any accounts found by
|
||||||
|
-- the mssql-brute and mssql-empty-password scripts.
|
||||||
|
--
|
||||||
|
-- @args mssql-tables.maxdb Limits the amount of databases that are
|
||||||
|
-- processed and returned (default 5). If set to zero or less
|
||||||
|
-- all databases are processed.
|
||||||
|
--
|
||||||
|
-- @args mssql-tables.maxtables Limits the amount of tables returned
|
||||||
|
-- (default 5). If set to zero or less all tables are returned.
|
||||||
|
--
|
||||||
|
-- @args mssql-tables.keywords If set shows only tables or columns matching
|
||||||
|
-- the keywords
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE
|
||||||
|
-- 1433/tcp open ms-sql-s
|
||||||
|
-- | mssql-tables:
|
||||||
|
-- | webshop
|
||||||
|
-- | table column type length
|
||||||
|
-- | payments user_id int 4
|
||||||
|
-- | payments purchase_id int 4
|
||||||
|
-- | payments cardholder varchar 50
|
||||||
|
-- | payments cardtype varchar 50
|
||||||
|
-- | payments cardno varchar 50
|
||||||
|
-- | payments expiry varchar 50
|
||||||
|
-- | payments cvv varchar 4
|
||||||
|
-- | products id int 4
|
||||||
|
-- | products manu varchar 50
|
||||||
|
-- | products model varchar 50
|
||||||
|
-- | products productname varchar 100
|
||||||
|
-- | products price float 8
|
||||||
|
-- | products imagefile varchar 255
|
||||||
|
-- | products quantity int 4
|
||||||
|
-- | products keywords varchar 100
|
||||||
|
-- | products description text 16
|
||||||
|
-- | users id int 4
|
||||||
|
-- | users username varchar 50
|
||||||
|
-- | users password varchar 50
|
||||||
|
-- |_ users fullname varchar 100
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- The sysdatabase table should be accessible by more or less everyone
|
||||||
|
-- The script attempts to use the sa account over some n00b if it has
|
||||||
|
-- the password in the registry. If not the first account in the
|
||||||
|
-- registry is used.
|
||||||
|
--
|
||||||
|
-- Once we have a list of DBs we iterate over it and attempt to extract
|
||||||
|
-- table names. In order for this to succeed we need to have either
|
||||||
|
-- sysadmin privileges or an account with access to the db. So, for each
|
||||||
|
-- db we successfully enumerate tables from we mark as finnished, we then
|
||||||
|
-- iterate over our know user accounts until either we exhausted our users
|
||||||
|
-- or we found all tables in all dbs.
|
||||||
|
--
|
||||||
|
-- Oh, and exclude all MS default dbs from this excercise.
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Version 0.1
|
||||||
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
|
-- Revised 04/02/2010 - v0.2
|
||||||
|
-- - Added support for filters
|
||||||
|
-- - Changed output formatting of restrictions
|
||||||
|
-- - Added parameter information in output if parameters are using their
|
||||||
|
-- defaults.
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service(1433, "ms-sql-s")
|
||||||
|
|
||||||
|
local function table_contains( tbl, val )
|
||||||
|
for k,v in pairs(tbl) do
|
||||||
|
if ( v == val ) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
action = function( host, port )
|
||||||
|
|
||||||
|
local status, result, dbs, tables, helper
|
||||||
|
local username = nmap.registry.args['mssql.username']
|
||||||
|
local password = nmap.registry.args['mssql.password'] or ""
|
||||||
|
|
||||||
|
local output = {}
|
||||||
|
local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" }
|
||||||
|
local db_query
|
||||||
|
local done_dbs = {}
|
||||||
|
local creds = {}
|
||||||
|
local db_limit, tbl_limit
|
||||||
|
|
||||||
|
local DB_COUNT = nmap.registry.args["mssql-tables.maxdb"] and tonumber(nmap.registry.args["mssql-tables.maxdb"]) or 5
|
||||||
|
local TABLE_COUNT = nmap.registry.args["mssql-tables.maxtables"] and tonumber(nmap.registry.args["mssql-tables.maxtables"]) or 2
|
||||||
|
local keywords_filter = ""
|
||||||
|
|
||||||
|
if ( DB_COUNT <= 0 ) then
|
||||||
|
db_limit = ""
|
||||||
|
else
|
||||||
|
db_limit = string.format( "TOP %d", DB_COUNT )
|
||||||
|
end
|
||||||
|
if (TABLE_COUNT <= 0 ) then
|
||||||
|
tbl_limit = ""
|
||||||
|
else
|
||||||
|
tbl_limit = string.format( "TOP %d", TABLE_COUNT )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Build the keyword filter
|
||||||
|
if ( nmap.registry.args['mssql-tables.keywords'] ) then
|
||||||
|
local keywords = nmap.registry.args['mssql-tables.keywords']
|
||||||
|
local tmp_tbl = {}
|
||||||
|
|
||||||
|
if( type(keywords) == 'string' ) then
|
||||||
|
keywords = { keywords }
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, v in ipairs(keywords) do
|
||||||
|
table.insert(tmp_tbl, ("'%s'"):format(v))
|
||||||
|
end
|
||||||
|
|
||||||
|
keywords_filter = (" AND ( so.name IN (%s) or sc.name IN (%s) ) "):format(
|
||||||
|
stdnse.strjoin(",", tmp_tbl),
|
||||||
|
stdnse.strjoin(",", tmp_tbl)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
db_query = ("SELECT %s name from master..sysdatabases WHERE name NOT IN (%s)"):format(db_limit, stdnse.strjoin(",", exclude_dbs))
|
||||||
|
|
||||||
|
if ( username ) then
|
||||||
|
creds[username] = password
|
||||||
|
elseif ( not(username) and nmap.registry.mssqlusers ) then
|
||||||
|
-- do we have a sysadmin?
|
||||||
|
if ( nmap.registry.mssqlusers.sa ) then
|
||||||
|
creds["sa"] = nmap.registry.mssqlusers.sa
|
||||||
|
else
|
||||||
|
creds = nmap.registry.mssqlusers
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If we don't have valid creds, simply fail silently
|
||||||
|
if ( not(creds) ) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for username, password in pairs( creds ) do
|
||||||
|
helper = mssql.Helper:new()
|
||||||
|
status, result = helper:Connect(host, port)
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\n" .. result
|
||||||
|
end
|
||||||
|
|
||||||
|
status, result = helper:Login( username, password, nil, host.ip )
|
||||||
|
if ( not(status) ) then
|
||||||
|
stdnse.print_debug("ERROR: %s", result)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
status, dbs = helper:Query( db_query )
|
||||||
|
|
||||||
|
if ( status ) then
|
||||||
|
-- all done?
|
||||||
|
if ( #done_dbs == #dbs.rows ) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(dbs.rows) do
|
||||||
|
if ( not( table_contains( done_dbs, v[1] ) ) ) then
|
||||||
|
query = [[ SELECT so.name 'table', sc.name 'column', st.name 'type', sc.length
|
||||||
|
FROM %s..syscolumns sc, %s..sysobjects so, %s..systypes st
|
||||||
|
WHERE so.id = sc.id AND sc.xtype=st.xtype AND
|
||||||
|
so.id IN (SELECT %s id FROM %s..sysobjects WHERE xtype='U') %s ORDER BY so.name, sc.name, st.name]]
|
||||||
|
query = query:format( v[1], v[1], v[1], tbl_limit, v[1], keywords_filter)
|
||||||
|
status, tables = helper:Query( query )
|
||||||
|
if ( not(status) ) then
|
||||||
|
stdnse.print_debug(tables)
|
||||||
|
else
|
||||||
|
local item = {}
|
||||||
|
item = mssql.Util.FormatOutputTable( tables, true )
|
||||||
|
if ( #item == 0 and keywords_filter ~= "" ) then
|
||||||
|
table.insert(item, "Filter returned no matches")
|
||||||
|
end
|
||||||
|
item.name = v[1]
|
||||||
|
|
||||||
|
table.insert(output, item)
|
||||||
|
table.insert(done_dbs, v[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
helper:Disconnect()
|
||||||
|
end
|
||||||
|
|
||||||
|
local pos = 1
|
||||||
|
local restrict_tbl = {}
|
||||||
|
|
||||||
|
if ( nmap.registry.args['mssql-tables.keywords'] ) then
|
||||||
|
tmp = nmap.registry.args['mssql-tables.keywords']
|
||||||
|
if ( type(tmp) == 'table' ) then
|
||||||
|
tmp = stdnse.strjoin(',', tmp)
|
||||||
|
end
|
||||||
|
table.insert(restrict_tbl, 1, ("Filter: %s"):format(tmp))
|
||||||
|
pos = pos + 1
|
||||||
|
else
|
||||||
|
table.insert(restrict_tbl, 1, "No filter (see mssql-tables.keywords)")
|
||||||
|
end
|
||||||
|
|
||||||
|
if ( DB_COUNT > 0 ) then
|
||||||
|
local tmp = ("Output restricted to %d databases"):format(DB_COUNT)
|
||||||
|
if ( not(nmap.registry.args['mssql-tables.maxdb']) ) then
|
||||||
|
tmp = tmp .. " (see mssql-tables.maxdb)"
|
||||||
|
end
|
||||||
|
table.insert(restrict_tbl, 1, tmp)
|
||||||
|
pos = pos + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if ( TABLE_COUNT > 0 ) then
|
||||||
|
local tmp = ("Output restricted to %d tables"):format(TABLE_COUNT)
|
||||||
|
if ( not(nmap.registry.args['mssql-tables.maxtables']) ) then
|
||||||
|
tmp = tmp .. " (see mssql-tables.maxtables)"
|
||||||
|
end
|
||||||
|
table.insert(restrict_tbl, 1, tmp)
|
||||||
|
pos = pos + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if ( 1 < pos and #output > 0) then
|
||||||
|
restrict_tbl.name = "Restrictions"
|
||||||
|
table.insert(output, "")
|
||||||
|
table.insert(output, restrict_tbl)
|
||||||
|
end
|
||||||
|
|
||||||
|
output = stdnse.format_output( true, output )
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
end
|
||||||
146
scripts/ms-sql-xp-cmdshell.nse
Normal file
146
scripts/ms-sql-xp-cmdshell.nse
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
description = [[
|
||||||
|
Queries Microsoft SQL Server (MSSQL) for a list of tables per database.
|
||||||
|
]]
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"intrusive"}
|
||||||
|
|
||||||
|
require 'shortport'
|
||||||
|
require 'stdnse'
|
||||||
|
require 'mssql'
|
||||||
|
|
||||||
|
dependencies = {"ms-sql-brute", "ms-sql-empty-password"}
|
||||||
|
---
|
||||||
|
-- @args mssql.username specifies the username to use to connect to
|
||||||
|
-- the server. This option overrides any accounts found by
|
||||||
|
-- the mssql-brute and mssql-empty-password scripts.
|
||||||
|
--
|
||||||
|
-- @args mssql.password specifies the password to use to connect to
|
||||||
|
-- the server. This option overrides any accounts found by
|
||||||
|
-- the mssql-brute and mssql-empty-password scripts.
|
||||||
|
--
|
||||||
|
-- @args mssql-xp-cmdshell.cmd specifies the OS command to run.
|
||||||
|
-- (default is ipconfig /all)
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE
|
||||||
|
-- 1433/tcp open ms-sql-s
|
||||||
|
-- | mssql-xp-cmdshell:
|
||||||
|
-- | Command: ipconfig /all; User: sa
|
||||||
|
-- | output
|
||||||
|
-- |
|
||||||
|
-- | Windows IP Configuration
|
||||||
|
-- |
|
||||||
|
-- | Host Name . . . . . . . . . . . . : EDUSRV011
|
||||||
|
-- | Primary Dns Suffix . . . . . . . : cqure.net
|
||||||
|
-- | Node Type . . . . . . . . . . . . : Unknown
|
||||||
|
-- | IP Routing Enabled. . . . . . . . : No
|
||||||
|
-- | WINS Proxy Enabled. . . . . . . . : No
|
||||||
|
-- | DNS Suffix Search List. . . . . . : cqure.net
|
||||||
|
-- |
|
||||||
|
-- | Ethernet adapter Local Area Connection 3:
|
||||||
|
-- |
|
||||||
|
-- | Connection-specific DNS Suffix . :
|
||||||
|
-- | Description . . . . . . . . . . . : AMD PCNET Family PCI Ethernet Adapter #2
|
||||||
|
-- | Physical Address. . . . . . . . . : 08-00-DE-AD-C0-DE
|
||||||
|
-- | DHCP Enabled. . . . . . . . . . . : Yes
|
||||||
|
-- | Autoconfiguration Enabled . . . . : Yes
|
||||||
|
-- | IP Address. . . . . . . . . . . . : 192.168.56.3
|
||||||
|
-- | Subnet Mask . . . . . . . . . . . : 255.255.255.0
|
||||||
|
-- | Default Gateway . . . . . . . . . :
|
||||||
|
-- | DHCP Server . . . . . . . . . . . : 192.168.56.2
|
||||||
|
-- | Lease Obtained. . . . . . . . . . : den 21 mars 2010 00:12:10
|
||||||
|
-- | Lease Expires . . . . . . . . . . : den 21 mars 2010 01:12:10
|
||||||
|
-- |_
|
||||||
|
--
|
||||||
|
-- The script needs an account with the sysadmin server role to work.
|
||||||
|
-- It needs to be fed credentials through the script arguments or from
|
||||||
|
-- the scripts mssq-brute or mssq-empty-password.
|
||||||
|
--
|
||||||
|
-- When run, the script iterates over the credentials and attempts to run
|
||||||
|
-- the command until either all credentials are exhausted or until the
|
||||||
|
-- command is executed.
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Version 0.1
|
||||||
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
|
|
||||||
|
portrule = shortport.port_or_service(1433, "ms-sql-s")
|
||||||
|
|
||||||
|
local function table_contains( tbl, val )
|
||||||
|
for k,v in pairs(tbl) do
|
||||||
|
if ( v == val ) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
action = function( host, port )
|
||||||
|
|
||||||
|
local status, result, helper
|
||||||
|
local username = nmap.registry.args['mssql.username']
|
||||||
|
local password = nmap.registry.args['mssql.password'] or ""
|
||||||
|
local creds
|
||||||
|
local query
|
||||||
|
local cmd = nmap.registry.args['mssql-xp-cmdshell.cmd'] or 'ipconfig /all'
|
||||||
|
local output = {}
|
||||||
|
|
||||||
|
query = ("EXEC master..xp_cmdshell '%s'"):format(cmd)
|
||||||
|
|
||||||
|
if ( username ) then
|
||||||
|
creds = {}
|
||||||
|
creds[username] = password
|
||||||
|
elseif ( not(username) and nmap.registry.mssqlusers ) then
|
||||||
|
-- do we have a sysadmin?
|
||||||
|
creds = {}
|
||||||
|
if ( nmap.registry.mssqlusers.sa ) then
|
||||||
|
creds["sa"] = nmap.registry.mssqlusers.sa
|
||||||
|
else
|
||||||
|
creds = nmap.registry.mssqlusers
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If we don't have valid creds, simply fail silently
|
||||||
|
if ( not(creds) ) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for username, password in pairs( creds ) do
|
||||||
|
helper = mssql.Helper:new()
|
||||||
|
status, result = helper:Connect(host, port)
|
||||||
|
if ( not(status) ) then
|
||||||
|
return " \n\n" .. result
|
||||||
|
end
|
||||||
|
|
||||||
|
status, result = helper:Login( username, password, nil, host.ip )
|
||||||
|
if ( not(status) ) then
|
||||||
|
stdnse.print_debug("ERROR: %s", result)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
status, result = helper:Query( query )
|
||||||
|
helper:Disconnect()
|
||||||
|
|
||||||
|
if ( status ) then
|
||||||
|
output = mssql.Util.FormatOutputTable( result, true )
|
||||||
|
if ( not(nmap.registry.args['mssql-xp-cmdshell.cmd']) ) then
|
||||||
|
table.insert(output, 1, cmd)
|
||||||
|
output = stdnse.format_output( true, output )
|
||||||
|
output = "(Use --script-args=mssql-xp-cmdshell.cmd='<CMD>' to change command.)" .. output
|
||||||
|
else
|
||||||
|
output = stdnse.format_output( true, output )
|
||||||
|
end
|
||||||
|
|
||||||
|
break
|
||||||
|
elseif ( result:gmatch("xp_configure") ) then
|
||||||
|
if( nmap.verbosity() > 1 ) then
|
||||||
|
return " \nProcedure xp_cmdshell disabled, for more information see \"Surface Area Configuration\" in Books Online."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
end
|
||||||
@@ -52,7 +52,14 @@ Entry { filename = "ldap-search.nse", categories = { "discovery", "safe", } }
|
|||||||
Entry { filename = "lexmark-config.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "lexmark-config.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "mongodb-databases.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "mongodb-databases.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "mongodb-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "mongodb-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
|
Entry { filename = "ms-sql-brute.nse", categories = { "auth", "intrusive", } }
|
||||||
|
Entry { filename = "ms-sql-config.nse", categories = { "discovery", "safe", } }
|
||||||
|
Entry { filename = "ms-sql-empty-password.nse", categories = { "auth", "intrusive", } }
|
||||||
|
Entry { filename = "ms-sql-hasdbaccess.nse", categories = { "auth", "discovery", "safe", } }
|
||||||
Entry { filename = "ms-sql-info.nse", categories = { "default", "discovery", "intrusive", } }
|
Entry { filename = "ms-sql-info.nse", categories = { "default", "discovery", "intrusive", } }
|
||||||
|
Entry { filename = "ms-sql-query.nse", categories = { "discovery", "safe", } }
|
||||||
|
Entry { filename = "ms-sql-tables.nse", categories = { "discovery", "safe", } }
|
||||||
|
Entry { filename = "ms-sql-xp-cmdshell.nse", categories = { "intrusive", } }
|
||||||
Entry { filename = "mysql-brute.nse", categories = { "auth", "intrusive", } }
|
Entry { filename = "mysql-brute.nse", categories = { "auth", "intrusive", } }
|
||||||
Entry { filename = "mysql-databases.nse", categories = { "discovery", "intrusive", } }
|
Entry { filename = "mysql-databases.nse", categories = { "discovery", "intrusive", } }
|
||||||
Entry { filename = "mysql-empty-password.nse", categories = { "auth", "intrusive", } }
|
Entry { filename = "mysql-empty-password.nse", categories = { "auth", "intrusive", } }
|
||||||
|
|||||||
Reference in New Issue
Block a user