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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
local nmap = require "nmap"
local smb = require "smb"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
local os = require "os"
local datetime = require "datetime"
description = [[
Attempts to determine the operating system, computer name, domain, workgroup, and current
time over the SMB protocol (ports 445 or 139).
This is done by starting a session with the anonymous
account (or with a proper user account, if one is given; it likely doesn't make
a difference); in response to a session starting, the server will send back all this
information.
The following fields may be included in the output, depending on the
circumstances (e.g. the workgroup name is mutually exclusive with domain and forest
names) and the information available:
* OS
* Computer name
* Domain name
* Forest name
* FQDN
* NetBIOS computer name
* NetBIOS domain name
* Workgroup
* System time
Some systems, like Samba, will blank out their name (and only send their domain).
Other systems (like embedded printers) will simply leave out the information. Other
systems will blank out various pieces (some will send back 0 for the current
time, for example).
If this script is used in conjunction with version detection it can augment the
standard nmap version detection information with data that this script has discovered.
Retrieving the name and operating system of a server is a vital step in targeting
an attack against it, and this script makes that retrieval easy. Additionally, if
a penetration tester is choosing between multiple targets, the time can help identify
servers that are being poorly maintained (for more information/random thoughts on
using the time, see http://www.skullsecurity.org/blog/?p=76.
Although the standard <code>smb*</code> script arguments can be used,
they likely won't change the outcome in any meaningful way. However, <code>smbnoguest</code>
will speed up the script on targets that do not allow guest access.
]]
---
--@usage
-- nmap --script smb-os-discovery.nse -p445 127.0.0.1
-- sudo nmap -sU -sS --script smb-os-discovery.nse -p U:137,T:139 127.0.0.1
--
--@output
-- Host script results:
-- | smb-os-discovery:
-- | OS: Windows Server (R) 2008 Standard 6001 Service Pack 1 (Windows Server (R) 2008 Standard 6.0)
-- | OS CPE: cpe:/o:microsoft:windows_2008::sp1
-- | Computer name: Sql2008
-- | NetBIOS computer name: SQL2008
-- | Domain name: lab.test.local
-- | Forest name: test.local
-- | FQDN: Sql2008.lab.test.local
-- | NetBIOS domain name: LAB
-- |_ System time: 2011-04-20T13:34:06-05:00
--
--@xmloutput
-- <elem key="os">Windows Server (R) 2008 Standard 6001 Service Pack 1</elem>
-- <elem key="cpe">cpe:/o:microsoft:windows_2008::sp1</elem>
-- <elem key="lanmanager">Windows Server (R) 2008 Standard 6.0</elem>
-- <elem key="domain">LAB</elem>
-- <elem key="server">SQL2008</elem>
-- <elem key="date">2011-04-20T13:34:06-05:00</elem>
-- <elem key="fqdn">Sql2008.lab.test.local</elem>
-- <elem key="domain_dns">lab.test.local</elem>
-- <elem key="forest_dns">test.local</elem>
author = "Ron Bowes"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default", "discovery", "safe"}
dependencies = {"smb-brute"}
--- Check whether or not this script should be run.
hostrule = function(host)
return smb.get_port(host) ~= nil
end
-- Some observed OS strings:
-- "Windows 5.0" (is Windows 2000)
-- "Windows 5.1" (is Windows XP)
-- "Windows Server 2003 3790 Service Pack 2"
-- "Windows Vista (TM) Ultimate 6000"
-- "Windows Server (R) 2008 Standard 6001 Service Pack 1"
-- "Windows 7 Professional 7601 Service Pack 1"
-- http://msdn.microsoft.com/en-us/library/cc246806%28v=prot.20%29.aspx has a
-- list of strings that don't quite match these.
function make_cpe(result)
local os = result.os
local parts = {}
if string.match(os, "^Windows 5%.0") then
parts = {"o", "microsoft", "windows_2000"}
elseif string.match(os, "^Windows 5%.1") then
parts = {"o", "microsoft", "windows_xp"}
elseif string.match(os, "^Windows Server.*2003") then
parts = {"o", "microsoft", "windows_server_2003"}
elseif string.match(os, "^Windows Vista") then
parts = {"o", "microsoft", "windows_vista"}
elseif string.match(os, "^Windows Server.*2008") then
parts = {"o", "microsoft", "windows_server_2008"}
elseif string.match(os, "^Windows 7") then
parts = {"o", "microsoft", "windows_7"}
elseif string.match(os, "^Windows 8%f[^%d.]") then
parts = {"o", "microsoft", "windows_8"}
elseif string.match(os, "^Windows 8.1") then
parts = {"o", "microsoft", "windows_8.1"}
elseif string.match(os, "^Windows 10%f[^%d.]") then
parts = {"o", "microsoft", "windows_10"}
elseif string.match(os, "^Windows Server.*2012") then
parts = {"o", "microsoft", "windows_server_2012"}
end
if parts[1] == "o" and parts[2] == "microsoft"
and string.match(parts[3], "^windows") then
parts[4] = ""
local sp = string.match(os, "Service Pack (%d+)")
if sp then
parts[5] = "sp" .. tostring(sp)
else
parts[5] = "-"
end
if string.match(os, "Professional") then
parts[6] = "professional"
end
end
if #parts > 0 then
return "cpe:/" .. table.concat(parts, ":")
end
end
function add_to_output(output_table, label, value)
if value then
table.insert(output_table, string.format("%s: %s", label, value))
end
end
action = function(host)
local response = stdnse.output_table()
local request_time = os.time()
local status, result = smb.get_os(host)
if(status == false) then
return stdnse.format_output(false, result)
end
-- Collect results.
response.os = result.os
response.lanmanager = result.lanmanager
response.domain = result.domain
response.server = result.server
if result.time and result.timezone then
response.date = datetime.format_timestamp(result.time, result.timezone * 60 * 60)
datetime.record_skew(host, result.time - result.timezone * 60 * 60, request_time)
end
response.fqdn = result.fqdn
response.domain_dns = result.domain_dns
response.forest_dns = result.forest_dns
response.workgroup = result.workgroup
response.cpe = make_cpe(result)
-- Build normal output.
local output_lines = {}
if response.os and response.lanmanager then
add_to_output(output_lines, "OS", string.format("%s (%s)", smb.get_windows_version(response.os), response.lanmanager))
else
add_to_output(output_lines, "OS", "Unknown")
end
add_to_output(output_lines, "OS CPE", response.cpe)
if response.fqdn then
-- Pull the first part of the FQDN as the computer name.
add_to_output(output_lines, "Computer name", string.match(response.fqdn, "^([^.]+)%.?"))
end
add_to_output(output_lines, "NetBIOS computer name", result.server)
if response.fqdn and response.domain_dns and response.fqdn ~= response.domain_dns then
-- If the FQDN doesn't match the domain name, the target is a domain member.
add_to_output(output_lines, "Domain name", response.domain_dns)
add_to_output(output_lines, "Forest name", response.forest_dns)
add_to_output(output_lines, "FQDN", response.fqdn)
add_to_output(output_lines, "NetBIOS domain name", response.domain)
else
add_to_output(output_lines, "Workgroup", response.workgroup or response.domain)
end
add_to_output(output_lines, "System time", response.date or "Unknown")
-- Augment service version detection
if result.port and response.lanmanager then
local proto
if result.port == 445 or result.port == 139 then
proto = 'tcp'
else
proto = 'udp'
end
local port = nmap.get_port_state(host,{number=result.port,protocol=proto})
local version, product
if string.match(response.lanmanager,"^Samba ") then
port.version.product = 'Samba smbd'
port.version.version = string.match(response.lanmanager,"^Samba (.*)")
nmap.set_port_version(host,port)
elseif smb.get_windows_version(response.os) then
port.version.product = string.format("%s %s",smb.get_windows_version(response.os), port.version.name)
nmap.set_port_version(host,port)
end
end
return response, stdnse.format_output(true, output_lines)
end
|