1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 20:51:30 +00:00

Load http fingerprints only once

This commit is contained in:
dmiller
2018-03-06 03:48:35 +00:00
parent ef385e5b71
commit 23218ad6bf

View File

@@ -171,6 +171,13 @@ local function get_variations(filename)
return variations return variations
end end
-- simplify unlocking the mutex, ensuring we don't try to parse again, and returning an error.
local function bad_prints(mutex, err)
nmap.registry.http_fingerprints = err
mutex "done"
return false, err
end
---Get the list of fingerprints from files. The files are defined in <code>fingerprint_files</code>. If category ---Get the list of fingerprints from files. The files are defined in <code>fingerprint_files</code>. If category
-- is non-nil, only choose scripts that are in that category. -- is non-nil, only choose scripts that are in that category.
-- --
@@ -181,11 +188,16 @@ local function get_fingerprints(fingerprint_file, category)
local total_count = 0 -- Used for 'limit' local total_count = 0 -- Used for 'limit'
-- Check if we've already read the file -- Check if we've already read the file
-- There might be a race condition here, where multiple scripts will read the file and set this variable, but the impact local mutex = nmap.mutex("http_fingerprints")
-- of that would be minimal (and definitely isn't security) mutex "lock"
if(nmap.registry.http_fingerprints ~= nil) then if nmap.registry.http_fingerprints then
if type(nmap.registry.http_fingerprints) == "table" then
stdnse.debug1("Using cached HTTP fingerprints") stdnse.debug1("Using cached HTTP fingerprints")
return nmap.registry.http_fingerprints mutex "done"
return true, nmap.registry.http_fingerprints
else
return bad_prints(mutex, nmap.registry.http_fingerprints)
end
end end
-- Try and find the file; if it isn't in Nmap's directories, take it as a direct path -- Try and find the file; if it isn't in Nmap's directories, take it as a direct path
@@ -199,7 +211,7 @@ local function get_fingerprints(fingerprint_file, category)
local file = loadfile(filename_full, "t", env) local file = loadfile(filename_full, "t", env)
if(not(file)) then if(not(file)) then
stdnse.debug1("Couldn't load configuration file: %s", filename_full) stdnse.debug1("Couldn't load configuration file: %s", filename_full)
return false, "Couldn't load fingerprint file: " .. filename_full return bad_prints(mutex, "Couldn't load fingerprint file: " .. filename_full)
end end
file() file()
@@ -211,14 +223,14 @@ local function get_fingerprints(fingerprint_file, category)
for i, fingerprint in pairs(fingerprints) do for i, fingerprint in pairs(fingerprints) do
-- Make sure we have a valid index -- Make sure we have a valid index
if(type(i) ~= 'number') then if(type(i) ~= 'number') then
return false, "The 'fingerprints' table is an array, not a table; all indexes should be numeric" return bad_prints(mutex, "The 'fingerprints' table is an array, not a table; all indexes should be numeric")
end end
-- Make sure they have either a string or a table of probes -- Make sure they have either a string or a table of probes
if(not(fingerprint.probes) or if(not(fingerprint.probes) or
(type(fingerprint.probes) ~= 'table' and type(fingerprint.probes) ~= 'string') or (type(fingerprint.probes) ~= 'table' and type(fingerprint.probes) ~= 'string') or
(type(fingerprint.probes) == 'table' and #fingerprint.probes == 0)) then (type(fingerprint.probes) == 'table' and #fingerprint.probes == 0)) then
return false, "Invalid path found for fingerprint " .. i return bad_prints(mutex, "Invalid path found for fingerprint " .. i)
end end
-- Make sure fingerprint.path is a table -- Make sure fingerprint.path is a table
@@ -230,7 +242,7 @@ local function get_fingerprints(fingerprint_file, category)
for i, probe in pairs(fingerprint.probes) do for i, probe in pairs(fingerprint.probes) do
-- Make sure we have a valid index -- Make sure we have a valid index
if(type(i) ~= 'number') then if(type(i) ~= 'number') then
return false, "The 'probes' table is an array, not a table; all indexes should be numeric" return bad_prints(mutex, "The 'probes' table is an array, not a table; all indexes should be numeric")
end end
-- Convert the probe to a table if it's a string -- Convert the probe to a table if it's a string
@@ -241,7 +253,7 @@ local function get_fingerprints(fingerprint_file, category)
-- Make sure the probes table has a 'path' -- Make sure the probes table has a 'path'
if(not(probe['path'])) then if(not(probe['path'])) then
return false, "The 'probes' table requires each element to have a 'path'." return bad_prints(mutex, "The 'probes' table requires each element to have a 'path'.")
end end
-- If they didn't set a method, set it to 'GET' -- If they didn't set a method, set it to 'GET'
@@ -251,35 +263,35 @@ local function get_fingerprints(fingerprint_file, category)
-- Make sure the method's a string -- Make sure the method's a string
if(type(probe['method']) ~= 'string') then if(type(probe['method']) ~= 'string') then
return false, "The 'method' in the probes file has to be a string" return bad_prints(mutex, "The 'method' in the probes file has to be a string")
end end
end end
-- Ensure that matches is an array -- Ensure that matches is an array
if(type(fingerprint.matches) ~= 'table') then if(type(fingerprint.matches) ~= 'table') then
return false, "'matches' field has to be a table" return bad_prints(mutex, "'matches' field has to be a table")
end end
-- Loop through the matches -- Loop through the matches
for i, match in pairs(fingerprint.matches) do for i, match in pairs(fingerprint.matches) do
-- Make sure we have a valid index -- Make sure we have a valid index
if(type(i) ~= 'number') then if(type(i) ~= 'number') then
return false, "The 'matches' table is an array, not a table; all indexes should be numeric" return bad_prints(mutex, "The 'matches' table is an array, not a table; all indexes should be numeric")
end end
-- Check that every element in the table is an array -- Check that every element in the table is an array
if(type(match) ~= 'table') then if(type(match) ~= 'table') then
return false, "Every element of 'matches' field has to be a table" return bad_prints(mutex, "Every element of 'matches' field has to be a table")
end end
-- Check the output field -- Check the output field
if(match['output'] == nil or type(match['output']) ~= 'string') then if(match['output'] == nil or type(match['output']) ~= 'string') then
return false, "The 'output' field in 'matches' has to be present and a string" return bad_prints(mutex, "The 'output' field in 'matches' has to be present and a string")
end end
-- Check the 'match' and 'dontmatch' fields, if present -- Check the 'match' and 'dontmatch' fields, if present
if((match['match'] and type(match['match']) ~= 'string') or (match['dontmatch'] and type(match['dontmatch']) ~= 'string')) then if((match['match'] and type(match['match']) ~= 'string') or (match['dontmatch'] and type(match['dontmatch']) ~= 'string')) then
return false, "The 'match' and 'dontmatch' fields in 'matches' have to be strings, if they exist" return bad_prints(mutex, "The 'match' and 'dontmatch' fields in 'matches' have to be strings, if they exist")
end end
-- Change blank 'match' strings to '.*' so they match everything -- Change blank 'match' strings to '.*' so they match everything
@@ -290,22 +302,22 @@ local function get_fingerprints(fingerprint_file, category)
-- Make sure the severity is an integer between 1 and 4. Default it to 1. -- Make sure the severity is an integer between 1 and 4. Default it to 1.
if(fingerprint.severity and (type(fingerprint.severity) ~= 'number' or fingerprint.severity < 1 or fingerprint.severity > 4)) then if(fingerprint.severity and (type(fingerprint.severity) ~= 'number' or fingerprint.severity < 1 or fingerprint.severity > 4)) then
return false, "The 'severity' field has to be an integer between 1 and 4" return bad_prints(mutex, "The 'severity' field has to be an integer between 1 and 4")
else else
fingerprint.severity = 1 fingerprint.severity = 1
end end
-- Make sure ignore_404 is a boolean. Default it to false. -- Make sure ignore_404 is a boolean. Default it to false.
if(fingerprint.ignore_404 and type(fingerprint.ignore_404) ~= 'boolean') then if(fingerprint.ignore_404 and type(fingerprint.ignore_404) ~= 'boolean') then
return false, "The 'ignore_404' field has to be a boolean" return bad_prints(mutex, "The 'ignore_404' field has to be a boolean")
else else
fingerprint.ignore_404 = false fingerprint.ignore_404 = false
end end
end end
-- Make sure we have some fingerprints fingerprints -- Make sure we have some fingerprints
if(#fingerprints == 0) then if(#fingerprints == 0) then
return false, "No fingerprints were loaded" return bad_prints(mutex, "No fingerprints were loaded")
end end
-- If the user wanted to filter by category, do it -- If the user wanted to filter by category, do it
@@ -321,7 +333,7 @@ local function get_fingerprints(fingerprint_file, category)
-- Make sure we still have fingerprints after the category filter -- Make sure we still have fingerprints after the category filter
if(#fingerprints == 0) then if(#fingerprints == 0) then
return false, "No fingerprints matched the given category (" .. category .. ")" return bad_prints(mutex, "No fingerprints matched the given category (" .. category .. ")")
end end
end end
@@ -345,7 +357,8 @@ local function get_fingerprints(fingerprint_file, category)
-- end -- end
-- Cache the fingerprints for other scripts, so we aren't reading the files every time -- Cache the fingerprints for other scripts, so we aren't reading the files every time
-- nmap.registry.http_fingerprints = fingerprints nmap.registry.http_fingerprints = fingerprints
mutex "done"
return true, fingerprints return true, fingerprints
end end