-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathnlist.nse
executable file
·333 lines (317 loc) · 14.8 KB
/
nlist.nse
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
-- Released as open source by NCC Group Plc - http://www.nccgroup.com/
-- Developed by James Conlan, [email protected]
-- https://github.com/nccgroup/nlist
-- You should have received a copy of the GNU General Public License along with
-- nList. If not, see https://www.gnu.org/licenses.
local io = require "io"
local lfs = require "lfs"
local nmap = require "nmap"
local os = require "os"
local json = require "json"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
description = [[
A script to produce target lists for use with various tools.
Works best when run as part of a version scan (-sV).
]]
---
--@usage
-- nmap [-sV] --script nlist [--script-args nlist.config=<config_file>,nlist.ignorehome,nlist.outdir=<output_directory>,nlist.overwrite] [-p-] <target>
--@args
-- All arguments override settings specified in configuration files
-- nlist.config=<config_file>: nList configuration file
-- nlist.ignorehome: If specified, the '.nlist' config file in the user's home directory is ignored
-- nlist.outdir=<output_directory>: Output directory to write list files to ('./target_lists' by default)
-- nlist.overwrite: If specified, existing output files are overwritten
--@output
-- Output files are written to the specified output directory ('./target_lists' by default)
author = "James Conlan"
license = "GNU General Public License v3.0 -- See https://www.gnu.org/licenses"
categories = {"default", "safe"}
exists = function(path)
local ok, err, code = os.rename(path, path)
if not ok then
if code == 13 then
-- Permission denied, but it exists
return true
else
return false
end
else
return true
end
end
getDefaultConfPath = function(pathSeparator)
if pathSeparator == "/" then
if exists("/usr/share/nmap") then
return "/usr/share/nmap/scripts/nlist.conf"
elseif exists("/usr/local/share/nmap") then
return "/usr/local/share/nmap/scripts/nlist.conf"
else
stdnse.debug(1, "Scripts directory location unknown")
end
elseif pathSeparator == "\\" then
if exists("C:\\Program Files (x86)\\Nmap") then
return "C:\\Program Files (x86)\\Nmap\\scripts\\nlist.conf"
elseif exists("C:\\Program Files\\Nmap") then
return "C:\\Program Files\\Nmap\\scripts\\nlist.conf"
else
stdnse.debug(1, "Scripts directory location unknown")
end
else
stdnse.debug(1, "Unknown OS")
end
return nil
end
getHomeConfPath = function(pathSeparator)
local homePath = os.getenv("HOME")
if homePath == nil then
homePath = os.getenv("HOMEPATH")
end
if homePath then
return homePath .. pathSeparator .. ".nlist"
else
return false
end
end
parseConfFile = function(confFile, confPath)
if not confFile then
stdnse.debug(1, "Could not open config file: '%s'", confPath)
return false, string.format("Config file '%s' could not be opened", confPath)
end
local confStr = confFile:read("*all")
confFile:close()
local status, config = json.parse(confStr)
if not status then
stdnse.debug(1, "Invalid config file: '%s'", confPath)
stdnse.debug(1, "JSON error: %s", config)
return false, string.format("'%s' is not a valid config file", confPath)
end
return true, config
end
hostrule = function(host)
if nmap.get_ports(host, nil, "tcp", "open") ~= nil or nmap.get_ports(host, nil, "udp", "open") ~= nil then
return true
end
stdnse.debug(1, "Skipping host %s with no open/open|filtered ports", host.ip)
end
action = function(host, port)
-- Parse config file
local pathSeparator = lfs.get_path_separator()
local confPath = stdnse.get_script_args("nlist.config")
local confFile = nil
if confPath then
confFile = io.open(confPath, "r")
if not confFile then
stdnse.debug(1, "Could not open config file: '%s'", confPath)
return string.format("Config file '%s' could not be opened", confPath)
end
end
if not confFile and not stdnse.get_script_args("nlist.ignorehome") then
stdnse.debug(1, "No config file specified, trying home directory")
confPath = getHomeConfPath(pathSeparator)
if confPath then
confFile = io.open(confPath, "r")
end
end
if not confFile then
stdnse.debug(1, "No '.nlist' file found in home directory")
confPath = getDefaultConfPath(pathSeparator)
if not confPath then
return "Could not open config file"
end
confFile = io.open(confPath, "r")
end
stdnse.debug(1, "Using config file: '%s'", confPath)
local status, config = parseConfFile(confFile, confPath)
if not status then
return config
end
-- Set output directory
local outDir = stdnse.get_script_args("nlist.outdir")
if outDir == nil then
outDir = tostring(config["output_directory"]) or nil
end
if outDir == nil then
outDir = "target_lists"
stdnse.debug(1, "No output directory specified in config file, using 'target_lists'")
end
local path = ""
if string.find(outDir, pathSeparator) == 1 then
path = pathSeparator
end
for dir in outDir:gmatch("[^" .. pathSeparator .. "]+") do
path = path .. dir .. pathSeparator
local status, error = lfs.mkdir(path)
if not status and error ~= "File exists" then
stdnse.debug(1, "Could not write to directory '%s': %s", path, error)
return string.format("Could not write to output directory '%s': %s", outDir, error)
end
end
stdnse.debug(1, "Output directory set to '%s'", outDir)
-- Determine checks to run
local defConfig = {}
local homeConfig = {}
if config["use_default_rules"] then
stdnse.debug(1, "Loading default rules", confPath)
local defConfPath = getDefaultConfPath(pathSeparator)
if not defConfPath then
return "Could not open default config file"
end
local confFile = io.open(defConfPath, "r")
local status, conf = parseConfFile(confFile)
defConfig = conf
if not status then
return defConfig
end
end
if config["use_home_rules"] and not stdnse.get_script_args("nlist.ignorehome") then
stdnse.debug(1, "Loading rules from home config", confPath)
local homeConfPath = getHomeConfPath(pathSeparator)
local confFile = io.open(homeConfPath, "r")
local status, conf = parseConfFile(confFile)
homeConfig = conf
if not status then
return homeConfig
end
end
-- Clear existing results files if requested
if stdnse.get_script_args("nlist.overwrite") == 1 or config["overwrite"] then
stdnse.debug(1, "Deleting existing results files")
for _, list in ipairs({defConfig, homeConfig, config}) do
if list["output_files"] then
for _, file in ipairs(list["output_files"]) do
local path = outDir .. pathSeparator .. file["name"]
local status, error = os.remove(path)
if not status and not error == "No such file or directory" then
stdnse.debug(1, "Could not delete file '%s': %s", path, error)
return string.format("Could not delete existing output file '%s': %s", path, error)
end
end
end
end
end
-- Perform checks
for i, protocol in ipairs({"tcp", "udp"}) do
for _, portState in ipairs({"open", "open|filtered"}) do
stdnse.debug(1, "Checking %s ports with state '%s'", protocol, portState)
local port = nmap.get_ports(host, nil, protocol, portState)
while port do
stdnse.debug(1, "Checking port %s:%d/%s", host.ip, port.number, port.protocol)
for listType, checkList in pairs({["default config"] = defConfig, ["home config"] = homeConfig, ["specified config"] = config}) do
if checkList["output_files"] then
stdnse.debug(1, "Performing checks from %s", listType)
for _, check in ipairs(checkList["output_files"]) do
local positive = true
for _, rule in ipairs(check["rules"]) do
if rule["port_protocol"] then
local equal = false
for _, proto in ipairs(rule["port_protocol"]) do
if string.lower(proto) == string.lower(port.protocol) then
equal = true
break
end
end
if not equal then
positive = false
break
end
end
if rule["port_number"] then
local equal = false
for _, num in ipairs(rule["port_number"]) do
if num == port.number then
equal = true
break
end
end
if not equal then
positive = false
break
end
end
if rule["service"] then
local equal = false
for _, srv in ipairs(rule["service"]) do
if string.lower(srv) == string.lower(port.service) then
equal = true
break
end
end
if not equal then
positive = false
break
end
end
if rule["service_type"] then
local equal = false
for _, typ in ipairs(rule["service_type"]) do
if string.lower(typ) == "ssl/tls" then
if shortport.ssl(host, port) then
equal = true
break
end
elseif string.lower(typ) == "http" then
if shortport.http(host, port) then
equal = true
break
end
else
stdnse.debug(1, "Invalid service type specified in config: '%s'", rule["service_type"])
return string.format("Value '%s' specified in config file is not a valid service type", rule["service_type"])
end
if not equal then
positive = false
break
end
end
end
if positive then
break
end
end
if positive then
stdnse.debug(1, "Check '%s' from %s returned positive", tostring(check["name"]), listType)
local outPath = outDir .. pathSeparator .. tostring(check["name"])
stdnse.debug(1, "Writing result to '%s'", outPath)
local resultArgs = {}
for i, val in ipairs(check["output_format"]) do
local valLower = string.lower(tostring(val))
if i == 1 then
table.insert(resultArgs, val)
elseif valLower == "ip" then
table.insert(resultArgs, host.ip)
elseif valLower == "port_number" then
table.insert(resultArgs, port.number)
elseif valLower == "port_protocol" then
table.insert(resultArgs, port.protocol)
elseif valLower == "service" then
table.insert(resultArgs, port.service)
else
stdnse.debug(1, "Invalid output value specified in config: '%s'", val)
return string.format("Value '%s' specified in config file is not a valid output value", val)
end
end
local result = string.format(table.unpack(resultArgs))
local outFile, error = io.open(outPath, "a")
if outFile == nil then
stdnse.debug(1, "File write error: '%s'", error)
return string.format("Could not write to output file '%s': %s", outPath, error)
else
io.output(outFile)
io.write(result .. "\n")
io.close(outFile)
end
else
stdnse.debug(1, "Check '%s' from %s returned negative", tostring(check["name"]), listType)
end
end
end
end
port = nmap.get_ports(host, port, protocol, portState)
end
end
end
return string.format("Output files successfully written to '%s'", outDir)
end