File: ms-sql-dump-hashes.nse

package info (click to toggle)
nmap 7.91%2Bdfsg1%2Breally7.80%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 52,600 kB
  • sloc: cpp: 60,892; ansic: 57,361; python: 17,800; sh: 16,347; xml: 11,556; perl: 2,679; makefile: 1,217; java: 45; objc: 43; awk: 23
file content (140 lines) | stat: -rw-r--r-- 4,363 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
local io = require "io"
local mssql = require "mssql"
local stdnse = require "stdnse"
local string = require "string"
local stringaux = require "stringaux"
local table = require "table"

description = [[
Dumps the password hashes from an MS-SQL 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.

Credentials passed as script arguments take precedence over credentials
discovered by other scripts.
]]

---
-- @usage
-- nmap -p 1433 <ip> --script ms-sql-dump-hashes
--
-- @args ms-sql-dump-hashes.dir Dump hashes to a file in this directory. File
--                              name is <ip>_<instance>_ms-sql_hashes.txt.
--                              Default: no file is saved.
--
-- @output
-- PORT     STATE SERVICE
-- 1433/tcp open  ms-sql-s
-- | ms-sql-dump-hashes:
-- |   nmap_test:0x01001234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF0123
-- |   sa:0x01001234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF0123
-- |_  webshop_dbo:0x01001234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF01234567890ABCDEF0123

--
--
-- Version 0.1
-- Created 08/03/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 = {"auth", "discovery", "safe"}


dependencies = {"ms-sql-brute", "ms-sql-empty-password"}

hostrule = mssql.Helper.GetHostrule_Standard()
portrule = mssql.Helper.GetPortrule_Standard()

local function process_instance(instance)

  local helper = mssql.Helper:new()
  local status, errorMessage = helper:ConnectEx( instance )
  if ( not(status) ) then
    return false, {
      ['name'] = string.format( "[%s]", instance:GetName() ),
      "ERROR: " .. errorMessage
    }
  end

  status, errorMessage = helper:LoginEx( instance )
  if ( not(status) ) then
    return false, {
      ['name'] = string.format( "[%s]", instance:GetName() ),
      "ERROR: " .. errorMessage
    }
  end

  local result
  local query = [[
  IF ( OBJECT_ID('master..sysxlogins' ) ) <> 0
    SELECT name, password FROM master..sysxlogins WHERE password IS NOT NULL
  ELSE IF ( OBJECT_ID('master.sys.sql_logins') ) <> 0
    SELECT name, password_hash FROM master.sys.sql_logins
  ]]
  status, result = helper:Query( query )

  local output = {}

  if ( status ) then
    for _, row in ipairs( result.rows ) do
      table.insert(output, ("%s:%s"):format(row[1] or "",row[2] or "") )
    end
  end

  helper:Disconnect()
  local instanceOutput = {}
  instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
  table.insert( instanceOutput, output )

  return true, instanceOutput

end

-- Saves the hashes to file
-- @param filename string name of the file
-- @param response table containing the resultset
-- @return status true on success, false on failure
-- @return err string containing the error if status is false
local function saveToFile(filename, response)
  local f = io.open( filename, "w")
  if ( not(f) ) then
    return false, ("Failed to open file (%s)"):format(filename)
  end
  for _, row in ipairs(response) do
    if ( not(f:write(row .."\n" ) ) ) then
      return false, ("Failed to write file (%s)"):format(filename)
    end
  end
  f:close()
  return true
end

action = function( host, port )
  local dir = stdnse.get_script_args("ms-sql-dump-hashes.dir")
  local scriptOutput = {}
  local status, instanceList = mssql.Helper.GetTargetInstances( host, port )

  if ( not status ) then
    return stdnse.format_output( false, instanceList )
  else
    for _, instance in pairs( instanceList ) do
      local status, instanceOutput = process_instance( instance )
      if ( status ) then
        local filename
        if ( dir ) then
          local instance = instance:GetName():match("%\\+(.+)$") or instance:GetName()
          filename = dir .. "/" .. stringaux.filename_escape(("%s_%s_ms-sql_hashes.txt"):format(host.ip, instance))
          saveToFile(filename, instanceOutput[1])
        end
      end
      table.insert( scriptOutput, instanceOutput )
    end
  end

  if ( #scriptOutput == 0 ) then return end

  local output = ( #scriptOutput > 1 and scriptOutput or scriptOutput[1] )

  return stdnse.format_output( true, output )
end